from datetime import datetime
import re
import logging

from bson import ObjectId
from fastapi import APIRouter, HTTPException, Request, Depends, Path as ApiPath

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.schemas.history import Historia
from app.services.clinical_document_service import ClinicalDocumentRequest
from modules.processing.resumen_google import HistoriaClinicaRequest

router = APIRouter()
logger = logging.getLogger(__name__)
_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 _build_case_group_key(doc: dict) -> str:
    case_key = str(doc.get("case_key") or "").strip()
    if case_key:
        return case_key
    nombre_paciente = str(doc.get("nombre_paciente") or "desconocido").strip() or "desconocido"
    return f"legacy::{nombre_paciente}"


def _extraer_cie10_inline(diagnostico: str):
    if not diagnostico:
        return None
    match = _CIE10_INLINE_PATTERN.match(str(diagnostico).strip())
    if not match:
        return None
    return {
        "codigo": match.group("codigo").upper(),
        "descripcion": match.group("descripcion").strip(),
    }


def _sanitize_cie10_entries(entries):
    """
    Oculta mensajes tecnicos de infraestructura para no mostrarlos en UI.
    """
    if not isinstance(entries, list):
        return entries

    sanitized = []
    for item in entries:
        if not isinstance(item, dict):
            sanitized.append(item)
            continue

        diagnostico = str(item.get("diagnostico", "")).strip()
        codigo = str(item.get("codigo", "")).strip()
        descripcion = str(item.get("descripcion", "")).strip()
        descripcion_low = descripcion.lower()
        codigo_low = codigo.lower()

        # Si el diagnostico ya trae codigo CIE-10, priorizarlo sobre estados tecnicos.
        inline = _extraer_cie10_inline(diagnostico)
        if inline and codigo_low in {"", "error", "pendiente", "sin codigo", "sin código"}:
            item = {
                **item,
                "codigo": inline["codigo"],
                "descripcion": inline["descripcion"],
            }
            sanitized.append(item)
            continue

        if codigo_low == "error" or "indice faiss cie-10 no disponible" in descripcion_low:
            item = {
                **item,
                "codigo": "Pendiente",
                "descripcion": "Codificacion automatica no disponible para este diagnostico.",
            }

        sanitized.append(item)
    return sanitized
