"""Opt-in FastAPI and Mangum bootstrap helpers."""

import importlib
from collections.abc import Callable, Iterable
from dataclasses import dataclass
from types import ModuleType
from typing import Any, TypeVar

from punq import Container  # type: ignore[import-untyped]

from ..bootstrap import ComponentLifetime, ComponentRegistry, ContainerBuilder
from ..bootstrap import autoload as autoload_modules


def _load_dependencies() -> tuple[type[Any], Callable[..., Any], type[Any], type[Any]]:
    missing: list[str] = []
    modules: dict[str, Any] = {}

    for module_name in ("fastapi", "mangum"):
        try:
            modules[module_name] = importlib.import_module(module_name)
        except ImportError:
            missing.append(module_name)

    if missing:
        missing_names = ", ".join(sorted(missing))
        raise ImportError(
            "iisi_app_core.integrations.fastapi requires optional dependencies "
            f"({missing_names}). Install them with `pip install \"iisi-app-core[fastapi]\"`."
        ) from None

    fastapi_module = modules["fastapi"]
    mangum_module = modules["mangum"]
    return (
        getattr(fastapi_module, "FastAPI"),
        getattr(fastapi_module, "Depends"),
        getattr(fastapi_module, "Request"),
        getattr(mangum_module, "Mangum"),
    )


FastAPI, Depends, Request, Mangum = _load_dependencies()

T = TypeVar("T")


@dataclass(slots=True, frozen=True)
class BuiltFastApiContext:
    """Resolved bootstrap context shared with the FastAPI app factory."""

    modules: tuple[ModuleType, ...]
    registry: ComponentRegistry
    container: Container


@dataclass(slots=True, frozen=True)
class BuiltFastApiApp(BuiltFastApiContext):
    """Final FastAPI build output."""

    app: FastAPI
    handler: Mangum | None


def get_container(request: Request) -> Container:
    """Read the punq container from FastAPI app state."""

    container = getattr(request.app.state, "container", None)
    if container is None:
        raise RuntimeError("FastAPI app state is missing `container`; build the app with FastApiMangumBuilder.")
    if not isinstance(container, Container):
        raise RuntimeError("FastAPI app state `container` must be a punq.Container instance.")
    return container


def from_container(interface: type[T], *, use_cache: bool = True) -> Any:
    """Create a FastAPI dependency that resolves an interface from the container."""

    def dependency(request: Request) -> T:
        return get_container(request).resolve(interface)

    dependency.__name__ = f"resolve_{interface.__name__}"
    return Depends(dependency, use_cache=use_cache)


def _unique_modules(modules: Iterable[ModuleType]) -> tuple[ModuleType, ...]:
    unique: dict[str, ModuleType] = {}
    for module in modules:
        unique.setdefault(module.__name__, module)
    return tuple(sorted(unique.values(), key=lambda item: item.__name__))


class FastApiMangumBuilder:
    """Build an autoloaded container, FastAPI app, and optional Mangum handler."""

    def __init__(self) -> None:
        self._modules: list[ModuleType] = []
        self._instance_bindings: list[tuple[type[Any], Any]] = []
        self._factory_bindings: list[tuple[type[Any], Callable[..., Any], ComponentLifetime]] = []
        self._app_factory: Callable[[BuiltFastApiContext], FastAPI] | None = None
        self._mangum_kwargs: dict[str, Any] | None = None

    def autoload(self, package: ModuleType) -> "FastApiMangumBuilder":
        self._modules.extend(autoload_modules(package))
        return self

    def add_modules(self, modules: Iterable[ModuleType]) -> "FastApiMangumBuilder":
        self._modules.extend(modules)
        return self

    def add_instance(self, interface: type[Any], instance: Any) -> "FastApiMangumBuilder":
        self._instance_bindings.append((interface, instance))
        return self

    def add_factory(
        self,
        interface: type[Any],
        factory: Callable[..., Any],
        *,
        lifetime: ComponentLifetime = ComponentLifetime.TRANSIENT,
    ) -> "FastApiMangumBuilder":
        self._factory_bindings.append((interface, factory, lifetime))
        return self

    def app(self, factory: Callable[[BuiltFastApiContext], FastAPI]) -> "FastApiMangumBuilder":
        self._app_factory = factory
        return self

    def enable_mangum(self, **kwargs: Any) -> "FastApiMangumBuilder":
        self._mangum_kwargs = dict(kwargs)
        return self

    def build(self) -> BuiltFastApiApp:
        if self._app_factory is None:
            raise ValueError("FastApiMangumBuilder.app(...) must be configured before build().")

        modules = _unique_modules(self._modules)
        registry = ComponentRegistry.from_modules(modules)

        builder = ContainerBuilder(registry)
        for interface, instance in self._instance_bindings:
            builder.add_instance(interface, instance)
        for interface, factory, lifetime in self._factory_bindings:
            builder.add_factory(interface, factory, lifetime=lifetime)

        container = builder.build()
        context = BuiltFastApiContext(modules=modules, registry=registry, container=container)
        app = self._app_factory(context)
        app.state.container = container
        app.state.registry = registry
        app.state.modules = modules

        handler = Mangum(app, **self._mangum_kwargs) if self._mangum_kwargs is not None else None
        return BuiltFastApiApp(
            modules=modules,
            registry=registry,
            container=container,
            app=app,
            handler=handler,
        )


__all__ = [
    "BuiltFastApiApp",
    "BuiltFastApiContext",
    "FastApiMangumBuilder",
    "from_container",
    "get_container",
]
