from __future__ import annotations

from typing import Dict, List
import re

from groq import Groq

from app.config import config
from app.services.patient_name_extraction import extract_patient_name_from_html


_MEDICAMENTO_GENERIC_CODES = {"2801"}
_MEDICAMENTO_CODE_PATTERN = re.compile(r"^[A-Z0-9]{2,12}$", re.IGNORECASE)
_PLACEHOLDER_TOKEN_PATTERN = re.compile(r"^\[(?:dato|desc|valor|med|dosis|cant|unit|total|cups|tipo|dias|tarifa|nombre|observaci[oó]n)\]$", re.IGNORECASE)
_PLACEHOLDER_INLINE_PATTERN = re.compile(r"\[(?:dato|desc|valor|med|dosis|cant|unit|total|cups|tipo|dias|tarifa|nombre|observaci[oó]n)\]", re.IGNORECASE)
_ITEM_KEYWORDS = {
    "insumo_material": [
        "sutura", "curacion", "curación", "gasa", "vendaje", "ferula", "férula",
        "yeso", "algodon", "algodón", "apósito", "aposito", "material", "aguja",
    ],
    "equipo_dispositivo": [
        "cateter", "catéter", "abocat", "buretrol", "equipo", "venoclisis", "jeringa",
        "sonda", "canula", "cánula", "dispositivo",
    ],
    "solucion": [
        "solucion", "solución", "cloruro de sodio", "solucion salina", "solución salina",
        "lactato de ringer", "dextrosa",
    ],
}


def _sanitizar_placeholder(value) -> str:
    texto = str(value or "").strip()
    if not texto:
        return ""
    texto_limpio = _PLACEHOLDER_INLINE_PATTERN.sub("", texto).strip(" -:/\t\r\n")
    if _PLACEHOLDER_TOKEN_PATTERN.match(texto) or texto.lower() in {"no disponible", "n/a", "none"}:
        return ""
    return texto_limpio.strip()


def _clasificar_tipo_item_medicamento(nombre: str, fuente: str) -> tuple[str, str]:
    nombre_norm = _sanitizar_placeholder(nombre).lower()
    if not nombre_norm:
        return "desconocido", "sin_nombre"
    for tipo, keywords in _ITEM_KEYWORDS.items():
        if any(keyword in nombre_norm for keyword in keywords):
            return tipo, "heuristica_keyword"
    if fuente == "factura":
        return "medicamento", "fallback_factura"
    return "medicamento", "fallback_clinico"


def _separar_nombre_y_dosis_en_texto(texto: str) -> tuple[str, str]:
    limpio = _sanitizar_placeholder(texto)
    if not limpio:
        return "", ""
    match = re.search(r"\d", limpio)
    if not match:
        return limpio, ""
    idx = match.start()
    nombre = limpio[:idx].strip(" -:/")
    dosis = limpio[idx:].strip(" -:/")
    if not nombre:
        return limpio, ""
    return nombre, dosis


def _texto_medicamento(value) -> str:
    return str(value or "").strip()


def _resolver_estado_codigo_medicamento(codigo: str, codigo_tipo: str, fuente: str) -> str:
    codigo_norm = _texto_medicamento(codigo).upper()
    if not codigo_norm:
        return "no_identificado"
    if codigo_norm in _MEDICAMENTO_GENERIC_CODES:
        return "pendiente_validacion"
    if fuente == "factura" and codigo_tipo != "medicamento":
        return "pendiente_validacion"
    return "identificado"


def normalizar_medicamento_canonico(item, fuente: str = "historia_clinica") -> Dict[str, str]:
    """
    Normaliza medicamentos de historia clínica, factura o entrada manual a un shape canónico.
    """
    fuente_norm = _texto_medicamento(fuente) or "desconocida"
    texto_original = ""
    codigo = ""
    medicamento = ""
    dosis = ""
    cantidad = ""
    codigo_tipo = "desconocido"
    codigo_origen = "desconocido"
    codigo_estado = ""
    tipo_item = "desconocido"
    tipo_item_origen = "desconocido"

    if item and isinstance(item, dict) and not isinstance(item, list):
        fuente_norm = _texto_medicamento(item.get("fuente")) or fuente_norm
        codigo = _texto_medicamento(
            item.get("codigo_medicamento") or item.get("cod_medicamento") or item.get("codigo")
        )
        medicamento = _sanitizar_placeholder(item.get("medicamento") or item.get("nombre") or item.get("detalle"))
        dosis = _sanitizar_placeholder(item.get("dosis"))
        cantidad = _sanitizar_placeholder(item.get("cantidad") or item.get("cant"))
        texto_original = _sanitizar_placeholder(item.get("texto_original"))
        codigo_estado = _texto_medicamento(item.get("codigo_estado"))
        codigo_tipo = _texto_medicamento(item.get("codigo_tipo")) or "desconocido"
        codigo_origen = _texto_medicamento(item.get("codigo_origen")) or "desconocido"
        tipo_item = _texto_medicamento(item.get("tipo_item")) or "desconocido"
        tipo_item_origen = _texto_medicamento(item.get("tipo_item_origen")) or "desconocido"

        if _texto_medicamento(item.get("codigo_medicamento")) or _texto_medicamento(item.get("cod_medicamento")):
            codigo_tipo = "medicamento"
            codigo_origen = "codigo_medicamento"
        elif _texto_medicamento(item.get("codigo")):
            codigo_tipo = "facturacion" if fuente_norm == "factura" else "medicamento"
            codigo_origen = "codigo"

        if not texto_original:
            partes = [codigo, cantidad, medicamento, dosis]
            texto_original = " - ".join([parte for parte in partes if parte])
    else:
        texto_original = _sanitizar_placeholder(re.sub(r"<.*?>", "", _texto_medicamento(item)))
        partes = [parte.strip() for parte in re.split(r"\s*-\s*", texto_original) if parte.strip()]
        tiene_codigo = bool(partes and _MEDICAMENTO_CODE_PATTERN.match(partes[0]))

        if len(partes) >= 4 and tiene_codigo:
            codigo = partes[0]
            cantidad = partes[1]
            medicamento = partes[2]
            dosis = " - ".join(partes[3:])
        elif len(partes) >= 3 and tiene_codigo:
            codigo = partes[0]
            medicamento = partes[1]
            dosis = partes[2]
            cantidad = partes[3] if len(partes) > 3 else ""
        elif len(partes) >= 3:
            cantidad = partes[0]
            medicamento = partes[1]
            dosis = " - ".join(partes[2:])
        elif len(partes) == 2:
            if _MEDICAMENTO_CODE_PATTERN.match(partes[0]):
                codigo = partes[0]
                medicamento = partes[1]
            else:
                medicamento = partes[0]
                dosis = partes[1]
        elif len(partes) == 1:
            medicamento, dosis = _separar_nombre_y_dosis_en_texto(partes[0])

        if codigo:
            codigo_tipo = "medicamento" if fuente_norm in {"historia_clinica", "manual", "cache_pdf"} else "desconocido"
            codigo_origen = "texto"

    codigo_estado = codigo_estado or _resolver_estado_codigo_medicamento(codigo, codigo_tipo, fuente_norm)
    tipo_item, tipo_item_origen = (
        (tipo_item, tipo_item_origen)
        if tipo_item and tipo_item != "desconocido"
        else _clasificar_tipo_item_medicamento(medicamento or texto_original, fuente_norm)
    )
    if codigo_estado != "identificado":
        codigo = codigo if codigo_estado == "pendiente_validacion" else ""

    return {
        "codigo": codigo,
        "codigo_estado": codigo_estado,
        "codigo_tipo": codigo_tipo,
        "codigo_origen": codigo_origen,
        "medicamento": medicamento or "No especificado",
        "dosis": dosis or "",
        "cantidad": cantidad or "",
        "fuente": fuente_norm,
        "texto_original": texto_original or medicamento or "No especificado",
        "tipo_item": tipo_item,
        "tipo_item_origen": tipo_item_origen,
    }


