from __future__ import annotations

from html import escape
from typing import Any

from app.llm.schemas import (
    ClinicalStructuredModel,
    DocumentoGenericoStructured,
    DocumentoQuirurgicoStructured,
    FacturaStructured,
    HistoriaClinicaStructured,
    LaboratorioStructured,
    PrefacturaStructured,
    PrescripcionStructured,
    RadiologiaStructured,
    get_clinical_document_model,
)


def _text(value: Any, default: str = "No especificado") -> str:
    raw = str(value or "").strip()
    return escape(raw or default)


def _list_items(values: list[str]) -> str:
    if not values:
        return "<ol><li>No especificado</li></ol>"
    return "<ol>" + "".join(f"<li>{_text(item)}</li>" for item in values) + "</ol>"


def _paragraph(label: str, value: Any) -> str:
    return f"<p><b>{escape(label)}</b></p><p>{_text(value)}</p>"


def _table(headers: list[str], rows: list[list[Any]]) -> str:
    if not rows:
        rows = [["No especificado" for _ in headers]]
    thead = "<tr>" + "".join(f"<th>{escape(header)}</th>" for header in headers) + "</tr>"
    body_rows = []
    for row in rows:
        body_rows.append("<tr>" + "".join(f"<td>{_text(cell)}</td>" for cell in row) + "</tr>")
    return (
        "<div class=\"ui-table-wrap\"><table border=\"1\"><thead>"
        + thead
        + "</thead><tbody>"
        + "".join(body_rows)
        + "</tbody></table></div>"
    )


def render_historia_html(model: HistoriaClinicaStructured) -> str:
    diagnosticos = [
        f"{item.codigo} - {item.descripcion}" if item.codigo else item.descripcion
        for item in model.diagnosticos
    ]
    procedimientos = [
        f"{item.codigo} {item.descripcion}" if item.codigo else item.descripcion
        for item in model.procedimientos
    ]
    medicamentos = []
    for item in model.medicamentos:
        parts = [item.nombre]
        if item.dosis:
            parts.append(item.dosis)
        if item.via:
            parts.append(item.via)
        if item.frecuencia:
            parts.append(item.frecuencia)
        medicamento = " - ".join(parts)
        if item.codigo:
            medicamento = f"{item.codigo} - {medicamento}"
        medicamentos.append(medicamento)
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            _paragraph("Prestador de servicio", model.prestador_servicio),
            _paragraph("Número de caso", model.numero_caso),
            _paragraph("Datos de identificación del paciente", model.identificacion_paciente),
            _paragraph("Sexo", model.sexo),
            _paragraph("Edad", model.edad),
            _paragraph("Fecha de nacimiento", model.fecha_nacimiento),
            _paragraph("Fecha de ingreso", model.fecha_ingreso),
            _paragraph("Motivo de consulta", model.motivo_consulta),
            _paragraph("Resumen", model.resumen_clinico),
            f"<p><b>Diagnósticos</b></p>{_list_items(diagnosticos)}",
            f"<p><b>Procedimientos</b></p>{_list_items(procedimientos)}",
            f"<p><b>Medicamentos administrados</b></p>{_list_items(medicamentos)}",
        ]
    )


def render_laboratorio_html(model: LaboratorioStructured) -> str:
    rows = [[item.parametro, item.resultado, item.rango_normal, item.estado] for item in model.resultados]
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            _paragraph("Fecha del examen", model.fecha_examen),
            _paragraph("Tipo de examen", model.tipo_examen),
            "<p><b>Resultados principales</b></p>",
            _table(["Parámetro", "Resultado", "Rango normal", "Estado"], rows),
            f"<p><b>Valores alterados</b></p>{_list_items(model.valores_alterados)}",
            _paragraph("Interpretación", model.interpretacion),
            _paragraph("Observaciones", model.observaciones),
        ]
    )


def render_radiologia_html(model: RadiologiaStructured) -> str:
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            _paragraph("Fecha del estudio", model.fecha_estudio),
            _paragraph("Tipo de estudio", model.tipo_estudio),
            _paragraph("Región anatómica", model.region_anatomica),
            _paragraph("Técnica", model.tecnica),
            _paragraph("Hallazgos", model.hallazgos),
            _paragraph("Conclusión", model.conclusion),
        ]
    )