@router.post("/analisis_historia")
async def realizar_analisis_historia(request: Request, services: AppServices = Depends(get_services)):
    """Realiza un análisis de la historia clínica"""
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    try:
        content_type = request.headers.get('Content-Type')
        if content_type == "application/json":
            data = await request.json()
            descripcion = data.get("descripcion")
        elif content_type == "application/x-www-form-urlencoded":
            form_data = await request.form()
            descripcion = form_data.get("descripcion")
        else:
            raise HTTPException(status_code=400, detail="Content-Type no soportado")

        if not descripcion:
            raise HTTPException(status_code=400, detail="El campo 'descripcion' es requerido")

        analisis = HistoriaClinicaRequest.analizar_historiaclinica(descripcion)
        id_documento = HistoriaClinicaRequest.guardar_historia(descripcion, analisis)

        return {
            "mensaje": "Análisis completado exitosamente",
            "id_documento": id_documento,
            "fecha_analisis": datetime.now().isoformat(),
            "analisis": analisis
        }
    except HTTPException as he:
        raise he    
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/asignar_codigos")
async def asignar_codigos(request: Request, services: AppServices = Depends(get_services)):
    """Asigna códigos CIE-10 a los diagnósticos extraídos en formato HTML"""
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    try:
        data = await request.json()
        analisis_html = data.get("analisis")
        if not analisis_html:
            raise HTTPException(status_code=400, detail="El campo 'analisis' con el HTML de diagnósticos es requerido")

        # Utilizar el retriever para asignar códigos batch
        df_codigos = retriever.asignar_codigos_batch(analisis_html)
        # Convertir a lista de dicts para la respuesta JSON
        registros = df_codigos.to_dict(orient="records")

        return {
            "mensaje": "Códigos asignados exitosamente",
            "codigos": registros
        }
    except HTTPException as he:
        raise he
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/analizar_ultimo_pdf")
def analizar_ultimo_pdf(services: AppServices = Depends(get_services)):
    """
    Consulta el último documento con 'contenido_pdf' en MongoDB 
    y realiza el análisis usando Groq.
    """
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    try:
        # Buscar el documento más reciente con contenido_pdf
        ultimo_doc = mongo_storage.collection.find_one(
            {"contenido_pdf": {"$exists": True}},
            sort=[("fecha_guardado", -1)]
        )

        if not ultimo_doc:
            raise HTTPException(status_code=404, detail="No se encontró ningún documento con contenido_pdf.")

        descripcion = ultimo_doc.get("contenido_pdf")
        nombre_archivo = ultimo_doc.get("nombre_archivo")
        usuario = ultimo_doc.get("usuario")

        if not descripcion:
            raise HTTPException(status_code=400, detail="El campo 'contenido_pdf' está vacío.")

        # Ejecutar análisis con el contenido del PDF
        analisis = HistoriaClinicaRequest.analizar_historiaclinica(descripcion)

        # Guardar el resultado como un nuevo documento
        id_doc = HistoriaClinicaRequest.guardar_historia(descripcion=descripcion, analisis=analisis)

        return {
            "mensaje": "Análisis completado exitosamente",
            "id_documento": id_doc,
            "nombre_archivo": nombre_archivo,
            "usuario": usuario,
            "fecha_analisis": datetime.now().isoformat(),
            "analisis": analisis
        }

    except HTTPException as he:
        raise he
    except Exception as e:
        print("❌ Error en analizar_ultimo_pdf:", str(e))  # Para depuración en consola
        raise HTTPException(status_code=500, detail=f"Error interno: {str(e)}")

