from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

import pytz
from fastapi.templating import Jinja2Templates

from app.batch_processing.infrastructure.runtime import BatchRuntime, build_batch_runtime
from app.config import config
from app.core.heavy_resources import HeavyResourceRegistry, LazyResourceProxy
from app.core.logging import MongoAuditRepository, set_audit_repository
from app.core.mongo_runtime import MongoRuntime, get_mongo_runtime
from app.individual_ingestion.infrastructure.runtime import (
    IndividualIngestionRuntime,
    build_individual_ingestion_runtime,
)
from app.services.case_deletion_service import CaseDeletionService
from app.services.case_epicrisis_runtime_service import CaseEpicrisisRuntimeService
from app.services.case_epicrisis_service import CaseEpicrisisService
from app.services.clinical_document_service import ClinicalDocumentService
from app.services.llm_task_cache import MongoLLMTaskCacheRepository
from app.services.rda_service import RdaService
from app.services.rips_service import RipsService
from modules.data_collection.mongodb_storage import MongoDBStorage


COLOMBIA_TZ = pytz.timezone("America/Bogota")


@dataclass
class AppServices:
    templates: Jinja2Templates
    base_dir: Path
    mongo_runtime: MongoRuntime
    mongo_storage: MongoDBStorage
    mongo_analyses: MongoDBStorage
    llm_task_cache_repository: MongoLLMTaskCacheRepository
    client_groq: Any
    client_gemini: Any | None
    llm_router: Any
    llm_providers: dict[str, Any]
    llm_policy: Any
    cie10_retriever: Any
    cups_retriever: Any
    soat_retriever: Any
    colombia_tz: pytz.BaseTzInfo
    clinical_document_service: ClinicalDocumentService
    case_epicrisis_service: CaseEpicrisisService
    case_epicrisis_runtime_service: CaseEpicrisisRuntimeService
    case_deletion_service: CaseDeletionService
    rda_service: RdaService
    rips_service: RipsService
    batch_runtime: BatchRuntime
    individual_ingestion_runtime: IndividualIngestionRuntime
    heavy_resources: HeavyResourceRegistry

    def configure_soat_index(self, index_dir: str) -> Any:
        self.heavy_resources.set_soat_index_dir(index_dir)
        if isinstance(self.soat_retriever, LazyResourceProxy):
            self.soat_retriever.reset()
        return self.heavy_resources.get_soat_retriever()

    def warmup_resources(self, *resource_names: str) -> dict[str, str]:
        return self.heavy_resources.warmup(*resource_names)

    def resources_status(self) -> dict[str, bool]:
        return self.heavy_resources.status()


def _build_audit_repository(runtime: MongoRuntime) -> MongoAuditRepository:
    return MongoAuditRepository(
        runtime.sync_database["audit_events"],
        retention_days=config.AUDIT_RETENTION_DAYS,
    )