def normalizar_lista_medicamentos(items, fuente: str = "historia_clinica") -> List[Dict[str, str]]:
    if not isinstance(items, list):
        return []
    return [normalizar_medicamento_canonico(item, fuente=fuente) for item in items if item]


def formatear_medicamento_canonico(medicamento: Dict[str, str]) -> str:
    tipo_item = _texto_medicamento(medicamento.get("tipo_item"))
    nombre = _texto_medicamento(medicamento.get("medicamento"))
    cantidad = _sanitizar_placeholder(medicamento.get("cantidad"))
    dosis = _sanitizar_placeholder(medicamento.get("dosis"))
    if tipo_item == "insumo_material":
        principal = f"INSUMO / MATERIAL - {nombre}" if nombre else "INSUMO / MATERIAL"
    elif tipo_item == "equipo_dispositivo":
        principal = f"EQUIPO / DISPOSITIVO - {nombre}" if nombre else "EQUIPO / DISPOSITIVO"
    elif tipo_item == "solucion":
        codigo_visible = (
            medicamento.get("codigo")
            if medicamento.get("codigo_estado") == "identificado" and medicamento.get("codigo")
            else "Código no identificado"
        )
        principal = f"{codigo_visible} - {nombre}" if nombre else codigo_visible
    else:
        codigo_visible = (
            medicamento.get("codigo")
            if medicamento.get("codigo_estado") == "identificado" and medicamento.get("codigo")
            else "Código no identificado"
        )
        principal = f"{codigo_visible} - {nombre}" if nombre else codigo_visible
    return " - ".join([parte for parte in [principal, cantidad, dosis] if parte])


def _generar_con_llm(
    prompt: str,
    client_groq: Groq,
    client_gemini=None,
    system_msg: str = "",
    force_provider: str | None = None,
) -> str:
    provider = (force_provider or config.LLM_PROVIDER or "groq").lower()
    if provider == "gemini":
        if client_gemini is None:
            return "<p><b>Error:</b> GEMINI_API_KEY no configurada.</p>"
        full_prompt = f"{system_msg}\n\n{prompt}".strip()
        response = client_gemini.generate_content(full_prompt)
        return getattr(response, "text", "") or ""

    resp = client_groq.chat.completions.create(
        model=config.GROQ_MODEL_CLINICAL,
        temperature=0.0,
        messages=[
            {"role": "system", "content": system_msg},
            {"role": "user", "content": prompt},
        ],
    )
    return resp.choices[0].message.content


def extraer_nombre_paciente(analisis_html: str) -> str:
    """Extrae el nombre del paciente del HTML de análisis"""
    print("🔍 Analizando HTML para extraer nombre del paciente...")
    print(f"📄 Primeros 500 caracteres del HTML: {analisis_html[:500]}")
    nombre = extract_patient_name_from_html(analisis_html)
    if not nombre:
        print("❌ No se pudo extraer el nombre del paciente")
        return "desconocido"

    print(f"✅ Nombre extraído: '{nombre}'")
    return nombre


def procesar_laboratorio(texto: str, client_groq: Groq, client_gemini=None) -> str:
    """Procesa un documento de laboratorio y extrae información relevante"""
    prompt = f"""
Eres un asistente médico especializado en análisis de laboratorio. Se te proporciona un reporte de laboratorio.

Tu tarea es extraer y estructurar:
1. **Nombre del paciente** (si está disponible)
2. **Fecha del examen**
3. **Tipo de examen**
4. **Resultados principales** con valores y rangos de referencia
5. **Valores alterados** (fuera de rango normal)
6. **Observaciones o comentarios del laboratorio**

Formato de salida en HTML (IMPORTANTE: incluir siempre el nombre del paciente al inicio):
<p><b>Nombre del paciente</b></p>
<p>[Extraer nombre del paciente del documento]</p>
<p><b>Fecha del examen</b></p>
<p>...</p>
<p><b>Tipo de examen</b></p>
<p>...</p>
<p><b>Resultados principales</b></p>
<table border="1">
<tr><th>Parámetro</th><th>Resultado</th><th>Rango normal</th><th>Estado</th></tr>
<tr><td>...</td><td>...</td><td>...</td><td>Normal/Alterado</td></tr>
</table>
<p><b>Valores alterados</b></p>
<ul><li>...</li></ul>
<p><b>Observaciones</b></p>
<p>...</p>

Documento de laboratorio:
{texto}
"""
    try:
        return _generar_con_llm(
            prompt,
            client_groq,
            client_gemini=client_gemini,
            system_msg=f"Eres un especialista en an?lisis de laboratorio m?dico. Usa HTML v?lido.",
        )
    except Exception as e:
        return f"<p><b>Error procesando laboratorio:</b> {str(e)}</p>"

