from datetime import datetime
import os
import re
from threading import Thread
from typing import Dict, List, Optional

from bson import ObjectId
from fastapi import APIRouter, Request, Depends, HTTPException
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
from starlette import status

from app.auth import get_current_user
from app.batch_processing.infrastructure.mongo_repositories import MongoBatchCaseRepository
from app.core.dependencies import get_services
from app.core.services import AppServices
from app.services.clinical_processing import (
    extraer_nombre_paciente,
    extraer_metadatos_historia,
    extraer_medicamentos_historia,
    extraer_procedimientos_historia,
    extraer_secciones_quirurgicas,
    formatear_medicamento_canonico,
    normalizar_lista_medicamentos,
)
from app.services.soat_processing import _construir_soat_y_glosa, _parse_cie10_lines
from app.routes.soat import generar_codigos_desde_soat_gemini

router = APIRouter()
EPICRISIS_PDF_CACHE_COLLECTION = "epicrisis_pdf_cache"

_CIE10_INLINE_PATTERN = re.compile(
    r"^\s*(?P<codigo>[A-TV-Z][0-9]{2}[0-9A-Z]?(?:\.[0-9A-Z]{1,2})?)\s*(?:[-:â€“â€”]\s*|\s+)(?P<descripcion>.+)$",
    flags=re.IGNORECASE,
)


def _normalizar_cie10(entries, retriever=None):
    if not isinstance(entries, list):
        return entries
    normalized = []
    for item in entries:
        if not isinstance(item, dict):
            normalized.append(item)
            continue
        codigo = str(item.get("codigo", "")).strip()
        codigo_low = codigo.lower()
        diagnostico = str(item.get("diagnostico", "")).strip()
        match = _CIE10_INLINE_PATTERN.match(diagnostico)
        if match and codigo_low in {"", "error", "pendiente", "sin codigo", "sin cÃ³digo"}:
            item = {
                **item,
                "codigo": match.group("codigo").upper(),
                "descripcion": match.group("descripcion").strip(),
            }
            normalized.append(item)
            continue

        # Si no hay codigo inline y el valor es tecnico (Error/Pendiente), intentar recodificar
        # usando el retriever si ya existe el indice FAISS.
        if (
            retriever is not None
            and diagnostico
            and codigo_low in {"error", "pendiente", "sin codigo", "sin cÃ³digo", ""}
        ):
            try:
                texto_cie10 = retriever.asignar_codigo_cie10(diagnostico, temperature=0.0, k=30)
                codigos_parseados = _parse_cie10_lines(texto_cie10)
                if codigos_parseados:
                    codigo_principal = codigos_parseados[0]
                    item = {
                        **item,
                        "codigo": codigo_principal.get("codigo", codigo),
                        "descripcion": codigo_principal.get("descripcion", item.get("descripcion", "")),
                    }
                else:
                    item = {
                        **item,
                        "codigo": "Pendiente",
                        "descripcion": "Codificacion automatica no disponible para este diagnostico.",
                    }
            except Exception:
                item = {
                    **item,
                    "codigo": "Pendiente",
                    "descripcion": "Codificacion automatica no disponible para este diagnostico.",
                }
        normalized.append(item)
    return normalized


def _strip_html_tags(value: str) -> str:
    return re.sub(r"<[^>]+>", "", value or "").strip()


def _get_epicrisis_pdf_cache_collection(mongo_storage):
    return mongo_storage.collection.database[EPICRISIS_PDF_CACHE_COLLECTION]


def _inject_print_script(html_pdf: str) -> str:
    script = '<script>window.addEventListener("load", function () { window.print(); });</script>'
    lower_html = html_pdf.lower()
    body_close_index = lower_html.rfind("</body>")
    if body_close_index >= 0:
        return f"{html_pdf[:body_close_index]}{script}{html_pdf[body_close_index:]}"
    return f"{html_pdf}{script}"


def _case_repo() -> MongoBatchCaseRepository:
    return MongoBatchCaseRepository()


def _canonical_case_epicrisis_url(case_key: str) -> str:
    return f"/epicrisis?case_key={case_key}"


def _find_latest_case_document(mongo, username: str, case_key: str) -> dict | None:
    return mongo.collection.find_one(
        {
            "usuario": username,
            "case_key": case_key,
            "tipo_documento": {"$nin": ["epicrisis", "epicrisis_case_cache"]},
        },
        sort=[("fecha_analisis", -1)],
    )