def build_services(
    base_dir: Path,
    *,
    mongo_runtime: MongoRuntime | None = None,
    heavy_resources: HeavyResourceRegistry | None = None,
) -> AppServices:
    runtime = mongo_runtime or get_mongo_runtime()
    resources = heavy_resources or HeavyResourceRegistry(base_dir)
    templates = Jinja2Templates(directory="web")

    mongo_storage = MongoDBStorage(
        config.MONGO_URI,
        config.DB_NAME,
        config.COLLECTION_NAME,
        service="mongo_storage_pdf",
        client=runtime.sync_client,
        database=runtime.sync_database,
    )
    mongo_analyses = MongoDBStorage(
        config.MONGO_URI,
        config.DB_NAME,
        "historias_analizadas",
        service="mongo_storage_analyses",
        client=runtime.sync_client,
        database=runtime.sync_database,
    )

    if str(config.AUDIT_MONGO_ENABLED).strip().lower() in {"1", "true", "yes", "on"}:
        set_audit_repository(_build_audit_repository(runtime))
    else:
        set_audit_repository(None)

    client_groq = LazyResourceProxy(
        resources.get_groq_client,
        available=resources.groq_available,
        name="groq_client",
    )
    client_gemini = (
        LazyResourceProxy(
            resources.get_gemini_client,
            available=resources.gemini_available,
            name="gemini_client",
        )
        if resources.gemini_available()
        else None
    )
    llm_router = LazyResourceProxy(resources.get_llm_router, name="llm_router")
    llm_policy = LazyResourceProxy(resources.get_model_selection_policy, name="llm_policy")
    llm_providers = {
        "groq": LazyResourceProxy(
            resources.get_groq_provider,
            available=resources.groq_available,
            name="groq_provider",
        ),
        "gemini": LazyResourceProxy(
            resources.get_gemini_provider,
            available=resources.gemini_available,
            name="gemini_provider",
        ),
        "openai": LazyResourceProxy(
            resources.get_openai_provider,
            available=resources.openai_stub_enabled,
            name="openai_provider",
        ),
    }
    cie10_retriever = LazyResourceProxy(resources.get_cie10_retriever, name="cie10_retriever")
    cups_retriever = LazyResourceProxy(resources.get_cups_retriever, name="cups_retriever")
    soat_retriever = LazyResourceProxy(
        resources.get_soat_retriever,
        available=resources.soat_available,
        name="soat_retriever",
    )
    llm_task_cache_repository = MongoLLMTaskCacheRepository(mongo_analyses)
    llm_task_cache_repository.ensure_indexes()

    clinical_document_service = ClinicalDocumentService(
        mongo_storage=mongo_storage,
        mongo_analyses=mongo_analyses,
        client_groq=client_groq,
        client_gemini=client_gemini,
        llm_router=llm_router,
        cie10_retriever=cie10_retriever,
        cups_retriever=cups_retriever,
        colombia_tz=COLOMBIA_TZ,
        llm_task_cache_repository=llm_task_cache_repository,
    )
    case_epicrisis_service = CaseEpicrisisService(
        mongo_analyses=mongo_analyses,
        client_groq=client_groq,
        client_gemini=client_gemini,
        soat_retriever=soat_retriever,
        cie10_retriever=cie10_retriever,
        llm_router=llm_router,
        colombia_tz=COLOMBIA_TZ,
        llm_task_cache_repository=llm_task_cache_repository,
    )
    rda_service = RdaService(
        mongo_analyses=mongo_analyses,
        colombia_tz=COLOMBIA_TZ,
        case_epicrisis_service=case_epicrisis_service,
    )
    rips_service = RipsService(
        mongo_analyses=mongo_analyses,
        colombia_tz=COLOMBIA_TZ,
        case_epicrisis_service=case_epicrisis_service,
    )
    batch_runtime = build_batch_runtime(
        base_dir,
        COLOMBIA_TZ,
        clinical_document_service=clinical_document_service,
        case_epicrisis_service=case_epicrisis_service,
        mongo_database=runtime.sync_database,
    )
    individual_ingestion_runtime = build_individual_ingestion_runtime(
        base_dir,
        COLOMBIA_TZ,
        clinical_document_service=clinical_document_service,
        mongo_database=runtime.sync_database,
    )
    case_epicrisis_runtime_service = CaseEpicrisisRuntimeService(
        case_repository=clinical_document_service.batch_case_repository,
        case_epicrisis_service=case_epicrisis_service,
        clinical_document_service=clinical_document_service,
        colombia_tz=COLOMBIA_TZ,
        recompute_batch_bulk_epicrisis=batch_runtime.recompute_batch_bulk_epicrisis.execute,
    )
    case_deletion_service = CaseDeletionService(
        mongo_analyses=mongo_analyses,
        mongo_database=runtime.sync_database,
    )

    return AppServices(
        templates=templates,
        base_dir=base_dir,
        mongo_runtime=runtime,
        mongo_storage=mongo_storage,
        mongo_analyses=mongo_analyses,
        llm_task_cache_repository=llm_task_cache_repository,
        client_groq=client_groq,
        client_gemini=client_gemini,
        llm_router=llm_router,
        llm_providers=llm_providers,
        llm_policy=llm_policy,
        cie10_retriever=cie10_retriever,
        cups_retriever=cups_retriever,
        soat_retriever=soat_retriever,
        colombia_tz=COLOMBIA_TZ,
        clinical_document_service=clinical_document_service,
        case_epicrisis_service=case_epicrisis_service,
        case_epicrisis_runtime_service=case_epicrisis_runtime_service,
        case_deletion_service=case_deletion_service,
        rda_service=rda_service,
        rips_service=rips_service,
        batch_runtime=batch_runtime,
        individual_ingestion_runtime=individual_ingestion_runtime,
        heavy_resources=resources,
    )