def procesar_radiologia(texto: str, client_groq: Groq, client_gemini=None) -> str:
    """Procesa un documento de radiología y extrae información relevante"""
    prompt = f"""
Eres un asistente médico especializado en reportes de radiología. Se te proporciona un reporte radiológico.

Tu tarea es extraer y estructurar:
1. **Nombre del paciente** (si está disponible)
2. **Fecha del estudio**
3. **Tipo de estudio** (Rx, TAC, RMN, etc.)
4. **Región anatómica estudiada**
5. **Técnica utilizada**
6. **Hallazgos principales**
7. **Conclusión/Impresión diagnóstica**

Formato de salida en HTML (IMPORTANTE: incluir siempre el nombre del paciente al inicio):
<p><b>Nombre del paciente</b></p>
<p>[Extraer nombre del paciente del documento]</p>
<p><b>Fecha del estudio</b></p>
<p>...</p>
<p><b>Tipo de estudio</b></p>
<p>...</p>
<p><b>Región anatómica</b></p>
<p>...</p>
<p><b>Técnica</b></p>
<p>...</p>
<p><b>Hallazgos</b></p>
<p>...</p>
<p><b>Conclusión</b></p>
<p>...</p>

Reporte radiológico:
{texto}
"""
    try:
        return _generar_con_llm(
            prompt,
            client_groq,
            client_gemini=client_gemini,
            system_msg=f"Eres un especialista en radiolog?a m?dica. Usa HTML v?lido.",
        )
    except Exception as e:
        return f"<p><b>Error procesando radiología:</b> {str(e)}</p>"

def procesar_prescripcion(texto: str, client_groq: Groq, client_gemini=None) -> str:
    """Procesa una prescripción médica y extrae información relevante"""
    prompt = f"""
Eres un asistente médico especializado en prescripciones. Se te proporciona una prescripción médica.

Tu tarea es extraer y estructurar:
1. **Nombre del paciente** (si está disponible)
2. **Fecha de prescripción**
3. **Médico prescriptor**
4. **Medicamentos prescritos** con dosis, frecuencia y duración
5. **Indicaciones especiales**
6. **Recomendaciones**

Formato de salida en HTML (IMPORTANTE: incluir siempre el nombre del paciente al inicio):
<p><b>Nombre del paciente</b></p>
<p>[Extraer nombre del paciente del documento]</p>
<p><b>Fecha de prescripción</b></p>
<p>...</p>
<p><b>Médico prescriptor</b></p>
<p>...</p>
<p><b>Medicamentos prescritos</b></p>
<table border="1">
<tr><th>Medicamento</th><th>Dosis</th><th>Frecuencia</th><th>Duración</th></tr>
<tr><td>...</td><td>...</td><td>...</td><td>...</td></tr>
</table>
<p><b>Indicaciones especiales</b></p>
<p>...</p>
<p><b>Recomendaciones</b></p>
<p>...</p>

Prescripción médica:
{texto}
"""
    try:
        return _generar_con_llm(
            prompt,
            client_groq,
            client_gemini=client_gemini,
            system_msg=f"Eres un especialista en prescripciones m?dicas. Usa HTML v?lido.",
        )
    except Exception as e:
        return f"<p><b>Error procesando prescripción:</b> {str(e)}</p>"

def procesar_documento_quirurgico(texto: str, client_groq: Groq, client_gemini=None) -> str:
    """Procesa un documento médico quirúrgico y extrae información estructurada completa"""
    prompt = f"""
Eres un asistente médico especializado en documentos quirúrgicos. Analiza el documento médico quirúrgico proporcionado y extrae la información de manera estructurada y precisa.

IMPORTANTE: Organiza los datos siguiendo exactamente esta estructura HTML:

<p><b>Nombre del paciente</b></p>
<p>[Extraer nombre completo del paciente del documento]</p>

<p><b>1. INFORMACIÓN DEL PACIENTE</b></p>
<ul>
<li><b>Tipo y número de documento:</b> [dato]</li>
<li><b>Sexo:</b> [dato]</li>
<li><b>Fecha de nacimiento:</b> [dato]</li>
<li><b>Edad:</b> [dato]</li>
<li><b>Número de caso:</b> [dato]</li>
<li><b>Número de admisión:</b> [dato]</li>
</ul>

<p><b>2. INFORMACIÓN QUIRÚRGICA</b></p>
<ul>
<li><b>Fecha y hora de inicio:</b> [dato]</li>
<li><b>Fecha y hora de finalización:</b> [dato]</li>
<li><b>Duración total:</b> [dato]</li>
</ul>

<p><b>3. DIAGNÓSTICOS</b></p>
<p><b>Prequirúrgicos:</b></p>
<ol>[Lista de diagnósticos previos]</ol>
<p><b>Postquirúrgicos:</b></p>
<ol>[Lista de diagnósticos posteriores]</ol>

<p><b>4. PROCEDIMIENTOS REALIZADOS</b></p>
<ol>[Enumerar cada procedimiento quirúrgico con descripción completa]</ol>

<p><b>5. HALLAZGOS QUIRÚRGICOS</b></p>
<p>[Describir hallazgos anatómicos y patológicos encontrados]</p>

<p><b>6. DESCRIPCIÓN DEL PROCEDIMIENTO</b></p>
<p>[Resumir paso a paso el procedimiento quirúrgico]</p>

<p><b>7. JUSTIFICACIÓN MÉDICA</b></p>
<p>[Razones médicas para realizar el procedimiento]</p>

<p><b>8. EQUIPO MÉDICO</b></p>
<ul>
<li><b>Cirujano Principal:</b> [Nombre, Especialidad, ID]</li>
<li><b>Anestesiólogo:</b> [Nombre, ID]</li>
<li><b>Ayudantes:</b> [Nombres e IDs]</li>
</ul>

<p><b>9. INFORMACIÓN ANESTÉSICA</b></p>
<ul>
<li><b>Tipo de anestesia:</b> [dato]</li>
<li><b>Medicación preanestésica:</b> [dato]</li>
<li><b>Riesgos anestésicos:</b> [dato]</li>
<li><b>Complicaciones anestésicas:</b> [dato]</li>
</ul>

<p><b>10. MATERIALES Y DISPOSITIVOS</b></p>
<ul>[Listar dispositivos médicos, instrumental y materiales]</ul>

<p><b>11. MEDICAMENTOS</b></p>
<ul>[Enumerar medicamentos del procedimiento]</ul>

<p><b>12. PLAN POSTOPERATORIO</b></p>
<ul>
<li><b>Estado de salida:</b> [dato]</li>
<li><b>Conducta a seguir:</b> [dato]</li>
<li><b>Órdenes médicas:</b> [dato]</li>
<li><b>Seguimiento requerido:</b> [dato]</li>
</ul>

<p><b>13. COMPLICACIONES</b></p>
<p>[Describir complicaciones durante o después del procedimiento]</p>

CRITERIOS: Transcribe exactamente los términos médicos. Si algo es ilegible marca como [ILEGIBLE]. Para manuscritos difíciles marca como [MANUSCRITO - VERIFICAR].

Documento quirúrgico:
{texto}
"""
    try:
        return _generar_con_llm(
            prompt,
            client_groq,
            client_gemini=client_gemini,
            system_msg=f"Eres un especialista en documentos m?dicos quir?rgicos. Extrae informaci?n de manera precisa y estructurada usando HTML v?lido.",
        )
    except Exception as e:
        return f"<p><b>Error procesando documento quirúrgico:</b> {str(e)}</p>"