def render_prescripcion_html(model: PrescripcionStructured) -> str:
    medicamentos = []
    for item in model.medicamentos:
        parts = [item.nombre]
        if item.dosis:
            parts.append(item.dosis)
        if item.via:
            parts.append(item.via)
        if item.frecuencia:
            parts.append(item.frecuencia)
        if item.cantidad:
            parts.append(item.cantidad)
        medicamentos.append(" - ".join(parts))
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            _paragraph("Fecha de prescripción", model.fecha),
            _paragraph("Diagnóstico contexto", model.diagnostico_contexto),
            f"<p><b>Medicamentos formulados</b></p>{_list_items(medicamentos)}",
            _paragraph("Observaciones", model.observaciones),
        ]
    )


def render_quirurgico_html(model: DocumentoQuirurgicoStructured) -> str:
    diagnosticos = [
        f"{item.codigo} - {item.descripcion}" if item.codigo else item.descripcion
        for item in model.diagnosticos
    ]
    procedimientos = [
        f"{item.codigo} {item.descripcion}" if item.codigo else item.descripcion
        for item in model.procedimientos
    ]
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            _paragraph("1. FECHA DEL PROCEDIMIENTO", model.fecha_procedimiento),
            f"<p><b>3. DIAGNÓSTICOS</b></p>{_list_items(diagnosticos)}",
            f"<p><b>4. PROCEDIMIENTOS REALIZADOS</b></p>{_list_items(procedimientos)}",
            _paragraph("5. HALLAZGOS QUIRÚRGICOS", model.hallazgos),
            _paragraph("6. DESCRIPCIÓN DEL PROCEDIMIENTO", model.descripcion_procedimiento or model.procedimiento_principal),
            _paragraph("Complicaciones", model.complicaciones),
            _paragraph("Conclusión", model.conclusion),
        ]
    )


def render_factura_html(model: FacturaStructured) -> str:
    servicios = model.servicios_procedimientos
    resumen = model.resumen_financiero
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            "<p><b>1. INFORMACIÓN DEL PROVEEDOR</b></p>",
            _table(
                ["Campo", "Valor"],
                [
                    ["Nombre de la institución médica", model.proveedor.nombre_institucion],
                    ["NIT", model.proveedor.nit],
                    ["Dirección", model.proveedor.direccion],
                    ["Ciudad y departamento", model.proveedor.ciudad_departamento],
                ],
            ),
            "<p><b>2. INFORMACIÓN DE LA FACTURA</b></p>",
            _table(
                ["Campo", "Valor"],
                [
                    ["Número de factura", model.datos_factura.numero_factura],
                    ["Prefijo", model.datos_factura.prefijo],
                    ["Fecha de emisión", model.datos_factura.fecha_emision],
                    ["Número de caso", model.datos_factura.numero_caso],
                    ["Fecha de vencimiento", model.datos_factura.fecha_vencimiento],
                ],
            ),
            "<p><b>3. INFORMACIÓN DEL PAGADOR</b></p>",
            _table(
                ["Campo", "Valor"],
                [
                    ["Aseguradora/EPS", model.pagador.aseguradora_eps],
                    ["NIT del pagador", model.pagador.nit_pagador],
                    ["Tipo de convenio", model.pagador.tipo_convenio],
                    ["Número de autorización", model.pagador.numero_autorizacion],
                ],
            ),
            "<p><b>4. INFORMACIÓN DEL PACIENTE</b></p>",
            _table(
                ["Campo", "Valor"],
                [
                    ["Nombre completo", model.paciente.nombre_completo or model.patient_name],
                    ["Número de identificación (CC)", model.paciente.numero_identificacion],
                    ["Fecha de ingreso", model.paciente.fecha_ingreso],
                    ["Fecha de egreso", model.paciente.fecha_egreso],
                    ["Estancia hospitalaria", model.paciente.estancia_hospitalaria],
                ],
            ),
            "<p><b>5. SERVICIOS Y PROCEDIMIENTOS MÉDICOS</b></p>",
            "<p><b>Procedimientos quirúrgicos</b></p>",
            _table(
                ["Concepto", "Código CUPS", "Descripción", "Cantidad", "Valor unitario", "Total"],
                [[item.concepto, item.codigo_cups, item.descripcion, item.cantidad, item.valor_unitario, item.total] for item in servicios.procedimientos_quirurgicos],
            ),
            "<p><b>Exámenes de laboratorio</b></p>",
            _table(
                ["Prueba", "Descripción", "Cantidad", "Valor unitario", "Total"],
                [[item.prueba, item.descripcion, item.cantidad, item.valor_unitario, item.total] for item in servicios.examenes_laboratorio],
            ),
            "<p><b>Imagenología</b></p>",
            _table(
                ["Estudio", "Descripción", "Cantidad", "Valor unitario", "Total"],
                [[item.estudio, item.descripcion, item.cantidad, item.valor_unitario, item.total] for item in servicios.imagenologia],
            ),
            "<p><b>Hospitalización</b></p>",
            _table(
                ["Habitación", "Días", "Tarifa", "Total"],
                [[item.habitacion, item.dias, item.tarifa, item.total] for item in servicios.hospitalizacion],
            ),
            "<p><b>Honorarios médicos</b></p>",
            _table(
                ["Rol", "Profesional", "Cantidad", "Valor unitario", "Total"],
                [[item.rol, item.profesional, item.cantidad, item.valor_unitario, item.total] for item in servicios.honorarios_medicos],
            ),
            "<p><b>Medicamentos</b></p>",
            _table(
                ["Medicamento", "Dosis", "Cantidad", "Valor unitario", "Total"],
                [[item.medicamento, item.dosis, item.cantidad, item.valor_unitario, item.total] for item in servicios.medicamentos],
            ),
            "<p><b>Otros servicios</b></p>",
            _table(
                ["Código facturación", "Código referencia", "Descripción", "Cantidad", "Valor unitario", "Total"],
                [
                    [
                        item.codigo_facturacion or item.concepto,
                        item.codigo_referencia or item.codigo_cups,
                        item.descripcion,
                        item.cantidad,
                        item.valor_unitario,
                        item.total,
                    ]
                    for item in servicios.otros_servicios
                ],
            ),
            "<p><b>6. ANÁLISIS FINANCIERO</b></p>",
            _table(
                ["Concepto", "Valor"],
                [
                    ["Total de servicios", resumen.total_servicios],
                    ["Descuentos", resumen.descuentos],
                    ["Copagos", resumen.copagos],
                    ["Valor total de la factura", resumen.valor_total_factura],
                    ["Valor en letras", resumen.valor_en_letras],
                ],
            ),
            _paragraph("7. OBSERVACIONES IMPORTANTES", resumen.observaciones_importantes),
        ]
    )


