import logging
from typing import Annotated, Any

import anyio
from fastapi import APIRouter, Depends, File, HTTPException, Security, UploadFile
from starlette import status

from app.auth import get_current_user
from app.case_epicrisis.domain.curation import CodeSystem
from app.core.dependencies import get_services
from app.core.services import AppServices
from app.llm import LLMTask
from app.llm.task_text import generate_task_text
from app.models import UserInDB
from app.services.soat_processing import (
    _agente_codificar_desde_soat_item,
    _extraer_seccion_documento,
    _generar_resumen_validacion,
    _validar_codigo_soat_con_documento,
    generar_codigos_desde_soat_con_gemini,
)
from app.soat_crosswalk.application.resolution import (
    SUPPORTED_CROSSWALK_YEAR,
    CupsCodeNotFoundError,
    CupsSoatResolver,
    InvalidCupsCodeError,
)
from app.soat_crosswalk.domain.models import CrosswalkStatus
from app.soat_crosswalk.infrastructure.catalogs import JsonCupsSoatCrosswalk, XlsxCupsCatalog
from app.soat_tariffs.application.valuation import tariff_entry_values
from app.soat_tariffs.infrastructure.json_catalog import JsonSoatTariffCatalog
from modules.processing.soat.soat_indexer import build_soat_index_from_pdf


router = APIRouter()
logger = logging.getLogger(__name__)
SoatPdfFile = Annotated[UploadFile, File(...)]
CurrentUser = Annotated[UserInDB, Security(get_current_user)]
ServicesDep = Annotated[AppServices, Depends(get_services)]


@router.get("/api/soat/cups/{cups_code}/tariff")
async def consultar_tarifa_por_cups(
    cups_code: str,
    year: int,
    current_user: CurrentUser,
):
    if year != SUPPORTED_CROSSWALK_YEAR:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
            detail="La vigencia del cruce CUPS-SOAT solo está disponible para 2026.",
        )
    cups_catalog = XlsxCupsCatalog()
    tariff_catalog = JsonSoatTariffCatalog()
    crosswalk = JsonCupsSoatCrosswalk(
        cups_catalog=cups_catalog,
        tariff_catalog=tariff_catalog,
    )
    resolver = CupsSoatResolver(
        cups_catalog=cups_catalog,
        crosswalk=crosswalk,
        tariff_catalog=tariff_catalog,
    )
    try:
        result = resolver.resolve(cups_code, year)
    except InvalidCupsCodeError as exc:
        raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
    except CupsCodeNotFoundError as exc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc

    response = result.model_dump(mode="json")
    if result.status == CrosswalkStatus.RESOLVED and result.selected_soat_code:
        catalog = tariff_catalog.load(year)
        entry = tariff_catalog.find_exact(year, result.selected_soat_code)
        if entry is None:  # El adaptador valida esto; protege el contrato HTTP frente a archivos manipulados.
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Cruce inconsistente")
        base, components = tariff_entry_values(entry, catalog.unit_value)
        response["valuation"] = {
            "soat_code": entry.code,
            "description": entry.description,
            "surgical_group": entry.surgical_group,
            "unit": catalog.unit,
            "unit_value": catalog.unit_value,
            "base_tariff": base,
            "components": components,
            "note": "Valoración SOAT de referencia; no modifica importes facturados.",
        }
    return response


@router.post("/soat/indexar_pdf")
async def soat_indexar_pdf(
    file: SoatPdfFile,
    *,
    current_user: CurrentUser,
    services: ServicesDep,
):
    base_dir = services.base_dir
    soat_index_dir = "soat_faiss"
    try:
        contents = await file.read()
        tmp_path = base_dir / "_tmp_soat.pdf"
        async with await anyio.open_file(tmp_path, "wb") as f:
            await f.write(contents)
        index_dir = build_soat_index_from_pdf(str(tmp_path), str(base_dir / soat_index_dir))
        services.configure_soat_index(index_dir)
        return {"mensaje": "Índice SOAT construido", "index_dir": index_dir}
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))


@router.post("/soat/codificar")
async def soat_codificar(
    data: dict,
    current_user: CurrentUser,
    services: ServicesDep,
):
    soat_retriever = services.soat_retriever
    if not soat_retriever:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Índice SOAT no inicializado")
    procedimientos = data.get("procedimientos") or []
    if not isinstance(procedimientos, list) or not procedimientos:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Procedimientos requeridos")
    resultados = soat_retriever.asignar_codigos(procedimientos)
    return {"resultados": resultados}