def procesar_documento_generico(
    texto: str,
    tipo_documento: str,
    client_groq: Groq,
    client_gemini=None,
    force_provider: str | None = None,
) -> str:
    """Procesa cualquier tipo de documento médico de forma genérica"""
    prompt = f"""
Eres un asistente médico. Se te proporciona un documento médico de tipo "{tipo_documento}".

Tu tarea es extraer y estructurar la información más relevante:
1. **Nombre del paciente** (si está disponible)
2. **Fecha del documento**
3. **Información principal del documento**
4. **Datos relevantes específicos del tipo {tipo_documento}**
5. **Conclusiones o recomendaciones**

Formato de salida en HTML (IMPORTANTE: incluir siempre el nombre del paciente al inicio):
<p><b>Nombre del paciente</b></p>
<p>[Extraer nombre del paciente del documento]</p>
<p><b>Fecha del documento</b></p>
<p>...</p>
<p><b>Tipo de documento</b></p>
<p>{tipo_documento}</p>
<p><b>Información principal</b></p>
<p>...</p>
<p><b>Datos específicos</b></p>
<p>...</p>
<p><b>Conclusiones</b></p>
<p>...</p>

Documento médico:
{texto}
"""
    try:
        return _generar_con_llm(
            prompt,
            client_groq,
            client_gemini=client_gemini,
            system_msg=f"Eres un especialista en documentos m?dicos tipo {tipo_documento}. Usa HTML v?lido.",
            force_provider=force_provider,
        )
    except Exception as e:
        return f"<p><b>Error procesando documento {tipo_documento}:</b> {str(e)}</p>"

def extraer_procedimientos_quirurgicos(analisis_html: str):
    """
    Extrae los procedimientos quirúrgicos del HTML analizado.
    Busca específicamente la sección 'PROCEDIMIENTOS REALIZADOS'.
    """
    # Buscar la sección de PROCEDIMIENTOS REALIZADOS
    proc_match = re.search(
        r"<p><b>4\.\s*PROCEDIMIENTOS REALIZADOS</b>.*?<ol>(.*?)</ol>",
        analisis_html,
        flags=re.DOTALL | re.IGNORECASE
    )
    
    if not proc_match:
        # Intentar con formato alternativo
        proc_match = re.search(
            r"<p><b>PROCEDIMIENTOS REALIZADOS</b>.*?<ol>(.*?)</ol>",
            analisis_html,
            flags=re.DOTALL | re.IGNORECASE
        )
    
    if not proc_match:
        return []
    
    proc_html = proc_match.group(1)
    
    # Extraer cada <li>...</li>
    procedimientos = re.findall(r"<li>(.*?)</li>", proc_html, flags=re.DOTALL)
    procedimientos = [p.strip() for p in procedimientos if p.strip()]
    
    return procedimientos

def extraer_diagnosticos_quirurgicos(analisis_html: str):
    """
    Extrae los diagnósticos del documento quirúrgico (Prequirúrgicos y Postquirúrgicos).
    Busca en la sección '3. DIAGNÓSTICOS'.
    """
    diagnosticos = []
    
    # Buscar sección de DIAGNÓSTICOS con formato numerado
    diag_match = re.search(
        r"<p><b>3\.\s*DIAGNÓSTICOS</b>(.*?)(?=<p><b>4\.|<p><b>PROCEDIMIENTOS|$)",
        analisis_html,
        flags=re.DOTALL | re.IGNORECASE
    )
    
    if not diag_match:
        # Intentar con formato alternativo sin número
        diag_match = re.search(
            r"<p><b>DIAGNÓSTICOS</b>(.*?)(?=<p><b>PROCEDIMIENTOS|$)",
            analisis_html,
            flags=re.DOTALL | re.IGNORECASE
        )
    
    if not diag_match:
        return []
    
    diag_section = diag_match.group(1)
    
    # Extraer diagnósticos de listas <ol>
    listas = re.findall(r"<ol>(.*?)</ol>", diag_section, flags=re.DOTALL)
    for lista in listas:
        items = re.findall(r"<li>(.*?)</li>", lista, flags=re.DOTALL)
        for item in items:
            # Limpiar el HTML del diagnóstico
            item_limpio = re.sub(r"<.*?>", "", item).strip()
            if item_limpio:
                diagnosticos.append(item_limpio)
    
    # Si no hay listas, buscar diagnósticos en formato de párrafos
    if not diagnosticos:
        # Buscar líneas que parezcan diagnósticos (mayúsculas, con o sin código)
        lineas = diag_section.split('\n')
        for linea in lineas:
            linea_limpia = re.sub(r"<.*?>", "", linea).strip()
            # Filtrar líneas que parecen diagnósticos (mayúsculas, más de 10 caracteres)
            if linea_limpia and len(linea_limpia) > 10 and linea_limpia.upper() == linea_limpia:
                # Evitar headers como "Prequirúrgicos:" o "Postquirúrgicos:"
                if not re.match(r"^(PRE|POST|QUIRÚRGICO|DIAGNÓSTICO)", linea_limpia):
                    diagnosticos.append(linea_limpia)
    
    return diagnosticos