def render_prefactura_html(model: PrefacturaStructured) -> str:
    rows = [
        [item.codigo, item.descripcion, item.cantidad, item.valor]
        for item in model.servicios
    ]
    return "".join(
        [
            _paragraph("Nombre del paciente", model.paciente_nombre or model.patient_name),
            _paragraph("Número de caso", model.numero_caso),
            _paragraph("Prestador", model.prestador),
            _paragraph("Identificación del paciente", model.paciente_identificacion),
            "<p><b>Servicios incluidos en prefactura</b></p>",
            _table(["Código", "Descripción", "Cantidad", "Valor"], rows),
            _paragraph("Valor estimado", model.valor_estimado),
            _paragraph("Observaciones", model.observaciones),
        ]
    )


def render_generico_html(model: DocumentoGenericoStructured) -> str:
    referencias = [
        f"{item.tipo}: {item.nombre} - {item.concepto}" if item.concepto else f"{item.tipo}: {item.nombre}"
        for item in model.referencias_diagnosticas
    ]
    return "".join(
        [
            _paragraph("Nombre del paciente", model.patient_name),
            _paragraph("Título", model.titulo or model.tipo_inferido),
            _paragraph("Resumen", model.resumen),
            f"<p><b>Referencias diagnósticas</b></p>{_list_items(referencias)}",
            _paragraph("Observaciones", model.observaciones),
        ]
    )


def render_clinical_document_html(model: ClinicalStructuredModel) -> str:
    if isinstance(model, HistoriaClinicaStructured):
        return render_historia_html(model)
    if isinstance(model, LaboratorioStructured):
        return render_laboratorio_html(model)
    if isinstance(model, RadiologiaStructured):
        return render_radiologia_html(model)
    if isinstance(model, PrescripcionStructured):
        return render_prescripcion_html(model)
    if isinstance(model, DocumentoQuirurgicoStructured):
        return render_quirurgico_html(model)
    if isinstance(model, FacturaStructured):
        return render_factura_html(model)
    if isinstance(model, PrefacturaStructured):
        return render_prefactura_html(model)
    if isinstance(model, DocumentoGenericoStructured):
        return render_generico_html(model)
    raise TypeError(f"Modelo clínico no soportado: {type(model)!r}")


def render_clinical_document_html_from_payload(document_type: str, payload: dict[str, Any]) -> str:
    model_cls = get_clinical_document_model(document_type)
    model = model_cls.model_validate(payload)
    return render_clinical_document_html(model)