def _queue_case_epicrisis_job(
    *,
    services: AppServices,
    username: str,
    case_key: str,
    regen: bool = False,
) -> dict[str, object]:
    now = datetime.now(services.colombia_tz).isoformat()
    case_repo = _case_repo()
    case = case_repo.get_user_case(username, case_key)
    latest_case_doc = _find_latest_case_document(services.mongo_analyses, username, case_key)

    if not case:
        if not latest_case_doc:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Caso no encontrado")

        cached_context = None if regen else services.case_epicrisis_service.get_cached_case_context(
            username,
            case_key,
        )
        if cached_context and isinstance(cached_context.get("contexto"), dict):
            return {
                "job_id": "",
                "status": "completado",
                "reused": True,
                "epicrisis_url": _canonical_case_epicrisis_url(case_key),
            }

        services.case_epicrisis_service.cache_case_context(
            username,
            case_key,
            regen=regen,
        )
        return {
            "job_id": "",
            "status": "completado",
            "reused": False,
            "epicrisis_url": _canonical_case_epicrisis_url(case_key),
        }

    if not case.get("ready_for_epicrisis"):
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="El caso aún no esta listo para generar epicrisis.",
        )

    cached_context = None if regen else services.case_epicrisis_service.get_cached_case_context(
        username,
        case_key,
    )
    if cached_context and isinstance(cached_context.get("contexto"), dict):
        case_repo.update_case(
            case["batch_id"],
            case_key,
            {
                "epicrisis_status": "completado",
                "epicrisis_url": _canonical_case_epicrisis_url(case_key),
                "epicrisis_error": "",
                "updated_at": now,
            },
        )
        return {
            "job_id": str(case.get("epicrisis_job_id") or ""),
            "status": "completado",
            "reused": True,
            "epicrisis_url": _canonical_case_epicrisis_url(case_key),
        }

    current_status = str(case.get("epicrisis_status") or "").strip()
    current_job_id = str(case.get("epicrisis_job_id") or "").strip()
    if not regen and current_status in {"en_cola", "procesando"} and current_job_id:
        return {
            "job_id": current_job_id,
            "status": current_status,
            "reused": True,
            "epicrisis_url": str(case.get("epicrisis_url") or _canonical_case_epicrisis_url(case_key)),
        }

    dispatcher_mode = os.getenv("BATCH_DISPATCHER", "inprocess").strip().lower()
    if dispatcher_mode == "celery":
        from app.batch_processing.celery_app import generate_epicrisis_job

        result = generate_epicrisis_job.delay(username, case_key, regen)
        case_repo.update_case(
            case["batch_id"],
            case_key,
            {
                "epicrisis_status": "en_cola",
                "epicrisis_job_id": result.id,
                "epicrisis_error": "",
                "epicrisis_url": _canonical_case_epicrisis_url(case_key),
                "updated_at": now,
            },
        )
        return {
            "job_id": result.id,
            "status": "en_cola",
            "reused": False,
            "epicrisis_url": _canonical_case_epicrisis_url(case_key),
        }

    def _run_inprocess() -> None:
        repo = _case_repo()
        repo.update_case(
            case["batch_id"],
            case_key,
            {
                "epicrisis_status": "procesando",
                "epicrisis_job_id": str(case.get("epicrisis_job_id") or ""),
                "updated_at": datetime.now(services.colombia_tz).isoformat(),
            },
        )
        try:
            services.case_epicrisis_service.cache_case_context(
                username,
                case_key,
                regen=regen,
            )
            repo.update_case(
                case["batch_id"],
                case_key,
                {
                    "epicrisis_status": "completado",
                    "epicrisis_url": _canonical_case_epicrisis_url(case_key),
                    "epicrisis_error": "",
                    "updated_at": datetime.now(services.colombia_tz).isoformat(),
                },
            )
        except Exception as exc:
            repo.update_case(
                case["batch_id"],
                case_key,
                {
                    "epicrisis_status": "fallido",
                    "epicrisis_error": str(exc),
                    "updated_at": datetime.now(services.colombia_tz).isoformat(),
                },
            )

    job_id = f"inprocess-{case_key}-{int(datetime.now().timestamp())}"
    case_repo.update_case(
        case["batch_id"],
        case_key,
        {
            "epicrisis_status": "en_cola",
            "epicrisis_job_id": job_id,
            "epicrisis_error": "",
            "epicrisis_url": _canonical_case_epicrisis_url(case_key),
            "updated_at": now,
        },
    )
    Thread(target=_run_inprocess, daemon=True).start()
    return {
        "job_id": job_id,
        "status": "en_cola",
        "reused": False,
        "epicrisis_url": _canonical_case_epicrisis_url(case_key),
    }


def _build_medication_display_list(items) -> List[str]:
    return [formatear_medicamento_canonico(item) for item in normalizar_lista_medicamentos(items)]


def _normalize_factura_medicamentos(doc: Optional[Dict]) -> Optional[Dict]:
    if not isinstance(doc, dict):
        return doc
    factura_json = doc.get("factura_json")
    if not isinstance(factura_json, dict):
        return doc
    servicios = factura_json.get("servicios_procedimientos")
    if not isinstance(servicios, dict):
        return doc
    servicios["medicamentos"] = normalizar_lista_medicamentos(
        servicios.get("medicamentos"), fuente="factura"
    )
    return doc


