from __future__ import annotations

import json
from typing import Any

from app.case_epicrisis.application.diagnostic_aid_interpretation import (
    apply_interpretation_proposal,
    build_interpretation_payload,
)
from app.config import config
from app.llm import LLMOutputKind, LLMStructuredRequest, LLMTask
from app.llm.schemas import DiagnosticAidInterpretationStructured
from app.services.llm_task_cache import build_llm_task_fingerprint


DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION = "v1"
DIAGNOSTIC_AID_INTERPRETATION_SCHEMA_VERSION = "v1"


class LLMDiagnosticAidInterpreter:
    """Infrastructure adapter for one auditable structured interpretation per aid."""

    def __init__(self, llm_router: Any, cache_repository: Any = None) -> None:
        self._llm_router = llm_router
        self._cache_repository = cache_repository

    def interpret(
        self,
        *,
        username: str,
        aids: list[dict[str, Any]],
    ) -> list[dict[str, Any]]:
        results: list[dict[str, Any]] = []
        for aid in aids:
            payload = build_interpretation_payload(
                aid,
                max_chars=config.EPICRISIS_LLM_DIAGNOSTIC_AID_MAX_CHARS,
            )
            if payload is None:
                results.append(aid)
                continue
            results.append(self._interpret_one(username=username, aid=aid, payload=payload))
        return results

    def _interpret_one(
        self,
        *,
        username: str,
        aid: dict[str, Any],
        payload: dict[str, Any],
    ) -> dict[str, Any]:
        try:
            route = self._resolve_route()
            fingerprint = build_llm_task_fingerprint(
                task=LLMTask.DIAGNOSTIC_AID_INTERPRETATION.value,
                provider=str(route.provider),
                model=str(route.model),
                cache_version=str(getattr(self._cache_repository, "cache_version", "v1")),
                prompt_version=DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION,
                schema_version=DIAGNOSTIC_AID_INTERPRETATION_SCHEMA_VERSION,
                payload=payload,
                metadata={"aid_key": payload["key"]},
            )
            if fingerprint and self._cache_repository is not None:
                cached = self._cache_repository.get(
                    username=username,
                    task=LLMTask.DIAGNOSTIC_AID_INTERPRETATION.value,
                    fingerprint=fingerprint,
                )
                if cached and cached.get("payload") is not None:
                    cached_model = DiagnosticAidInterpretationStructured.model_validate(cached["payload"])
                    return apply_interpretation_proposal(
                        aid,
                        cached_model.model_dump(mode="json"),
                        provider=str(cached.get("provider") or route.provider),
                        model=str(cached.get("model") or route.model),
                        prompt_version=DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION,
                    )

            prompt_payload = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
            response = self._llm_router.generate_structured(
                LLMStructuredRequest(
                    task=LLMTask.DIAGNOSTIC_AID_INTERPRETATION,
                    prompt=(
                        "Evalúa una sola ayuda diagnóstica con la evidencia clínica entregada. "
                        "No inventes datos, no cambies el tipo ni el nombre de la ayuda y no reemplaces "
                        "resultados determinísticos. Solo puedes citar IDs presentes en evidencias. "
                        "Distingue una orden o solicitud pendiente de un resultado realmente reportado. "
                        "Si falta conclusión atribuible, la evidencia es insuficiente o hay contradicción, "
                        "responde pendiente_revision con interpretación vacía. Devuelve únicamente JSON válido.\n\n"
                        f"Ayuda y evidencia:\n{prompt_payload}"
                    ),
                    output_model=DiagnosticAidInterpretationStructured,
                    output_kind=LLMOutputKind.STRUCTURED_OBJECT,
                    system_prompt=(
                        "Eres auditor médico. Trabaja únicamente con la evidencia recibida. "
                        "Tu respuesta es una propuesta revisable; nunca agregues evidencia externa."
                    ),
                    metadata={
                        "operation": "diagnostic_aid_interpretation",
                        "prompt_version": DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION,
                        "schema_version": DIAGNOSTIC_AID_INTERPRETATION_SCHEMA_VERSION,
                        "aid_key": payload["key"],
                        "evidence_ids": [
                            str(item["id"]) for item in payload.get("evidencias") or []
                        ],
                        "input_characters": len(prompt_payload),
                    },
                )
            )
            structured = DiagnosticAidInterpretationStructured.model_validate(response.content)
            serialized = structured.model_dump(mode="json")
            if fingerprint and self._cache_repository is not None:
                self._cache_repository.upsert(
                    username=username,
                    task=LLMTask.DIAGNOSTIC_AID_INTERPRETATION.value,
                    fingerprint=fingerprint,
                    provider=str(response.provider or route.provider),
                    model=str(response.model or route.model),
                    payload=serialized,
                    metadata={
                        "operation": "diagnostic_aid_interpretation",
                        "prompt_version": DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION,
                        "schema_version": DIAGNOSTIC_AID_INTERPRETATION_SCHEMA_VERSION,
                        "aid_key": payload["key"],
                        "evidence_ids": [
                            str(item["id"]) for item in payload.get("evidencias") or []
                        ],
                    },
                )
            return apply_interpretation_proposal(
                aid,
                serialized,
                provider=str(response.provider or route.provider),
                model=str(response.model or route.model),
                prompt_version=DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION,
            )
        except Exception as exc:
            return apply_interpretation_proposal(
                aid,
                {
                    "decision": "pendiente_revision",
                    "reason": f"No fue posible ejecutar la interpretación asistida: {type(exc).__name__}.",
                },
                provider="",
                model="",
                prompt_version=DIAGNOSTIC_AID_INTERPRETATION_PROMPT_VERSION,
            )

    def _resolve_route(self) -> Any:
        if self._llm_router is None or not hasattr(self._llm_router, "_resolve_route"):
            raise RuntimeError("No se pudo resolver la ruta de interpretación de ayudas.")
        return self._llm_router._resolve_route(  # noqa: SLF001
            LLMTask.DIAGNOSTIC_AID_INTERPRETATION,
            metadata={"operation": "diagnostic_aid_interpretation"},
        )