@router.post("/agente/codificar_desde_soat")
async def agente_codificar_desde_soat(
    payload: dict[str, Any],
    current_user: CurrentUser,
    services: ServicesDep,
):
    """Dado un conjunto de códigos/descripciones SOAT, propone CUPS y CIE‑10 óptimos con justificación y evidencia."""
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    items = payload.get("items") or []
    if not isinstance(items, list) or not items:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="'items' debe ser una lista no vacía"
        )
    resultados = []
    for item in items:
        try:
            resultados.append(
                _agente_codificar_desde_soat_item(
                    item,
                    soat_retriever,
                    retriever,
                    cups_retriever,
                    client_groq,
                    client_gemini,
                    llm_router=services.llm_router,
                )
            )
        except Exception as e:
            resultados.append({"input": item, "error": str(e)})
    return {"resultados": resultados}


@router.post("/agente/analizar_hallazgos_qx")
async def agente_analizar_hallazgos_qx(
    payload: dict[str, Any],
    current_user: CurrentUser,
    services: ServicesDep,
):
    """
    Analiza Hallazgos y Descripción Quirúrgica para proponer CIE-10 y CUPS
    y devolverlos en formato compatible con la lista seleccionable de epicrisis.
    """
    hallazgos = str(payload.get("hallazgos") or "").strip()
    descripcion_procedimiento = str(payload.get("descripcion_procedimiento") or "").strip()
    max_chars = int(payload.get("max_chars") or 1800)

    if not hallazgos and not descripcion_procedimiento:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Debe enviar 'hallazgos' o 'descripcion_procedimiento'.",
        )

    partes = []
    if hallazgos:
        partes.append(f"Hallazgos quirurgicos: {hallazgos}")
    if descripcion_procedimiento:
        partes.append(f"Descripcion del procedimiento: {descripcion_procedimiento}")

    descripcion_agente = " | ".join(partes).strip()
    if len(descripcion_agente) > max_chars:
        descripcion_agente = descripcion_agente[:max_chars]

    resultado = await generar_codigos_desde_soat_gemini(
        {
            "descripciones": [
                {
                    "codigo_soat": "QX-HALLAZGOS",
                    "descripcion": descripcion_agente,
                }
            ]
        },
        current_user,
        services,
    )

    resultados = []
    for item in resultado.get("resultados") or []:
        if not isinstance(item, dict):
            continue
        if item.get("error"):
            continue
        resultados.append(
            {
                **item,
                "fuente": "agente_hallazgos_qx",
            }
        )

    total_diagnosticos = 0
    total_procedimientos = 0
    for item in resultados:
        if item.get("cie10_principal"):
            total_diagnosticos += 1
        total_diagnosticos += len(item.get("cie10_secundarios") or [])

        if item.get("cups_principal"):
            total_procedimientos += 1
        total_procedimientos += len(item.get("cups_alternativos") or [])

    return {
        "mensaje": "Análisis de hallazgos quirúrgicos completado",
        "fuente": "agente_hallazgos_qx",
        "resultados": resultados,
        "total_resultados": len(resultados),
        "total_diagnosticos": total_diagnosticos,
        "total_procedimientos": total_procedimientos,
    }


@router.post("/soat/chat")
async def soat_chat(
    payload: dict[str, Any],
    current_user: CurrentUser,
    services: ServicesDep,
):
    soat_retriever = services.soat_retriever
    if not soat_retriever:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Índice SOAT no inicializado")
    if not services.llm_router:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="No hay capacidad LLM configurada para el chat SOAT.",
        )
    mensaje = (payload.get("mensaje") or "").strip()
    if not mensaje:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="'mensaje' es requerido")
    try:
        docs = soat_retriever.buscar(mensaje, k=15)
        contexto = "\n\n".join([f"(p.{d.metadata.get('page')}) {d.page_content[:1100]}" for d in docs])
        catalog_registry = getattr(services, "catalog_registry", None)
        catalog_reference = (
            catalog_registry.reference(CodeSystem.SOAT) if catalog_registry is not None else None
        )
        catalog_label = (
            f"{catalog_reference.index_name} ({catalog_reference.source_name})"
            if catalog_reference is not None and catalog_reference.available
            else "índice SOAT vigente"
        )
        prompt = f"""
Eres un especialista en auditoría y codificación médica en Colombia. Responde usando EXCLUSIVAMENTE
el contexto proporcionado de {catalog_label}, citando páginas cuando corresponda.
Si no hay información suficiente, dilo explícitamente.

Pregunta del usuario:
{mensaje}

Contexto SOAT (extractos):
{contexto}
"""
        resp = generate_task_text(
            services.llm_router,
            task=LLMTask.SOAT_CHAT,
            prompt=prompt,
            system_prompt="Eres un asistente RAG estricto. No inventes contenido fuera del contexto.",
            metadata={"operation": "soat_chat", "username": current_user.username, "query": mensaje[:200]},
            generation_config={"temperature": 0.0},
        )
        respuesta = resp.content
        fuentes = [{"page": d.metadata.get("page"), "source": d.metadata.get("source")} for d in docs[:10]]
        return {"respuesta": respuesta, "fuentes": fuentes}
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))