def _extraer_procedimientos_factura(factura_doc: Optional[Dict]) -> List[Dict[str, str]]:
    if not factura_doc:
        return []

    procedimientos: List[Dict[str, str]] = []
    factura_json = factura_doc.get("factura_json") if isinstance(factura_doc, dict) else None

    if isinstance(factura_json, dict):
        servicios = factura_json.get("servicios_procedimientos") or {}
        for item in servicios.get("procedimientos_quirurgicos") or []:
            if not isinstance(item, dict):
                continue
            descripcion = (
                str(item.get("descripcion") or "").strip()
                or str(item.get("concepto") or "").strip()
            )
            codigo = str(item.get("codigo_cups") or "").strip()
            if descripcion and descripcion.lower() not in {"procedimiento", "[desc]", "no especificado"}:
                procedimientos.append({"codigo_soat": codigo, "descripcion": descripcion})

    if procedimientos:
        return procedimientos

    factura_html = str((factura_doc.get("analisis_html") if isinstance(factura_doc, dict) else "") or "")
    if not factura_html:
        return []

    match = re.search(
        r"Procedimientos\s+quir[Ãºu]rgicos.*?<table[^>]*>(.*?)</table>",
        factura_html,
        flags=re.IGNORECASE | re.DOTALL,
    )
    if not match:
        return []

    table_html = match.group(1)
    rows = re.findall(r"<tr[^>]*>(.*?)</tr>", table_html, flags=re.IGNORECASE | re.DOTALL)
    for row in rows:
        cells = re.findall(r"<td[^>]*>(.*?)</td>", row, flags=re.IGNORECASE | re.DOTALL)
        if len(cells) < 3:
            continue
        concepto = _strip_html_tags(cells[0])
        codigo = _strip_html_tags(cells[1])
        descripcion = _strip_html_tags(cells[2])
        descripcion_final = descripcion or concepto
        if not descripcion_final:
            continue
        if descripcion_final.lower() in {"procedimiento", "[desc]", "no especificado"}:
            continue
        procedimientos.append({"codigo_soat": codigo, "descripcion": descripcion_final})

    return procedimientos


def _merge_codigos_soat(existing: List[Dict], nuevos: List[Dict]) -> List[Dict]:
    merged: List[Dict] = []
    seen = set()
    for item in (existing or []) + (nuevos or []):
        if not isinstance(item, dict):
            continue
        key = (
            str(item.get("codigo_soat") or "").strip().lower(),
            str(item.get("descripcion") or "").strip().lower(),
        )
        if key in seen:
            continue
        seen.add(key)
        merged.append(item)
    return merged

