"""Helpers for optional dependencies and extras."""

from __future__ import annotations

import importlib
import importlib.util
import sys
from types import ModuleType

PORTAUDIO_INSTALL_FIX = (
    "Install PortAudio first (Debian/Ubuntu: `sudo apt-get install libportaudio2`; "
    "macOS: `brew install portaudio`), then retry."
)


def _extra_install_hint(extra: str | None) -> str:
    if extra is None:
        return ""
    return (
        f" Install with: uv add 'easycat[{extra}]'. "
        f"From the EasyCat repo, use: uv sync --extra {extra} --group dev."
    )


def require_module(
    module_name: str,
    *,
    extra: str | None = None,
    purpose: str | None = None,
) -> ModuleType:
    """Import and return a module or raise a clear missing-extra error."""
    if module_name not in sys.modules and importlib.util.find_spec(module_name) is None:
        hint = _extra_install_hint(extra)
        label = purpose or module_name
        raise ImportError(f"{label} requires the {module_name} package.{hint}")
    try:
        return importlib.import_module(module_name)
    except ImportError as exc:
        # The package itself is present (find_spec succeeded above) but importing it
        # failed because one of its own dependencies is missing or broken. This is
        # common for optional extras that pull native/transitive deps.
        hint = _extra_install_hint(extra)
        label = purpose or module_name
        raise ImportError(
            f"{label} could not import {module_name} (a dependency failed to load): {exc}.{hint}"
        ) from exc
    except OSError as exc:
        hint = (
            f" {PORTAUDIO_INSTALL_FIX}"
            if module_name == "sounddevice"
            else _extra_install_hint(extra)
        )
        label = purpose or module_name
        raise ImportError(f"{label} could not load {module_name}: {exc}.{hint}") from exc