@router.post("/soat/validar_factura")
async def validar_factura_soat(
    payload: dict[str, Any],
    current_user: CurrentUser,
    services: ServicesDep,
):
    """
    Valida códigos SOAT de una factura contra documentación quirúrgica.
    Propone CIE-10 y CUPS que justifiquen los códigos SOAT facturados.
    """
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    documento_quirurgico = payload.get("documento_quirurgico", "").strip()
    codigos_soat_factura = payload.get("codigos_soat_factura", [])

    if not documento_quirurgico:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="Documento quirúrgico es requerido"
        )

    if not codigos_soat_factura or not isinstance(codigos_soat_factura, list):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="Códigos SOAT de factura son requeridos"
        )

    try:
        # Extraer información estructurada del documento quirúrgico
        diagnosticos_prequx = _extraer_seccion_documento(documento_quirurgico, "DIAGNÓSTICOS PREQUIRÚRGICO")
        procedimientos = _extraer_seccion_documento(documento_quirurgico, "PROCEDIMIENTOS REALIZADOS")
        hallazgos = _extraer_seccion_documento(documento_quirurgico, "HALLAZGOS QUIRÚRGICOS")
        descripcion_proc = _extraer_seccion_documento(documento_quirurgico, "DESCRIPCIÓN DEL PROCEDIMIENTO")
        justificacion = _extraer_seccion_documento(documento_quirurgico, "JUSTIFICACIÓN DEL PROCEDIMIENTO")
        diagnosticos_postqx = _extraer_seccion_documento(documento_quirurgico, "DIAGNÓSTICOS POSTQUIRÚRGICO")

        # Procesar cada código SOAT de la factura
        resultados_validacion = []

        for codigo_soat_item in codigos_soat_factura:
            codigo_soat = codigo_soat_item.get("codigo", "").strip()
            descripcion_soat = codigo_soat_item.get("descripcion", "").strip()
            fecha = codigo_soat_item.get("fecha", "")
            cantidad = codigo_soat_item.get("cantidad", 1)
            valor = codigo_soat_item.get("valor", "")

            # Validar el código SOAT contra la documentación
            resultado_validacion = await _validar_codigo_soat_con_documento(
                codigo_soat=codigo_soat,
                descripcion_soat=descripcion_soat,
                diagnosticos_prequx=diagnosticos_prequx,
                procedimientos=procedimientos,
                hallazgos=hallazgos,
                descripcion_proc=descripcion_proc,
                justificacion=justificacion,
                diagnosticos_postqx=diagnosticos_postqx,
                soat_retriever=soat_retriever,
                retriever=retriever,
                cups_retriever=cups_retriever,
                client_groq=client_groq,
                client_gemini=client_gemini,
                llm_router=services.llm_router,
            )

            resultado_validacion.update(
                {
                    "codigo_soat": codigo_soat,
                    "descripcion_soat": descripcion_soat,
                    "fecha": fecha,
                    "cantidad": cantidad,
                    "valor": valor,
                }
            )

            resultados_validacion.append(resultado_validacion)

        # Generar resumen de validación
        resumen = _generar_resumen_validacion(resultados_validacion, documento_quirurgico)

        return {
            "mensaje": "Validación de factura SOAT completada",
            "documento_quirurgico_procesado": {
                "diagnosticos_prequx": diagnosticos_prequx,
                "procedimientos": procedimientos,
                "hallazgos": hallazgos,
                "justificacion": justificacion,
            },
            "validaciones": resultados_validacion,
            "resumen": resumen,
        }

    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error validando factura SOAT: {str(e)}",
        )


@router.post("/soat/generar_codigos_gemini")
async def generar_codigos_desde_soat_gemini(
    payload: dict[str, Any],
    current_user: CurrentUser,
    services: ServicesDep,
):
    """
    Nueva feature: Genera códigos CIE-10 y CUPS directamente desde descripciones SOAT usando Gemini.
    Este endpoint NO usa RAG, sino que confía en el conocimiento de Gemini para codificación médica.
    """
    client_gemini = services.client_gemini
    descripciones_soat = payload.get("descripciones", [])

    if not descripciones_soat or not isinstance(descripciones_soat, list):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="Se requiere una lista de descripciones SOAT"
        )

    if not client_gemini and not services.llm_router:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="No hay capacidad LLM configurada para generar códigos SOAT.",
        )

    try:
        resultados = generar_codigos_desde_soat_con_gemini(
            descripciones_soat,
            client_gemini=client_gemini,
            llm_router=services.llm_router,
            cache_repository=services.llm_task_cache_repository,
            username=current_user.username,
        )
        return {
            "mensaje": "Códigos generados exitosamente",
            "total_procesados": len(resultados),
            "resultados": resultados,
        }

    except Exception as e:
        logger.exception("Error generando códigos con Gemini")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error al generar códigos: {str(e)}"
        )