def procesar_factura(texto: str, client_groq: Groq, client_gemini=None) -> str:
    """Procesa una factura médica y extrae información estructurada en HTML usando el prompt dado."""
    prompt = f"""
Eres un especialista en facturas médicas. DEVUELVE EXCLUSIVAMENTE HTML VÁLIDO con esta estructura y en este orden (no agregues estilos inline ni encabezados H1/H2):

<p><b>Nombre del paciente</b></p>
<p></p>

<p><b>1. INFORMACIÓN DEL PROVEEDOR</b></p>
<table>
<thead><tr><th>Campo</th><th>Valor</th></tr></thead>
<tbody>
<tr><td>Nombre de la institución médica</td><td></td></tr>
<tr><td>NIT</td><td></td></tr>
<tr><td>Dirección</td><td></td></tr>
<tr><td>Ciudad y departamento</td><td></td></tr>
</tbody>
</table>

<p><b>2. INFORMACIÓN DEL LA FACTURA</b></p>
<table>
<thead><tr><th>Campo</th><th>Valor</th></tr></thead>
<tbody>
<tr><td>Número de factura</td><td></td></tr>
<tr><td>Prefijo</td><td></td></tr>
<tr><td>Fecha de emisión</td><td></td></tr>
<tr><td>Número de caso</td><td></td></tr>
<tr><td>Fecha de vencimiento</td><td></td></tr>
</tbody>
</table>

<p><b>3. INFORMACIÓN DEL PAGADOR</b></p>
<table>
<thead><tr><th>Campo</th><th>Valor</th></tr></thead>
<tbody>
<tr><td>Aseguradora/EPS</td><td></td></tr>
<tr><td>NIT del pagador</td><td></td></tr>
<tr><td>Tipo de convenio</td><td></td></tr>
<tr><td>Número de autorización</td><td></td></tr>
</tbody>
</table>

<p><b>4. INFORMACIÓN DEL PACIENTE</b></p>
<table>
<thead><tr><th>Campo</th><th>Valor</th></tr></thead>
<tbody>
<tr><td>Nombre completo</td><td></td></tr>
<tr><td>Número de identificación (CC)</td><td></td></tr>
<tr><td>Fecha de ingreso</td><td></td></tr>
<tr><td>Fecha de egreso</td><td></td></tr>
<tr><td>Estancia hospitalaria</td><td></td></tr>
</tbody>
</table>

<p><b>5. SERVICIOS Y PROCEDIMIENTOS MÉDICOS</b></p>
<p><b>Procedimientos quirúrgicos</b></p>
<table>
<thead><tr><th>Concepto</th><th>Código CUPS</th><th>Descripción</th><th>Cantidad</th><th>Valor unitario</th><th>Total</th></tr></thead>
<tbody>
<tr><td>Procedimiento</td><td></td><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
<p><b>Exámenes de laboratorio</b></p>
<table>
<thead><tr><th>Prueba</th><th>Descripción</th><th>Cantidad</th><th>Valor unitario</th><th>Total</th></tr></thead>
<tbody>
<tr><td>Prueba</td><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
<p><b>Imagenología</b></p>
<table>
<thead><tr><th>Estudio</th><th>Descripción</th><th>Cantidad</th><th>Valor unitario</th><th>Total</th></tr></thead>
<tbody>
<tr><td>Estudio</td><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
<p><b>Hospitalización</b></p>
<table>
<thead><tr><th>Habitación</th><th>Días</th><th>Tarifa</th><th>Total</th></tr></thead>
<tbody>
<tr><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
<p><b>Honorarios médicos</b></p>
<table>
<thead><tr><th>Rol</th><th>Profesional</th><th>Cantidad</th><th>Valor unitario</th><th>Total</th></tr></thead>
<tbody>
<tr><td>Cirujano</td><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
<p><b>Medicamentos</b></p>
<table>
<thead><tr><th>Medicamento</th><th>Dosis</th><th>Cantidad</th><th>Valor unitario</th><th>Total</th></tr></thead>
<tbody>
<tr><td></td><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>

<p><b>6. ANÁLISIS FINANCIERO</b></p>
<table>
<thead><tr><th>Concepto</th><th>Valor</th></tr></thead>
<tbody>
<tr><td>Total de servicios</td><td></td></tr>
<tr><td>Descuentos</td><td></td></tr>
<tr><td>Copagos</td><td></td></tr>
<tr><td>Valor total de la factura</td><td></td></tr>
<tr><td>Valor en letras</td><td></td></tr>
</tbody>
</table>

<p><b>7. OBSERVACIONES IMPORTANTES</b></p>
<ul><li></li></ul>

Usa solo etiquetas p, ul, ol, table, thead, tbody, tr, th, td. No styles inline, no markdown. Si un dato no está en el documento, deja la celda o el contenido vacío; no uses placeholders entre corchetes.

Factura médica original:
{texto}
"""
    try:
        return _generar_con_llm(
            prompt,
            client_groq,
            client_gemini=client_gemini,
            system_msg=f"Eres un especialista en facturas m?dicas. Responde solo HTML v?lido.",
        )
    except Exception as e:
        return f"<p><b>Error procesando factura:</b> {str(e)}</p>"

