"""Base infrastructure shared across recipe routes."""

from __future__ import annotations

import logging
import os
from typing import Awaitable, Callable, Mapping

import jinja2
from aiohttp import web

from ..config import config
from ..recipes import RecipeParserFactory
from ..services.downloader import get_downloader
from ..services.recipes import (
    RecipeAnalysisService,
    RecipePersistenceService,
    RecipeSharingService,
)
from ..services.batch_import_service import BatchImportService
from ..services.server_i18n import server_i18n
from ..services.service_registry import ServiceRegistry
from ..services.settings_manager import get_settings_manager
from ..utils.constants import CARD_PREVIEW_WIDTH
from ..utils.exif_utils import ExifUtils
from .handlers.recipe_handlers import (
    BatchImportHandler,
    RecipeAnalysisHandler,
    RecipeHandlerSet,
    RecipeListingHandler,
    RecipeManagementHandler,
    RecipePageView,
    RecipeQueryHandler,
    RecipeSharingHandler,
    RecipeWorkflowHandler,
)
from .recipe_route_registrar import ROUTE_DEFINITIONS

logger = logging.getLogger(__name__)


class BaseRecipeRoutes:
    """Common dependency and startup wiring for recipe routes."""

    _HANDLER_NAMES: tuple[str, ...] = tuple(
        definition.handler_name for definition in ROUTE_DEFINITIONS
    )

    template_name: str = "recipes.html"

    def __init__(self) -> None:
        self.recipe_scanner = None
        self.lora_scanner = None
        self.civitai_client = None
        self.settings = get_settings_manager()
        self.server_i18n = server_i18n
        self.template_env = jinja2.Environment(
            loader=jinja2.FileSystemLoader(config.templates_path),
            autoescape=True,
        )

        self._i18n_registered = False
        self._startup_hooks_registered = False
        self._handler_set: RecipeHandlerSet | None = None
        self._handler_mapping: Mapping[
            str, Callable[[web.Request], Awaitable[web.StreamResponse]]
        ] | None = None

    async def attach_dependencies(self, app: web.Application | None = None) -> None:
        """Resolve shared services from the registry."""

        await self._ensure_services()
        self._ensure_i18n_filter()

    async def ensure_dependencies_ready(self) -> None:
        """Ensure dependencies are available for request handlers."""

        if self.recipe_scanner is None or self.civitai_client is None:
            await self.attach_dependencies()

    def register_startup_hooks(self, app: web.Application) -> None:
        """Register startup hooks once for dependency wiring."""

        if self._startup_hooks_registered:
            return

        app.on_startup.append(self.attach_dependencies)
        self._startup_hooks_registered = True

    def to_route_mapping(
        self,
    ) -> Mapping[str, Callable[[web.Request], Awaitable[web.StreamResponse]]]:
        """Return a mapping of handler name to coroutine for registrar binding."""

        if self._handler_mapping is None:
            handler_set = self._create_handler_set()
            self._handler_set = handler_set
            self._handler_mapping = handler_set.to_route_mapping()
        return self._handler_mapping

    # Internal helpers -------------------------------------------------

    async def _ensure_services(self) -> None:
        if self.recipe_scanner is None:
            self.recipe_scanner = await ServiceRegistry.get_recipe_scanner()
            self.lora_scanner = getattr(self.recipe_scanner, "_lora_scanner", None)

        if self.civitai_client is None:
            self.civitai_client = await ServiceRegistry.get_civitai_client()

    def _ensure_i18n_filter(self) -> None:
        if not self._i18n_registered:
            self.template_env.filters["t"] = self.server_i18n.create_template_filter()
            self._i18n_registered = True

    def get_handler_owner(self):
        """Return the object supplying bound handler coroutines."""

        if self._handler_set is None:
            self._handler_set = self._create_handler_set()
        return self._handler_set

    def _create_handler_set(self) -> RecipeHandlerSet:
        recipe_scanner_getter = lambda: self.recipe_scanner
        civitai_client_getter = lambda: self.civitai_client

        standalone_mode = (
            os.environ.get("LORA_MANAGER_STANDALONE", "0") == "1"
            or os.environ.get("HF_HUB_DISABLE_TELEMETRY", "0") == "0"
        )
        if not standalone_mode:
            from ..metadata_collector import get_metadata  # pyright: ignore[reportMissingImports]
            from ..metadata_collector.metadata_processor import (  # pyright: ignore[reportMissingImports]
                MetadataProcessor,
            )
            from ..metadata_collector.metadata_registry import (  # pyright: ignore[reportMissingImports]
                MetadataRegistry,
            )
        else:  # pragma: no cover - optional dependency path
            get_metadata = None  # pyright: ignore[reportAssignmentType]
            MetadataProcessor = None  # pyright: ignore[reportAssignmentType]
            MetadataRegistry = None  # pyright: ignore[reportAssignmentType]

        analysis_service = RecipeAnalysisService(
            exif_utils=ExifUtils,
            recipe_parser_factory=RecipeParserFactory,
            downloader_factory=get_downloader,
            metadata_collector=get_metadata,
            metadata_processor_cls=MetadataProcessor,
            metadata_registry_cls=MetadataRegistry,
            standalone_mode=standalone_mode,
            logger=logger,
        )
        persistence_service = RecipePersistenceService(
            exif_utils=ExifUtils,
            card_preview_width=CARD_PREVIEW_WIDTH,
            logger=logger,
        )
        sharing_service = RecipeSharingService(logger=logger)

        page_view = RecipePageView(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            settings_service=self.settings,
            server_i18n=self.server_i18n,
            template_env=self.template_env,
            template_name=self.template_name,
            recipe_scanner_getter=recipe_scanner_getter,
            logger=logger,
        )
        listing = RecipeListingHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            logger=logger,
        )
        query = RecipeQueryHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            format_recipe_file_url=listing.format_recipe_file_url,
            logger=logger,
        )
        management = RecipeManagementHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            logger=logger,
            persistence_service=persistence_service,
            analysis_service=analysis_service,
            downloader_factory=get_downloader,
            civitai_client_getter=civitai_client_getter,
        )
        analysis = RecipeAnalysisHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            civitai_client_getter=civitai_client_getter,
            logger=logger,
            analysis_service=analysis_service,
        )
        sharing = RecipeSharingHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            logger=logger,
            sharing_service=sharing_service,
        )

        # Lazy import: standalone mode replaces the ``server`` module with a
        # mock, so resolve PromptServer at handler-set build time instead of
        # module import time. The handler's standalone check guards UX.
        from server import PromptServer  # pyright: ignore[reportMissingImports]

        workflow = RecipeWorkflowHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            prompt_server=PromptServer,
            logger=logger,
        )

        from ..services.websocket_manager import ws_manager

        batch_import_service = BatchImportService(
            analysis_service=analysis_service,
            persistence_service=persistence_service,
            ws_manager=ws_manager,
            logger=logger,
        )
        batch_import = BatchImportHandler(
            ensure_dependencies_ready=self.ensure_dependencies_ready,
            recipe_scanner_getter=recipe_scanner_getter,
            civitai_client_getter=civitai_client_getter,
            logger=logger,
            batch_import_service=batch_import_service,
        )

        return RecipeHandlerSet(
            page_view=page_view,
            listing=listing,
            query=query,
            management=management,
            analysis=analysis,
            sharing=sharing,
            batch_import=batch_import,
            workflow=workflow,
        )