@router.post("/guardar_historia")
async def guardar_historia(
    data: Historia,
    current_user = Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    documento = {
        "descripcion": data.descripcion,
        "analisis": data.analisis,
        "usuario": current_user.username,
        "fecha_guardado": datetime.utcnow()
    }
    mongo.collection.insert_one(documento)
    return {"status": "guardado", "fecha": documento["fecha_guardado"]}

@router.get("/procesar_ultimo_pdf")
async def procesar_ultimo_pdf(current_user = Depends(get_current_user), services: AppServices = Depends(get_services)):
    """
    Procesa el ultimo PDF de historia clinica del usuario autenticado.
    Si falla el analisis principal (Gemini), usa fallback con Groq para evitar 500.
    """
    mongo_storage = services.mongo_storage

    try:
        doc = mongo_storage.collection.find_one(
            {"contenido_pdf": {"$exists": True}, "usuario": current_user.username},
            sort=[("fecha_guardado", -1)]
        )
        if not doc:
            raise HTTPException(status_code=404, detail="No se encontro ningun PDF para este usuario.")

        desc = doc.get("contenido_pdf")
        if not desc:
            raise HTTPException(status_code=400, detail="El campo 'contenido_pdf' esta vacio.")

        nombre = doc.get("nombre_archivo", "desconocido")
        payload = services.clinical_document_service.process_and_persist(
            ClinicalDocumentRequest(
                raw_text=desc,
                detected_type="historia_clinica",
                username=current_user.username,
                original_name=nombre,
                ingestion_source="manual",
            )
        )
        return {
            **payload,
            "mensaje": payload.get("mensaje") or "Analisis y codificacion completados exitosamente",
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.exception("Error en procesar_ultimo_pdf")
        raise HTTPException(status_code=500, detail=f"Error interno al procesar el PDF: {e}")


@router.get("/mis_historias")
async def obtener_mis_historias(current_user = Depends(get_current_user), services: AppServices = Depends(get_services)):
    """
    Devuelve todas las historias analizadas asociadas al usuario autenticado,
    agrupadas por paciente.
    """
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    try:
        services.clinical_document_service.repair_existing_documents_for_user(
            current_user.username
        )
        # Usar current_user.username para filtrar y ocultar caches de epicrisis del listado principal.
        resultados = mongo.collection.find(
            {
                "usuario": current_user.username,
                "tipo_documento": {"$nin": ["epicrisis", "epicrisis_case_cache"]},
            }
        )
        historias_por_paciente = {}
        historias_por_caso = {}
        case_repository = MongoBatchCaseRepository()
        case_lookup_cache = {}

        for doc in resultados:
            doc["_id"] = str(doc["_id"])  # Convertir ObjectId a string
            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"] = _sanitize_cie10_entries(doc["codigos_cie10"])
            
            # Agrupar por paciente
            nombre_paciente = doc.get("nombre_paciente", "desconocido")
            if nombre_paciente not in historias_por_paciente:
                historias_por_paciente[nombre_paciente] = []
            
            historias_por_paciente[nombre_paciente].append(doc)

            case_group_key = _build_case_group_key(doc)
            if case_group_key not in historias_por_caso:
                normalized_case_key = str(doc.get("case_key") or "").strip()
                if normalized_case_key not in case_lookup_cache:
                    case_lookup_cache[normalized_case_key] = (
                        case_repository.get_user_case(current_user.username, normalized_case_key)
                        if normalized_case_key
                        else {}
                    ) or {}
                case_info = case_lookup_cache[normalized_case_key]
                historias_por_caso[case_group_key] = {
                    "group_key": case_group_key,
                    "case_key": str(doc.get("case_key") or "").strip(),
                    "case_number": str(doc.get("case_number") or case_info.get("case_number") or "").strip(),
                    "patient_name": nombre_paciente,
                    "patient_id": str(doc.get("patient_id") or case_info.get("patient_id") or "").strip(),
                    "documentos": [],
                    "tipos_documento": [],
                    "ready_for_epicrisis": bool(case_info.get("ready_for_epicrisis", False)),
                    "epicrisis_status": str(case_info.get("epicrisis_status") or "pendiente"),
                    "epicrisis_url": str(
                        case_info.get("epicrisis_url")
                        or (f"/epicrisis?case_key={doc.get('case_key')}" if str(doc.get("case_key") or "").strip() else "")
                    ),
                }

            historias_por_caso[case_group_key]["documentos"].append(doc)

        # Ordenar documentos de cada paciente por fecha (más reciente primero)
        for paciente in historias_por_paciente:
            historias_por_paciente[paciente].sort(
                key=lambda x: x.get("fecha_analisis", ""), 
                reverse=True
            )

        for case_key, case_group in historias_por_caso.items():
            case_group["documentos"].sort(
                key=lambda x: x.get("fecha_analisis", ""),
                reverse=True,
            )
            case_group["tipos_documento"] = sorted(
                {
                    str(item.get("tipo_documento") or "sin_tipo")
                    for item in case_group["documentos"]
                }
            )

        return {
            "historias_por_paciente": historias_por_paciente,
            "historias_por_caso": historias_por_caso,
        }

    except Exception as e:
        print("❌ Error al obtener historias:", str(e))
        raise HTTPException(status_code=500, detail="Error al consultar las historias.")

@router.post("/documentos/eliminar_seleccion")
async def eliminar_documentos_seleccionados(
    payload: dict,
    current_user = Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    """
    Elimina documentos seleccionados por ID del usuario autenticado.
    Nota: no elimina documentos tipo 'epicrisis' para preservar cache.
    """
    mongo = services.mongo_analyses
    try:
        document_ids = payload.get("document_ids") or []
        if not isinstance(document_ids, list) or not document_ids:
            raise HTTPException(status_code=400, detail="'document_ids' debe ser una lista no vacia")

        valid_ids = [
            doc_id
            for doc_id in document_ids
            if isinstance(doc_id, str) and ObjectId.is_valid(doc_id)
        ]
        if not valid_ids:
            raise HTTPException(status_code=400, detail="No hay IDs validos para eliminar")

        object_ids = [ObjectId(doc_id) for doc_id in valid_ids]
        delete_result = mongo.collection.delete_many(
            {
                "_id": {"$in": object_ids},
                "usuario": current_user.username,
                "tipo_documento": {"$nin": ["epicrisis", "epicrisis_case_cache"]},
            }
        )

        return {
            "mensaje": "Documentos eliminados",
            "solicitados": len(document_ids),
            "ids_validos": len(valid_ids),
            "eliminados": int(delete_result.deleted_count),
        }
    except HTTPException:
        raise
    except Exception as e:
        print("Error eliminando documentos seleccionados:", str(e))
        raise HTTPException(status_code=500, detail="Error al eliminar documentos seleccionados")

@router.get("/historia/{id}")
async def obtener_historia_por_id(
    id: str = ApiPath(..., description="ID del documento en MongoDB"),
    current_user = Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    """
    Devuelve los datos completos de una historia si pertenece al usuario autenticado.
    """
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    try:
        # Validación del formato del ID
        if not ObjectId.is_valid(id):
            raise HTTPException(status_code=400, detail="ID no válido")

        # Búsqueda en MongoDB
        doc = mongo.collection.find_one({"_id": ObjectId(id)})

        # Verificación de existencia y pertenencia al usuario
        if not doc or doc.get("usuario") != current_user.username:
            raise HTTPException(status_code=404, detail="Historia no encontrada o acceso denegado")

        # Serialización de campos no JSON compatibles
        doc["_id"] = str(doc["_id"])
        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"] = _sanitize_cie10_entries(doc["codigos_cie10"])

        return doc

    except HTTPException:
        # Re-lanzar excepciones HTTP manejadas
        raise
    except Exception as e:
        # Captura errores internos
        print(f"❌ Error al obtener historia por ID: {e}")
        raise HTTPException(status_code=500, detail="Error interno al consultar la historia")

@router.get("/documentos_paciente/{nombre_paciente}")
async def obtener_documentos_paciente(
    nombre_paciente: str = ApiPath(..., description="Nombre del paciente"),
    current_user = Depends(get_current_user),
    services: AppServices = Depends(get_services),
):
    """
    Devuelve todos los documentos de un paciente específico del usuario autenticado.
    """
    mongo_storage = services.mongo_storage
    mongo = services.mongo_analyses
    client_groq = services.client_groq
    client_gemini = services.client_gemini
    retriever = services.cie10_retriever
    cups_retriever = services.cups_retriever
    soat_retriever = services.soat_retriever
    colombia_tz = services.colombia_tz
    try:
        # Buscar documentos del paciente específico para este usuario
        resultados = mongo.collection.find({
            "usuario": current_user.username,
            "nombre_paciente": nombre_paciente,
            "tipo_documento": {"$nin": ["epicrisis", "epicrisis_case_cache"]},
        }).sort("fecha_analisis", -1)  # Ordenar por fecha desc
        
        documentos = []
        for doc in resultados:
            doc["_id"] = str(doc["_id"])
            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"] = _sanitize_cie10_entries(doc["codigos_cie10"])
            documentos.append(doc)
        
        if not documentos:
            raise HTTPException(
                status_code=404, 
                detail=f"No se encontraron documentos para el paciente '{nombre_paciente}'"
            )
        
        # Agrupar por tipo de documento
        documentos_por_tipo = {}
        for doc in documentos:
            tipo = doc.get("tipo_documento", "sin_tipo")
            if tipo not in documentos_por_tipo:
                documentos_por_tipo[tipo] = []
            documentos_por_tipo[tipo].append(doc)
        
        return {
            "nombre_paciente": nombre_paciente,
            "total_documentos": len(documentos),
            "documentos_por_tipo": documentos_por_tipo,
            "todos_los_documentos": documentos
        }
        
    except HTTPException:
        raise
    except Exception as e:
        print(f"❌ Error al obtener documentos del paciente: {e}")
        raise HTTPException(status_code=500, detail="Error interno al consultar los documentos")