def extraer_factura_json(analisis_html: str) -> Dict:
    """
    Extrae los datos de la factura del HTML y los estructura en formato JSON.
    Retorna un diccionario con todas las secciones de la factura.
    """
    resultado = {
        "nombre_paciente": "",
        "proveedor": {},
        "informacion_factura": {},
        "pagador": {},
        "informacion_paciente": {},
        "servicios_procedimientos": {
            "procedimientos_quirurgicos": [],
            "examenes_laboratorio": [],
            "imagenologia": [],
            "hospitalizacion": [],
            "honorarios_medicos": [],
            "medicamentos": []
        },
        "analisis_financiero": {},
        "observaciones": []
    }
    
    if not analisis_html:
        return resultado
    
    # Helper para extraer valor de tabla HTML
    def extraer_valor_tabla(html_texto: str, campo_buscado: str) -> str:
        """Extrae un valor específico de una tabla HTML"""
        patron = rf'<tr>\s*<td>{re.escape(campo_buscado)}</td>\s*<td>(.*?)</td>'
        match = re.search(patron, html_texto, re.DOTALL | re.IGNORECASE)
        if match:
            valor = _sanitizar_placeholder(re.sub(r'<.*?>', '', match.group(1)).strip())
            return valor
        return ""
    
    # Helper para extraer tabla completa
    def extraer_tabla(html_texto: str, seccion: str) -> List[Dict]:
        """Extrae una tabla y la convierte a lista de diccionarios"""
        # Buscar la sección y tabla siguiente
        patron = rf'<p><b>{re.escape(seccion)}</b>.*?<table>.*?<tbody>(.*?)</tbody>'
        match = re.search(patron, html_texto, re.DOTALL | re.IGNORECASE)
        if not match:
            return []
        
        tabla_html = match.group(1)
        filas = re.findall(r'<tr>(.*?)</tr>', tabla_html, re.DOTALL)
        resultados = []
        
        for fila in filas:
            celdas = re.findall(r'<td>(.*?)</td>', fila, re.DOTALL)
            if len(celdas) >= 2:
                # Limpiar HTML de las celdas
                celdas_limpias = [_sanitizar_placeholder(re.sub(r'<.*?>', '', c).strip()) for c in celdas]
                if not any(celdas_limpias):
                    continue
                
                # Crear diccionario según el tipo de tabla
                if seccion == "Procedimientos quirúrgicos":
                    if len(celdas_limpias) >= 6:
                        if not any(celdas_limpias[1:]):
                            continue
                        resultados.append({
                            "concepto": celdas_limpias[0],
                            "codigo_cups": celdas_limpias[1],
                            "descripcion": celdas_limpias[2],
                            "cantidad": celdas_limpias[3],
                            "valor_unitario": celdas_limpias[4],
                            "total": celdas_limpias[5]
                        })
                elif seccion == "Exámenes de laboratorio":
                    if len(celdas_limpias) >= 5:
                        if not any(celdas_limpias[1:]):
                            continue
                        resultados.append({
                            "prueba": celdas_limpias[0],
                            "descripcion": celdas_limpias[1],
                            "cantidad": celdas_limpias[2],
                            "valor_unitario": celdas_limpias[3],
                            "total": celdas_limpias[4]
                        })
                elif seccion == "Imagenología" or seccion == "Imagenologia":
                    if len(celdas_limpias) >= 5:
                        if not any(celdas_limpias[1:]):
                            continue
                        resultados.append({
                            "estudio": celdas_limpias[0],
                            "descripcion": celdas_limpias[1],
                            "cantidad": celdas_limpias[2],
                            "valor_unitario": celdas_limpias[3],
                            "total": celdas_limpias[4]
                        })
                elif seccion == "Hospitalización" or seccion == "Hospitalizacion":
                    if len(celdas_limpias) >= 4:
                        if not any(celdas_limpias):
                            continue
                        resultados.append({
                            "habitacion": celdas_limpias[0],
                            "dias": celdas_limpias[1],
                            "tarifa": celdas_limpias[2],
                            "total": celdas_limpias[3]
                        })
                elif seccion == "Honorarios médicos":
                    if len(celdas_limpias) >= 5:
                        if not any(celdas_limpias[1:]):
                            continue
                        resultados.append({
                            "rol": celdas_limpias[0],
                            "profesional": celdas_limpias[1],
                            "cantidad": celdas_limpias[2],
                            "valor_unitario": celdas_limpias[3],
                            "total": celdas_limpias[4]
                        })
                elif seccion == "Medicamentos":
                    if len(celdas_limpias) >= 5:
                        if not any(celdas_limpias):
                            continue
                        resultados.append({
                            "medicamento": celdas_limpias[0],
                            "dosis": celdas_limpias[1],
                            "cantidad": celdas_limpias[2],
                            "valor_unitario": celdas_limpias[3],
                            "total": celdas_limpias[4]
                        })
        
        return resultados
    
    # Extraer nombre del paciente
    nombre_match = re.search(
        r'<p><b>Nombre del paciente</b>\s*</p>\s*<p>([^<]+)</p>',
        analisis_html,
        re.IGNORECASE | re.DOTALL
    )
    if nombre_match:
        resultado["nombre_paciente"] = nombre_match.group(1).strip()
    
    # Extraer información del proveedor
    seccion_proveedor = re.search(
        r'<p><b>1\.\s*INFORMACIÓN DEL PROVEEDOR</b>.*?<table>(.*?)</table>',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_proveedor:
        tabla_html = seccion_proveedor.group(1)
        resultado["proveedor"] = {
            "nombre_institucion": extraer_valor_tabla(tabla_html, "Nombre de la institución médica"),
            "nit": extraer_valor_tabla(tabla_html, "NIT"),
            "direccion": extraer_valor_tabla(tabla_html, "Dirección"),
            "ciudad_departamento": extraer_valor_tabla(tabla_html, "Ciudad y departamento")
        }
    
    # Extraer información de la factura
    seccion_factura = re.search(
        r'<p><b>2\.\s*INFORMACIÓN DEL LA FACTURA</b>.*?<table>(.*?)</table>',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_factura:
        tabla_html = seccion_factura.group(1)
        resultado["informacion_factura"] = {
            "numero_factura": extraer_valor_tabla(tabla_html, "Número de factura"),
            "prefijo": extraer_valor_tabla(tabla_html, "Prefijo"),
            "fecha_emision": extraer_valor_tabla(tabla_html, "Fecha de emisión"),
            "numero_caso": extraer_valor_tabla(tabla_html, "Número de caso"),
            "fecha_vencimiento": extraer_valor_tabla(tabla_html, "Fecha de vencimiento")
        }
    
    # Extraer información del pagador
    seccion_pagador = re.search(
        r'<p><b>3\.\s*INFORMACIÓN DEL PAGADOR</b>.*?<table>(.*?)</table>',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_pagador:
        tabla_html = seccion_pagador.group(1)
        resultado["pagador"] = {
            "aseguradora_eps": extraer_valor_tabla(tabla_html, "Aseguradora/EPS"),
            "nit_pagador": extraer_valor_tabla(tabla_html, "NIT del pagador"),
            "tipo_convenio": extraer_valor_tabla(tabla_html, "Tipo de convenio"),
            "numero_autorizacion": extraer_valor_tabla(tabla_html, "Número de autorización")
        }
    
    # Extraer información del paciente
    seccion_paciente = re.search(
        r'<p><b>4\.\s*INFORMACIÓN DEL PACIENTE</b>.*?<table>(.*?)</table>',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_paciente:
        tabla_html = seccion_paciente.group(1)
        resultado["informacion_paciente"] = {
            "nombre_completo": extraer_valor_tabla(tabla_html, "Nombre completo"),
            "numero_identificacion": extraer_valor_tabla(tabla_html, "Número de identificación (CC)"),
            "fecha_ingreso": extraer_valor_tabla(tabla_html, "Fecha de ingreso"),
            "fecha_egreso": extraer_valor_tabla(tabla_html, "Fecha de egreso"),
            "estancia_hospitalaria": extraer_valor_tabla(tabla_html, "Estancia hospitalaria")
        }
    
    # Extraer servicios y procedimientos
    seccion_servicios = re.search(
        r'<p><b>5\.\s*SERVICIOS Y PROCEDIMIENTOS MÉDICOS</b>(.*?)(?=<p><b>6\.|$)',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_servicios:
        servicios_html = seccion_servicios.group(1)
        resultado["servicios_procedimientos"]["procedimientos_quirurgicos"] = extraer_tabla(servicios_html, "Procedimientos quirúrgicos")
        resultado["servicios_procedimientos"]["examenes_laboratorio"] = extraer_tabla(servicios_html, "Exámenes de laboratorio")
        resultado["servicios_procedimientos"]["imagenologia"] = extraer_tabla(servicios_html, "Imagenología") or extraer_tabla(servicios_html, "Imagenologia")
        resultado["servicios_procedimientos"]["hospitalizacion"] = extraer_tabla(servicios_html, "Hospitalización") or extraer_tabla(servicios_html, "Hospitalizacion")
        resultado["servicios_procedimientos"]["honorarios_medicos"] = extraer_tabla(servicios_html, "Honorarios médicos")
        resultado["servicios_procedimientos"]["medicamentos"] = extraer_tabla(servicios_html, "Medicamentos")
    
    # Extraer análisis financiero
    seccion_financiero = re.search(
        r'<p><b>6\.\s*ANÁLISIS FINANCIERO</b>.*?<table>(.*?)</table>',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_financiero:
        tabla_html = seccion_financiero.group(1)
        resultado["analisis_financiero"] = {
            "total_servicios": extraer_valor_tabla(tabla_html, "Total de servicios"),
            "descuentos": extraer_valor_tabla(tabla_html, "Descuentos"),
            "copagos": extraer_valor_tabla(tabla_html, "Copagos"),
            "valor_total_factura": extraer_valor_tabla(tabla_html, "Valor total de la factura"),
            "valor_en_letras": extraer_valor_tabla(tabla_html, "Valor en letras")
        }
    
    # Extraer observaciones
    seccion_observaciones = re.search(
        r'<p><b>7\.\s*OBSERVACIONES IMPORTANTES</b>.*?<ul>(.*?)</ul>',
        analisis_html,
        re.DOTALL | re.IGNORECASE
    )
    if seccion_observaciones:
        lista_html = seccion_observaciones.group(1)
        items = re.findall(r'<li>(.*?)</li>', lista_html, re.DOTALL)
        resultado["observaciones"] = [re.sub(r'<.*?>', '', item).strip() for item in items if item.strip()]
    
    return resultado

def extraer_procedimientos_para_soat(analisis_html: str):
    # Reusar extraer_procedimientos_quirurgicos ya extrae <li>...
    procedimientos = extraer_procedimientos_quirurgicos(analisis_html)
    # Si cada item incluye código CUPS al inicio, removerlo dejando descripción limpia
    limpias = []
    for p in procedimientos:
        # Remueve prefijo tipo "123456 Descripción" si existe
        m = re.match(r"^\d{6}\s+(.+)$", p)
        limpias.append(m.group(1).strip() if m else p)
    return limpias

def extraer_metadatos_historia(analisis_html: str) -> Dict:
    """
    Extrae metadatos estructurados de la historia clínica.
    Retorna un diccionario con todos los campos relevantes.
    """
    if not analisis_html:
        return {}
    
    metadatos = {}
    
    # Función auxiliar para extraer valor después de un label - MEJORADA
    def extraer_valor(patron_label: str, flags=re.DOTALL | re.IGNORECASE):
        # Patrón 1: <p><b>Label</b></p>\n<p>VALOR</p>
        patron1 = rf"<p><b>{patron_label}</b>\s*</p>\s*<p>([^<]+)</p>"
        match1 = re.search(patron1, analisis_html, flags)
        if match1:
            valor = match1.group(1).strip()
            # Filtrar valores genéricos
            if valor and valor.lower() not in ["no especificado", "n/a", "na", "---"]:
                return valor
        
        # Patrón 2: <b>Label:</b> VALOR (en la misma línea o siguiente)
        patron2 = rf"<b>{patron_label}[:\s]*</b>\s*([^<\n]+)"
        match2 = re.search(patron2, analisis_html, flags)
        if match2:
            valor = match2.group(1).strip()
            if valor and valor.lower() not in ["no especificado", "n/a", "na", "---"]:
                return valor
        
        # Patrón 3: <li><b>Label:</b> VALOR</li>
        patron3 = rf"<li><b>{patron_label}[:\s]*</b>\s*([^<]+)</li>"
        match3 = re.search(patron3, analisis_html, flags)
        if match3:
            valor = match3.group(1).strip()
            if valor and valor.lower() not in ["no especificado", "n/a", "na", "---"]:
                return valor
        
        # Patrón 4: <p>Label: VALOR</p> (sin bold)
        patron4 = rf"<p>{patron_label}[:\s]+([^<]+)</p>"
        match4 = re.search(patron4, analisis_html, flags)
        if match4:
            valor = match4.group(1).strip()
            if valor and valor.lower() not in ["no especificado", "n/a", "na", "---"]:
                return valor
        
        return None
    
    # Extraer nombre del paciente
    nombre = extraer_valor("Nombre del paciente")
    if nombre:
        metadatos["nombre_paciente"] = nombre
    
    # Extraer prestador de servicio (múltiples variantes)
    prestador = (extraer_valor("Prestador de servicio") or 
                 extraer_valor("Prestador") or
                 extraer_valor("Institución médica") or
                 extraer_valor("Institución") or 
                 extraer_valor("Centro médico"))
    if prestador:
        metadatos["prestador_servicio"] = prestador
    
    # Extraer número de caso (múltiples variantes)
    caso = (extraer_valor("Número de caso") or 
            extraer_valor("No\\. de Caso") or 
            extraer_valor("No\\. de caso") or
            extraer_valor("Caso") or
            extraer_valor("N° de caso"))
    if caso:
        metadatos["caso"] = caso
    
    # Extraer datos de identificación (múltiples variantes)
    identificacion = (extraer_valor("Datos de identificación del paciente") or
                     extraer_valor("Tipo y número de documento") or
                     extraer_valor("Documento de identidad") or
                     extraer_valor("Identificación") or
                     extraer_valor("CC") or
                     extraer_valor("Cédula"))
    if identificacion:
        metadatos["datos_identificacion_paciente"] = identificacion
    
    # Extraer sexo
    sexo = extraer_valor("Sexo") or extraer_valor("Género")
    if sexo:
        metadatos["sexo"] = sexo.upper()  # Normalizar a mayúsculas
    
    # Extraer edad
    edad = extraer_valor("Edad")
    if edad:
        metadatos["edad"] = edad
    
    # Extraer fecha de ingreso (múltiples variantes)
    fecha_ingreso = (extraer_valor("Fecha de ingreso") or 
                    extraer_valor("Fecha ingreso") or
                    extraer_valor("Fecha de admisión"))
    if fecha_ingreso:
        metadatos["fecha_ingreso"] = fecha_ingreso
    
    # Extraer fecha de nacimiento (múltiples variantes)
    fecha_nacimiento = (extraer_valor("Fecha de nacimiento") or 
                       extraer_valor("Fecha nacimiento") or
                       extraer_valor("F\\. nacimiento"))
    if fecha_nacimiento:
        metadatos["fecha_nacimiento"] = fecha_nacimiento
    
    # Extraer motivo de consulta
    motivo = (extraer_valor("Motivo de consulta") or 
             extraer_valor("Motivo consulta") or
             extraer_valor("Motivo"))
    if motivo:
        metadatos["motivo_consulta"] = motivo.upper()  # Normalizar a mayúsculas
    
    # Extraer resumen
    patron_resumen = r"<p><b>Resumen</b>\s*</p>\s*<p>(.*?)</p>\s*(?=<p><b>|$)"
    match_resumen = re.search(patron_resumen, analisis_html, re.DOTALL | re.IGNORECASE)
    if match_resumen:
        resumen = match_resumen.group(1).strip()
        if resumen and resumen.lower() != "no especificado":
            metadatos["resumen"] = resumen
    
    return metadatos

def extraer_nombre_y_resumen_historia(analisis_html: str):
    """Extrae solo el nombre del paciente y el resumen de la historia clínica."""
    if not analisis_html:
        return None, None
    
    nombre_paciente = ""
    resumen = ""
    
    # Extraer nombre del paciente
    # Patrón 1: <p><b>Nombre del paciente</b></p><p>NOMBRE</p>
    patron_nombre = r"<p><b>Nombre del paciente</b>\s*</p>\s*<p>([^<]+)</p>"
    match_nombre = re.search(patron_nombre, analisis_html, re.DOTALL | re.IGNORECASE)
    if match_nombre:
        nombre_paciente = match_nombre.group(1).strip()
    
    # Extraer resumen
    # Patrón: <p><b>Resumen</b></p> seguido del contenido hasta el próximo <p><b>
    patron_resumen = r"<p><b>Resumen</b>\s*</p>\s*<p>(.*?)</p>\s*(?=<p><b>|$)"
    match_resumen = re.search(patron_resumen, analisis_html, re.DOTALL | re.IGNORECASE)
    if match_resumen:
        resumen = match_resumen.group(1).strip()
    
    return nombre_paciente, resumen

def extraer_procedimientos_historia(analisis_html: str):
    """
    Extrae los procedimientos de la historia clínica.
    Busca la sección 'Procedimientos' y retorna lista de procedimientos.
    """
    if not analisis_html:
        return []
    
    procedimientos = []
    
    # Buscar sección de Procedimientos
    # Patrón: <p><b>Procedimientos</b></p> seguido de <ol> o <ul>
    proc_match = re.search(
        r"<p><b>Procedimientos</b>\s*</p>\s*<ol>(.*?)</ol>",
        analisis_html,
        flags=re.DOTALL | re.IGNORECASE
    )
    
    if not proc_match:
        # Intentar con <ul>
        proc_match = re.search(
            r"<p><b>Procedimientos</b>\s*</p>\s*<ul>(.*?)</ul>",
            analisis_html,
            flags=re.DOTALL | re.IGNORECASE
        )
    
    if proc_match:
        proc_html = proc_match.group(1)
        
        # Extraer cada <li>...</li>
        items = re.findall(r"<li>(.*?)</li>", proc_html, flags=re.DOTALL)
        procedimientos = [item.strip() for item in items if item.strip()]
    
    return procedimientos

def extraer_medicamentos_historia(analisis_html: str):
    """
    Extrae los medicamentos de la historia clínica y los devuelve en formato canónico.
    """
    if not analisis_html:
        return []
    
    medicamentos = []
    
    # Buscar sección de Medicamentos (múltiples variantes)
    patrones_medicamentos = [
        r"<p><b>Medicamentos\s*administrados</b>\s*</p>\s*<ol>(.*?)</ol>",
        r"<p><b>Medicamentos</b>\s*</p>\s*<ol>(.*?)</ol>",
        r"<p><b>Medicamentos\s*administrados</b>\s*</p>\s*<ul>(.*?)</ul>",
        r"<p><b>Medicamentos</b>\s*</p>\s*<ul>(.*?)</ul>",
        r"<p><b>Plan\s*farmacológico</b>\s*</p>\s*<ol>(.*?)</ol>",
        r"<p><b>Plan\s*farmacológico</b>\s*</p>\s*<ul>(.*?)</ul>",
    ]
    
    for patron in patrones_medicamentos:
        med_match = re.search(patron, analisis_html, flags=re.DOTALL | re.IGNORECASE)
        if med_match:
            med_html = med_match.group(1)
            
            # Extraer cada <li>...</li>
            items = re.findall(r"<li>(.*?)</li>", med_html, flags=re.DOTALL)
            
            for item in items:
                # Limpiar HTML tags
                item_texto = re.sub(r"<.*?>", "", item.strip())
                if item_texto:
                    medicamentos.append(
                        normalizar_medicamento_canonico(item_texto, fuente="historia_clinica")
                    )
            
            break
    
    return medicamentos

def extraer_secciones_quirurgicas(analisis_html: str):
    """Extrae las secciones 5 (Hallazgos) y 6 (Descripción) del documento quirúrgico."""
    if not analisis_html:
        return None, None
    
    hallazgos = ""
    descripcion = ""
    
    # Extraer sección 5: HALLAZGOS QUIRÚRGICOS
    patron_hallazgos = r"<p><b>5\.\s*HALLAZGOS QUIRÚRGICOS</b></p>\s*<p>(.*?)</p>"
    match_hallazgos = re.search(patron_hallazgos, analisis_html, re.DOTALL | re.IGNORECASE)
    if match_hallazgos:
        hallazgos = match_hallazgos.group(1).strip()
    else:
        # Intentar formato alternativo
        patron_alt = r"<b>HALLAZGOS QUIRÚRGICOS</b>.*?<p>(.*?)</p>"
        match_alt = re.search(patron_alt, analisis_html, re.DOTALL | re.IGNORECASE)
        if match_alt:
            hallazgos = match_alt.group(1).strip()
    
    # Extraer sección 6: DESCRIPCIÓN DEL PROCEDIMIENTO
    patron_desc = r"<p><b>6\.\s*DESCRIPCIÓN DEL PROCEDIMIENTO</b></p>\s*<p>(.*?)</p>"
    match_desc = re.search(patron_desc, analisis_html, re.DOTALL | re.IGNORECASE)
    if match_desc:
        descripcion = match_desc.group(1).strip()
    else:
        # Intentar formato alternativo
        patron_alt2 = r"<b>DESCRIPCIÓN DEL PROCEDIMIENTO</b>.*?<p>(.*?)</p>"
        match_alt2 = re.search(patron_alt2, analisis_html, re.DOTALL | re.IGNORECASE)
        if match_alt2:
            descripcion = match_alt2.group(1).strip()
    
    return hallazgos, descripcion