@router.get("/", response_class=HTMLResponse)
async def login_form(request: Request, services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("login.html", {"request": request})

@router.get("/consulta", response_class=HTMLResponse)
async def consulta(request: Request, services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("consulta.html", {"request": request})

@router.get("/subir_documento", response_class=HTMLResponse)
async def subir_documento_form(request: Request, services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("subir_documento.html", {"request": request})

@router.get("/ver_historia", response_class=HTMLResponse)
async def ver_historia(request: Request, id: str, services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("ver_historia.html", {"request": request, "historia_id": id})

@router.get("/pacientes", response_class=HTMLResponse)
async def ver_pacientes(request: Request, services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("ver_pacientes.html", {"request": request})

@router.get("/paciente/epicrisis", response_class=HTMLResponse)
async def epicrisis_view(request: Request, nombre_paciente: str = "BLANCA LIEJA", current_user = Depends(get_current_user), services: AppServices = Depends(get_services)):
    """Renderiza la vista de epicrisis combinando historia clÃ­nica y documento quirÃºrgico del paciente."""
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    cie10_retriever = services.cie10_retriever
    try:
        def serialize_doc(doc):
            if not doc:
                return None
            doc["_id"] = str(doc.get("_id")) if doc.get("_id") else None
            if "fecha_analisis" in doc and isinstance(doc["fecha_analisis"], datetime):
                doc["fecha_analisis"] = doc["fecha_analisis"].isoformat()
            if "codigos_cie10" in doc:
                doc["codigos_cie10"] = _normalizar_cie10(doc["codigos_cie10"], retriever=cie10_retriever)
            return doc
        
        # Buscar historia clÃ­nica mÃ¡s reciente
        historia = mongo.collection.find_one(
            {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": "historia_clinica"},
            sort=[("fecha_analisis", -1)]
        )
        if not historia:
            # Fallback para documentos antiguos sin tipo_documento
            historia = mongo.collection.find_one(
                {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": {"$exists": False}},
                sort=[("fecha_analisis", -1)]
            )
        
        # Buscar documento quirÃºrgico mÃ¡s reciente
        quirurgico = mongo.collection.find_one(
            {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": "quirurgico"},
            sort=[("fecha_analisis", -1)]
        )
        
        # Buscar factura mÃ¡s reciente
        factura = mongo.collection.find_one(
            {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": "factura"},
            sort=[("fecha_analisis", -1)]
        )
        
        soat_resultados, glosa_html = _construir_soat_y_glosa(
            historia_html=(historia.get("analisis_html") if historia else None),
            qx_html=(quirurgico.get("analisis_html") if quirurgico else None),
            soat_retriever=soat_retriever,
            client_groq=client_groq
        )
        
        # NUEVA FEATURE: Generar cÃ³digos CIE-10 y CUPS desde SOAT usando Gemini
        codigos_desde_soat = []
        if soat_resultados and len(soat_resultados) > 0:
            try:
                descripciones_soat = [
                    {
                        "codigo_soat": item.get("codigo_soat", ""),
                        "descripcion": item.get("descripcion", "")
                    }
                    for item in soat_resultados[:5]  # Limitar a los primeros 5 para no saturar
                ]
                if descripciones_soat:
                    payload_gemini = {"descripciones": descripciones_soat}
                    resultado_gemini = await generar_codigos_desde_soat_gemini(payload_gemini, current_user, services)
                    codigos_desde_soat = resultado_gemini.get("resultados", [])
            except Exception as e:
                print(f"âš ï¸ Error generando cÃ³digos desde SOAT con Gemini: {e}")
                codigos_desde_soat = []
        
        try:
            procedimientos_factura = _extraer_procedimientos_factura(factura)
            if procedimientos_factura:
                resultado_factura = await generar_codigos_desde_soat_gemini(
                    {"descripciones": procedimientos_factura},
                    current_user,
                    services,
                )
                codigos_desde_soat = _merge_codigos_soat(
                    codigos_desde_soat,
                    resultado_factura.get("resultados", []),
                )
        except Exception as e:
            print(f"Error procesando procedimientos de factura automaticamente: {e}")

        # Extraer metadatos completos de la historia clÃ­nica
        metadatos_hc = extraer_metadatos_historia(
            historia.get("analisis_html") if historia else None
        )
        
        # Extraer procedimientos de la historia clÃ­nica
        procedimientos_hc = extraer_procedimientos_historia(
            historia.get("analisis_html") if historia else None
        )
        
        # Extraer medicamentos de la historia clÃ­nica
        medicamentos_hc = extraer_medicamentos_historia(
            historia.get("analisis_html") if historia else None
        )
        medicamentos_hc_display = _build_medication_display_list(medicamentos_hc)
        
        # Extraer secciones 5 y 6 del documento quirÃºrgico
        hallazgos_qx, descripcion_qx = extraer_secciones_quirurgicas(
            quirurgico.get("analisis_html") if quirurgico else None
        )
        
        contexto = {
            "request": request,
            "user": current_user,
            "nombre_paciente": nombre_paciente,
            "historia": serialize_doc(historia),
            "quirurgico": serialize_doc(quirurgico),
            "factura": _normalize_factura_medicamentos(serialize_doc(factura)),
            "metadatos_hc": metadatos_hc,
            "procedimientos_hc": procedimientos_hc,
            "medicamentos_hc": medicamentos_hc,
            "medicamentos_hc_display": medicamentos_hc_display,
            "hallazgos_quirurgicos": hallazgos_qx,
            "descripcion_procedimiento": descripcion_qx,
            "soat_resultados": soat_resultados,
            "glosa_analisis": glosa_html,
            "codigos_desde_soat": codigos_desde_soat,
            "regen_url": None,
            "epicrisis_cached": False,
        }
        return templates.TemplateResponse("epicrisis.html", contexto)
    except Exception as e:
        print(f"âŒ Error en epicrisis_view: {e}")
        raise HTTPException(status_code=500, detail="Error al cargar la epicrisis")

@router.get("/epicrisis", response_class=HTMLResponse)
async def epicrisis_view(
    request: Request,
    id: Optional[str] = None,
    case_key: Optional[str] = None,
    regen: bool = False,
    current_user = Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    """Vista canónica de epicrisis: prioriza case_key y mantiene compatibilidad por ID."""
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    soat_retriever = services.soat_retriever
    cie10_retriever = services.cie10_retriever
    try:
        normalized_case_key = str(case_key or "").strip()
        if normalized_case_key:
            latest_case_doc = _find_latest_case_document(
                mongo,
                current_user.username,
                normalized_case_key,
            )
            cache_doc = None if regen else services.case_epicrisis_service.get_cached_case_context(
                current_user.username, normalized_case_key
            )
            if cache_doc and isinstance(cache_doc.get("contexto"), dict):
                contexto = dict(cache_doc["contexto"])
                contexto["request"] = request
                contexto["user"] = current_user
                contexto["regen_url"] = f"{_canonical_case_epicrisis_url(normalized_case_key)}&regen=1"
                contexto["regen_case_key"] = normalized_case_key
                contexto["epicrisis_cached"] = True
                return templates.TemplateResponse("epicrisis.html", contexto)

            case = _case_repo().get_user_case(current_user.username, normalized_case_key)
            if not case and latest_case_doc:
                contexto = services.case_epicrisis_service.cache_case_context(
                    current_user.username,
                    normalized_case_key,
                    regen=regen,
                )
                contexto["request"] = request
                contexto["user"] = current_user
                contexto["regen_url"] = f"{_canonical_case_epicrisis_url(normalized_case_key)}&regen=1"
                contexto["regen_case_key"] = normalized_case_key
                contexto["epicrisis_cached"] = False
                return templates.TemplateResponse("epicrisis.html", contexto)

            if not case:
                raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Caso no encontrado")

            _queue_case_epicrisis_job(
                services=services,
                username=current_user.username,
                case_key=normalized_case_key,
                regen=regen,
            )

            html = f"""
            <html><head><meta charset='utf-8'><title>Epicrisis en preparación</title></head>
            <body style='font-family: Arial, sans-serif; padding: 24px;'>
              <h2>Epicrisis del caso en preparación</h2>
              <p><strong>Caso:</strong> {case.get('case_number') or normalized_case_key}</p>
              <p><strong>Estado:</strong> {case.get('epicrisis_status') or 'pendiente'}</p>
              <p>Actualiza la página en unos segundos o espera a que el worker termine.</p>
            </body></html>
            """
            return HTMLResponse(content=html, status_code=202)

        if not id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Debes indicar 'case_key' o 'id' para cargar la epicrisis.",
            )
        if not ObjectId.is_valid(id):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="ID no válido")

        base_doc = mongo.collection.find_one({"_id": ObjectId(id)})
        if not base_doc or base_doc.get("usuario") != current_user.username:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Documento no encontrado o acceso denegado")

        base_case_key = str(base_doc.get("case_key") or "").strip()
        if base_case_key:
            return RedirectResponse(
                url=f"{_canonical_case_epicrisis_url(base_case_key)}{'&regen=1' if regen else ''}",
                status_code=status.HTTP_307_TEMPORARY_REDIRECT,
            )

        nombre_paciente = base_doc.get("nombre_paciente")
        if not nombre_paciente:
            analisis_html = base_doc.get("analisis_html") or base_doc.get("analisis") or ""
            nombre_paciente = extraer_nombre_paciente(analisis_html)

        def serialize_doc(doc):
            if not doc:
                return None
            doc["_id"] = str(doc.get("_id")) if doc.get("_id") else None
            if "fecha_analisis" in doc and isinstance(doc["fecha_analisis"], datetime):
                doc["fecha_analisis"] = doc["fecha_analisis"].isoformat()
            if "codigos_cie10" in doc:
                doc["codigos_cie10"] = _normalizar_cie10(doc["codigos_cie10"], retriever=cie10_retriever)
            return doc

        historia = None
        quirurgico = None
        factura = None
        tipo = base_doc.get("tipo_documento")
        if tipo == "historia_clinica" or not tipo:
            historia = base_doc
        elif tipo == "quirurgico":
            quirurgico = base_doc
        elif tipo == "factura":
            factura = base_doc

        if not historia and nombre_paciente and nombre_paciente != "desconocido":
            historia = mongo.collection.find_one(
                {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": "historia_clinica"},
                sort=[("fecha_analisis", -1)]
            ) or mongo.collection.find_one(
                {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": {"$exists": False}},
                sort=[("fecha_analisis", -1)]
            )
        if not quirurgico and nombre_paciente and nombre_paciente != "desconocido":
            quirurgico = mongo.collection.find_one(
                {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": "quirurgico"},
                sort=[("fecha_analisis", -1)]
            )
        if not factura and nombre_paciente and nombre_paciente != "desconocido":
            factura = mongo.collection.find_one(
                {"usuario": current_user.username, "nombre_paciente": nombre_paciente, "tipo_documento": "factura"},
                sort=[("fecha_analisis", -1)]
            )

        historia_id = str(historia.get("_id")) if historia and historia.get("_id") else None
        quirurgico_id = str(quirurgico.get("_id")) if quirurgico and quirurgico.get("_id") else None
        factura_id = str(factura.get("_id")) if factura and factura.get("_id") else None

        if not regen:
            cache_doc = mongo.collection.find_one(
                {
                    "usuario": current_user.username,
                    "tipo_documento": "epicrisis",
                    "epicrisis_base_id": id,
                    "historia_id": historia_id,
                    "quirurgico_id": quirurgico_id,
                    "factura_id": factura_id,
                },
                sort=[("fecha_analisis", -1)],
            )
            if cache_doc and isinstance(cache_doc.get("contexto"), dict):
                contexto_cache = cache_doc["contexto"]
                contexto_cache["medicamentos_hc"] = normalizar_lista_medicamentos(
                    contexto_cache.get("medicamentos_hc"), fuente="historia_clinica"
                )
                contexto_cache["medicamentos_hc_display"] = _build_medication_display_list(
                    contexto_cache.get("medicamentos_hc")
                )
                contexto_cache["factura"] = _normalize_factura_medicamentos(contexto_cache.get("factura"))
                contexto_cache["request"] = request
                contexto_cache["user"] = current_user
                contexto_cache["regen_url"] = f"/epicrisis?id={id}&regen=1"
                contexto_cache["regen_case_key"] = ""
                contexto_cache["epicrisis_cached"] = True
                return templates.TemplateResponse("epicrisis.html", contexto_cache)

        soat_resultados, glosa_html = _construir_soat_y_glosa(
            historia_html=(historia.get("analisis_html") if historia else None),
            qx_html=(quirurgico.get("analisis_html") if quirurgico else None),
            soat_retriever=soat_retriever,
            client_groq=client_groq
        )

        codigos_desde_soat = []
        if soat_resultados and len(soat_resultados) > 0:
            try:
                descripciones_soat = [
                    {
                        "codigo_soat": item.get("codigo_soat", ""),
                        "descripcion": item.get("descripcion", "")
                    }
                    for item in soat_resultados[:5]
                ]
                if descripciones_soat:
                    payload_gemini = {"descripciones": descripciones_soat}
                    resultado_gemini = await generar_codigos_desde_soat_gemini(payload_gemini, current_user, services)
                    codigos_desde_soat = resultado_gemini.get("resultados", [])
            except Exception as e:
                print(f"Error generando códigos desde SOAT con Gemini: {e}")
                codigos_desde_soat = []

        try:
            procedimientos_factura = _extraer_procedimientos_factura(factura)
            if procedimientos_factura:
                resultado_factura = await generar_codigos_desde_soat_gemini(
                    {"descripciones": procedimientos_factura},
                    current_user,
                    services,
                )
                codigos_desde_soat = _merge_codigos_soat(
                    codigos_desde_soat,
                    resultado_factura.get("resultados", []),
                )
        except Exception as e:
            print(f"Error procesando procedimientos de factura automaticamente: {e}")

        metadatos_hc = extraer_metadatos_historia(
            historia.get("analisis_html") if historia else None
        )
        procedimientos_hc = extraer_procedimientos_historia(
            historia.get("analisis_html") if historia else None
        )
        medicamentos_hc = extraer_medicamentos_historia(
            historia.get("analisis_html") if historia else None
        )
        medicamentos_hc_display = _build_medication_display_list(medicamentos_hc)
        hallazgos_qx, descripcion_qx = extraer_secciones_quirurgicas(
            quirurgico.get("analisis_html") if quirurgico else None
        )

        contexto = {
            "request": request,
            "user": current_user,
            "nombre_paciente": nombre_paciente or "desconocido",
            "historia": serialize_doc(historia),
            "quirurgico": serialize_doc(quirurgico),
            "factura": _normalize_factura_medicamentos(serialize_doc(factura)),
            "metadatos_hc": metadatos_hc,
            "procedimientos_hc": procedimientos_hc,
            "medicamentos_hc": medicamentos_hc,
            "medicamentos_hc_display": medicamentos_hc_display,
            "hallazgos_quirurgicos": hallazgos_qx,
            "descripcion_procedimiento": descripcion_qx,
            "soat_resultados": soat_resultados,
            "glosa_analisis": glosa_html,
            "codigos_desde_soat": codigos_desde_soat,
            "regen_url": f"/epicrisis?id={id}&regen=1",
            "regen_case_key": "",
            "epicrisis_cached": False,
        }

        try:
            mongo.collection.insert_one(
                {
                    "usuario": current_user.username,
                    "tipo_documento": "epicrisis",
                    "epicrisis_base_id": id,
                    "nombre_paciente": nombre_paciente or "desconocido",
                    "historia_id": historia_id,
                    "quirurgico_id": quirurgico_id,
                    "factura_id": factura_id,
                    "fecha_analisis": datetime.now(),
                    "regen_requested": bool(regen),
                    "contexto": {
                        "nombre_paciente": nombre_paciente or "desconocido",
                        "historia": contexto["historia"],
                        "quirurgico": contexto["quirurgico"],
                        "factura": contexto["factura"],
                        "metadatos_hc": contexto["metadatos_hc"],
                        "procedimientos_hc": contexto["procedimientos_hc"],
                        "medicamentos_hc": contexto["medicamentos_hc"],
                        "medicamentos_hc_display": contexto["medicamentos_hc_display"],
                        "hallazgos_quirurgicos": contexto["hallazgos_quirurgicos"],
                        "descripcion_procedimiento": contexto["descripcion_procedimiento"],
                        "soat_resultados": contexto["soat_resultados"],
                        "glosa_analisis": contexto["glosa_analisis"],
                        "codigos_desde_soat": contexto["codigos_desde_soat"],
                        "regen_url": contexto["regen_url"],
                        "regen_case_key": "",
                        "epicrisis_cached": False,
                    },
                }
            )
        except Exception as cache_error:
            print(f"Error guardando cache de epicrisis: {cache_error}")

        return templates.TemplateResponse("epicrisis.html", contexto)
    except HTTPException:
        raise
    except Exception as e:
        print(f"Error en epicrisis_view: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error al generar la epicrisis")


@router.post("/epicrisis/pdf_cache")
async def guardar_epicrisis_pdf_cache(
    request: Request,
    current_user=Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    mongo = services.mongo_analyses
    try:
        payload = await request.json()
        if not isinstance(payload, dict):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Payload invalido")

        html_pdf = str(payload.get("html_pdf") or "").strip()
        if not html_pdf:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="El campo 'html_pdf' es obligatorio")

        nombre_paciente = str(payload.get("nombre_paciente") or "").strip() or "desconocido"
        case_key = str(payload.get("case_key") or "").strip() or None
        epicrisis_base_id = str(payload.get("epicrisis_base_id") or "").strip() or None
        historia_id = str(payload.get("historia_id") or "").strip() or None
        quirurgico_id = str(payload.get("quirurgico_id") or "").strip() or None
        factura_id = str(payload.get("factura_id") or "").strip() or None

        def _normalize_text_list(values):
            if not isinstance(values, list):
                return []
            clean = []
            for item in values:
                text = str(item or "").strip()
                if text:
                    clean.append(text)
            return clean

        notas_medicas = payload.get("notas_medicas")
        if not isinstance(notas_medicas, dict):
            notas_medicas = {}
        medicamentos_detallados = normalizar_lista_medicamentos(
            payload.get("medicamentos"), fuente="cache_pdf"
        )

        cache_collection = _get_epicrisis_pdf_cache_collection(mongo)
        insert_result = cache_collection.insert_one(
            {
                "usuario": current_user.username,
                "tipo_documento": "epicrisis_pdf_cache",
                "nombre_paciente": nombre_paciente,
                "case_key": case_key,
                "epicrisis_base_id": epicrisis_base_id,
                "historia_id": historia_id,
                "quirurgico_id": quirurgico_id,
                "factura_id": factura_id,
                "fecha_guardado": datetime.now(),
                "html_pdf": html_pdf,
                "resumen_clinico": str(payload.get("resumen_clinico") or ""),
                "hallazgos_quirurgicos": str(payload.get("hallazgos_quirurgicos") or ""),
                "descripcion_procedimiento": str(payload.get("descripcion_procedimiento") or ""),
                "diagnosticos": _normalize_text_list(payload.get("diagnosticos")),
                "procedimientos": _normalize_text_list(payload.get("procedimientos")),
                "medicamentos": [formatear_medicamento_canonico(item) for item in medicamentos_detallados],
                "medicamentos_detallados": medicamentos_detallados,
                "notas_medicas": {
                    "hallazgos_extra": str(notas_medicas.get("hallazgos_extra") or ""),
                    "descripcion_procedimiento_extra": str(
                        notas_medicas.get("descripcion_procedimiento_extra") or ""
                    ),
                },
            }
        )

        return {
            "mensaje": "PDF de epicrisis almacenado",
            "cache_id": str(insert_result.inserted_id),
            "nombre_paciente": nombre_paciente,
            "case_key": case_key,
            "fecha_guardado": datetime.now().isoformat(),
        }
    except HTTPException:
        raise
    except Exception as e:
        print(f"Error guardando cache PDF de epicrisis: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="No se pudo guardar el PDF de epicrisis")


@router.get("/epicrisis/caso/{case_key}", response_class=HTMLResponse)
async def epicrisis_case_view(
    case_key: str,
    current_user=Depends(get_current_user),
):
    case = _case_repo().get_user_case(current_user.username, case_key)
    if not case:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Caso no encontrado")
    return RedirectResponse(url=_canonical_case_epicrisis_url(case_key), status_code=status.HTTP_307_TEMPORARY_REDIRECT)


@router.post("/epicrisis")
async def solicitar_epicrisis_case_aware(
    case_key: str = "",
    regen: bool = False,
    current_user=Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    normalized_case_key = str(case_key or "").strip()
    if not normalized_case_key:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="El parámetro 'case_key' es obligatorio.",
        )

    queue_result = _queue_case_epicrisis_job(
        services=services,
        username=current_user.username,
        case_key=normalized_case_key,
        regen=regen,
    )
    case = _case_repo().get_user_case(current_user.username, normalized_case_key) or {}
    latest_case_doc = _find_latest_case_document(
        services.mongo_analyses,
        current_user.username,
        normalized_case_key,
    ) or {}
    current_status = str(queue_result.get("status") or case.get("epicrisis_status") or "en_cola")
    return JSONResponse(
        status_code=200 if current_status == "completado" else 202,
        content={
            "case_key": normalized_case_key,
            "case_number": case.get("case_number") or latest_case_doc.get("case_number", ""),
            "epicrisis_status": current_status,
            "epicrisis_job_id": queue_result.get("job_id", case.get("epicrisis_job_id", "")),
            "epicrisis_url": queue_result.get(
                "epicrisis_url",
                case.get("epicrisis_url", _canonical_case_epicrisis_url(normalized_case_key)),
            ),
            "reused": bool(queue_result.get("reused", False)),
        },
    )


@router.get("/epicrisis/estado")
async def estado_epicrisis_case_aware(
    case_key: str = "",
    current_user=Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    normalized_case_key = str(case_key or "").strip()
    if not normalized_case_key:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="El parámetro 'case_key' es obligatorio.",
        )

    case = _case_repo().get_user_case(current_user.username, normalized_case_key)
    latest_case_doc = _find_latest_case_document(
        services.mongo_analyses,
        current_user.username,
        normalized_case_key,
    )
    if not case and not latest_case_doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Caso no encontrado")
    cache_doc = services.case_epicrisis_service.get_cached_case_context(
        current_user.username,
        normalized_case_key,
    )
    case_data = case or {}
    epicrisis_status = case_data.get("epicrisis_status", "pendiente")
    epicrisis_url = case_data.get("epicrisis_url", _canonical_case_epicrisis_url(normalized_case_key))
    if cache_doc and isinstance(cache_doc.get("contexto"), dict):
        epicrisis_status = "completado"
        epicrisis_url = _canonical_case_epicrisis_url(normalized_case_key)
    return {
        "case_key": normalized_case_key,
        "case_number": case_data.get("case_number") or (latest_case_doc or {}).get("case_number", ""),
        "ready_for_epicrisis": case_data.get("ready_for_epicrisis", bool(latest_case_doc)),
        "epicrisis_status": epicrisis_status,
        "epicrisis_job_id": case_data.get("epicrisis_job_id", ""),
        "epicrisis_error": case_data.get("epicrisis_error", ""),
        "epicrisis_url": epicrisis_url,
    }


@router.get("/epicrisis/pdf_cache/print/{cache_id}", response_class=HTMLResponse)
async def imprimir_epicrisis_pdf_cache(
    cache_id: str,
    current_user=Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    mongo = services.mongo_analyses
    try:
        if not ObjectId.is_valid(cache_id):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="ID de cache no valido")

        cache_collection = _get_epicrisis_pdf_cache_collection(mongo)
        cache_doc = cache_collection.find_one(
            {"_id": ObjectId(cache_id), "usuario": current_user.username}
        )
        if not cache_doc:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No se encontro el PDF almacenado")

        html_pdf = str(cache_doc.get("html_pdf") or "").strip()
        if not html_pdf:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="El cache no contiene contenido imprimible")

        return HTMLResponse(content=_inject_print_script(html_pdf))
    except HTTPException:
        raise
    except Exception as e:
        print(f"Error cargando cache PDF de epicrisis: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="No se pudo cargar el PDF de epicrisis")


@router.get("/epicrisis/pdf_cache/ultimo", response_class=HTMLResponse)
async def imprimir_ultimo_pdf_epicrisis_paciente(
    nombre_paciente: str = "",
    case_key: str = "",
    current_user=Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    mongo = services.mongo_analyses
    try:
        cache_collection = _get_epicrisis_pdf_cache_collection(mongo)
        query = {"usuario": current_user.username}
        if case_key:
            query["case_key"] = case_key
        else:
            query["nombre_paciente"] = nombre_paciente
        cache_doc = cache_collection.find_one(query, sort=[("fecha_guardado", -1)])
        if not cache_doc:
            return HTMLResponse(
                status_code=404,
                content=(
                    "<html><head><meta charset='utf-8'><title>PDF no disponible</title></head>"
                    "<body style='font-family: Arial, sans-serif; padding: 24px;'>"
                    "<h2>No hay PDF de epicrisis guardado</h2>"
                    "<p>Primero genera el PDF desde la pagina de epicrisis para este paciente o caso.</p>"
                    "</body></html>"
                ),
            )

        html_pdf = str(cache_doc.get("html_pdf") or "").strip()
        if not html_pdf:
            return HTMLResponse(
                status_code=404,
                content=(
                    "<html><head><meta charset='utf-8'><title>PDF vacio</title></head>"
                    "<body style='font-family: Arial, sans-serif; padding: 24px;'>"
                    "<h2>El ultimo PDF guardado no tiene contenido imprimible</h2>"
                    "<p>Vuelve a generar el PDF desde la epicrisis e intenta nuevamente.</p>"
                    "</body></html>"
                ),
            )

        return HTMLResponse(content=_inject_print_script(html_pdf))
    except HTTPException:
        raise
    except Exception as e:
        print(f"Error cargando ultimo PDF de epicrisis por paciente: {e}")
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="No se pudo cargar el PDF de epicrisis")

@router.get("/soat_chat", response_class=HTMLResponse)
async def soat_chat_page(request: Request, current_user = Depends(get_current_user), services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("soat_chat.html", {"request": request, "user": current_user})

@router.get("/soat_agent", response_class=HTMLResponse)
async def soat_agent_page(request: Request, current_user = Depends(get_current_user), services: AppServices = Depends(get_services)):
    templates = services.templates
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    soat_retriever = services.soat_retriever
    return templates.TemplateResponse("soat_agent.html", {"request": request, "user": current_user})
