from __future__ import annotations

from typing import Any

from app.llm.schemas import FacturaStructured
from app.services.clinical_processing import extraer_factura_json


_SECTION_DEFINITIONS = (
    (
        "procedimientos_quirurgicos",
        "Procedimientos quirúrgicos",
        (
            ("codigo_cups", "CUPS"),
            ("codigo_facturacion", "Código factura"),
            ("descripcion", "Descripción"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
    (
        "procedimientos_no_quirurgicos",
        "Procedimientos no quirúrgicos",
        (
            ("codigo_cups", "CUPS"),
            ("codigo_facturacion", "Código factura"),
            ("descripcion", "Descripción"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
    (
        "examenes_laboratorio",
        "Laboratorio",
        (
            ("prueba", "Prueba"),
            ("descripcion", "Descripción"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
    (
        "imagenologia",
        "Imagenología",
        (
            ("estudio", "Estudio"),
            ("descripcion", "Descripción"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
    (
        "hospitalizacion",
        "Hospitalización",
        (("habitacion", "Habitación"), ("dias", "Días"), ("tarifa", "Tarifa"), ("total", "Total")),
    ),
    (
        "honorarios_medicos",
        "Honorarios médicos",
        (
            ("rol", "Rol"),
            ("profesional", "Profesional"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
    (
        "medicamentos",
        "Medicamentos",
        (
            ("medicamento", "Medicamento"),
            ("dosis", "Dosis"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
    (
        "otros_servicios",
        "Otros servicios",
        (
            ("codigo_facturacion", "Código factura"),
            ("codigo_referencia", "Código referencia"),
            ("descripcion", "Descripción"),
            ("cantidad", "Cantidad"),
            ("valor_unitario", "Valor unitario"),
            ("total", "Total"),
        ),
    ),
)

_FIELD_ALIASES = {
    "nombre_institucion": ("nombre_institucion", "nombre"),
    "nit": ("nit",),
    "direccion": ("direccion",),
    "ciudad_departamento": ("ciudad_departamento", "ciudad"),
    "numero_factura": ("numero_factura", "numero"),
    "prefijo": ("prefijo",),
    "fecha_emision": ("fecha_emision",),
    "numero_caso": ("numero_caso",),
    "fecha_vencimiento": ("fecha_vencimiento",),
    "aseguradora_eps": ("aseguradora_eps", "aseguradora"),
    "nit_pagador": ("nit_pagador", "nit"),
    "tipo_convenio": ("tipo_convenio",),
    "numero_autorizacion": ("numero_autorizacion",),
    "nombre_completo": ("nombre_completo", "nombre"),
    "numero_identificacion": ("numero_identificacion", "identificacion"),
    "fecha_ingreso": ("fecha_ingreso",),
    "fecha_egreso": ("fecha_egreso",),
    "estancia_hospitalaria": ("estancia_hospitalaria",),
}

_STATUS_LABELS = {
    "liquidada": "Liquidada",
    "pendiente_revision": "Pendiente de revisión",
    "conflicto": "Conflicto",
}


def _text(value: Any, default: str = "") -> str:
    if value is None:
        return default
    rendered = str(value).strip()
    return rendered or default


def _mapping(value: Any) -> dict[str, Any]:
    return value if isinstance(value, dict) else {}


def _field(source: dict[str, Any], name: str) -> str:
    for alias in _FIELD_ALIASES.get(name, (name,)):
        value = _text(source.get(alias))
        if value:
            return value
    return ""


def _display_value(value: Any) -> str:
    return _text(value, "—")


def _currency(value: Any) -> str:
    if value is None or isinstance(value, bool):
        return "—"
    if isinstance(value, (int, float)):
        return f"${value:,.0f}".replace(",", ".")
    return _display_value(value)


def _has_meaningful_data(invoice: dict[str, Any]) -> bool:
    def has_value(value: Any) -> bool:
        if isinstance(value, dict):
            return any(has_value(item) for item in value.values())
        if isinstance(value, (list, tuple, set)):
            return any(has_value(item) for item in value)
        return bool(_text(value))

    return has_value(invoice)


def _normalize_rows(items: Any) -> list[dict[str, str]]:
    return [
        {str(key): _display_value(value) for key, value in item.items()}
        for item in (items or [])
        if isinstance(item, dict)
    ]


def _canonical_lines(invoice: dict[str, Any]) -> list[dict[str, str]]:
    rows = []
    for item in _normalize_rows(invoice.get("lineas_canonicas")):
        row = dict(item)
        row["categoria"] = _display_value(item.get("categoria"))
        row["clasificacion_cups"] = _display_value(item.get("clasificacion_cups"))
        rows.append(row)
    return rows


def _components(valuation: dict[str, Any]) -> list[dict[str, str]]:
    base = _mapping(valuation.get("componentes"))
    liquidated = _mapping(valuation.get("componentes_liquidados"))
    codes = _mapping(valuation.get("codigos_componentes"))
    names = list(dict.fromkeys([*base, *liquidated, *codes]))
    return [
        {
            "name": _text(name),
            "base": _currency(base.get(name)),
            "liquidated": _currency(liquidated.get(name)),
            "code": _display_value(codes.get(name)),
        }
        for name in names
    ]


def _crosswalk_details(valuation: dict[str, Any]) -> list[dict[str, str]]:
    crosswalk = _mapping(valuation.get("cruce_cups_soat"))
    if not crosswalk:
        return []
    details = []
    for key, label in (
        ("status", "Estado del cruce"),
        ("cups_code", "CUPS"),
        ("selected_soat_code", "SOAT seleccionado"),
        ("selected_soat_description", "Descripción SOAT"),
    ):
        if crosswalk.get(key) is not None:
            details.append({"label": label, "value": _display_value(crosswalk[key])})
    evidence = crosswalk.get("evidence")
    if evidence:
        details.append({"label": "Evidencia del cruce", "value": "; ".join(_text(item) for item in evidence)})
    return details


def _soat_procedures(invoice: dict[str, Any], canonical: list[dict[str, str]]) -> list[dict[str, Any]]:
    procedures = []
    services = _mapping(invoice.get("servicios_procedimientos"))
    for item in services.get("procedimientos_quirurgicos") or []:
        if not isinstance(item, dict):
            continue
        valuation = _mapping(item.get("valoracion_soat"))
        index = valuation.get("indice_linea_canonica")
        if index is None:
            canonical_index = -1
        else:
            try:
                canonical_index = int(index)
            except (TypeError, ValueError):
                canonical_index = -1
        linked = canonical[canonical_index] if 0 <= canonical_index < len(canonical) else None
        procedures.append(
            {
                "codigo_facturado_cups": " / ".join(
                    value
                    for value in (
                        _text(item.get("codigo_facturacion")),
                        _text(item.get("codigo_cups") or item.get("codigo_referencia")),
                    )
                    if value
                )
                or "—",
                "codigo_soat": _display_value(item.get("codigo_soat")),
                "descripcion": _display_value(item.get("descripcion") or item.get("concepto")),
                "grupo": _display_value(item.get("grupo_quirurgico_soat")),
                "estado": _STATUS_LABELS.get(
                    _text(valuation.get("estado")), _display_value(valuation.get("estado"))
                ),
                "estado_codigo": _text(valuation.get("estado"), "sin_valuación"),
                "regla": _display_value(valuation.get("regla_aplicada")),
                "valor_base": _currency(valuation.get("valor_base")),
                "valor_liquidado": _currency(valuation.get("valor_liquidado")),
                "linea_canonica": (
                    f"Línea canónica {canonical_index + 1}"
                    if linked is not None
                    else "Sin vínculo persistido"
                ),
                "linea_canonica_descripcion": _display_value(linked.get("descripcion")) if linked else "",
                "evidencia": [_text(entry) for entry in valuation.get("evidencia") or [] if _text(entry)],
                "componentes": _components(valuation),
                "cruce": _crosswalk_details(valuation),
            }
        )
    return procedures


def build_factura_view(factura: dict[str, Any] | None) -> dict[str, Any]:
    """Builds the read-only invoice workspace from persisted data only."""
    raw_document = factura if isinstance(factura, dict) else {}
    invoice = extraer_factura_json(raw_document)
    invoice = dict(invoice) if isinstance(invoice, dict) else {}
    persisted_json = raw_document.get("factura_json")
    if isinstance(persisted_json, dict) and "resumen_tarifario_soat" in persisted_json:
        invoice["resumen_tarifario_soat"] = persisted_json.get("resumen_tarifario_soat")
    elif isinstance(raw_document.get("analysis_structured"), dict):
        try:
            structured = FacturaStructured.model_validate(raw_document["analysis_structured"])
        except (TypeError, ValueError):
            structured = None
        if structured is not None and structured.resumen_tarifario_soat is not None:
            invoice["resumen_tarifario_soat"] = structured.resumen_tarifario_soat.model_dump(
                exclude_none=True
            )
    canonical = _canonical_lines(invoice)
    services = _mapping(invoice.get("servicios_procedimientos"))
    sections = []
    for key, label, columns in _SECTION_DEFINITIONS:
        sections.append(
            {
                "key": key,
                "label": label,
                "count": len([item for item in services.get(key) or [] if isinstance(item, dict)]),
                "columns": [
                    {"key": column_key, "label": column_label} for column_key, column_label in columns
                ],
                "rows": _normalize_rows(services.get(key)),
            }
        )

    provider = _mapping(invoice.get("proveedor"))
    invoice_info = _mapping(invoice.get("informacion_factura"))
    payer = _mapping(invoice.get("pagador"))
    patient = _mapping(invoice.get("informacion_paciente"))
    summary = _mapping(invoice.get("resumen_tarifario_soat"))
    soat_procedures = _soat_procedures(invoice, canonical)
    raw_html = _text(raw_document.get("analisis_html") or raw_document.get("analisis"))

    return {
        "is_structured": bool(
            raw_document.get("factura_json")
            or raw_document.get("analysis_structured")
            or _has_meaningful_data(invoice)
        ),
        "legacy_html": raw_html,
        "patient_name": _text(invoice.get("nombre_paciente") or patient.get("nombre_completo")),
        "identity_groups": [
            {
                "title": "Proveedor",
                "fields": [
                    {"label": "Institución", "value": _field(provider, "nombre_institucion")},
                    {"label": "NIT", "value": _field(provider, "nit")},
                    {"label": "Dirección", "value": _field(provider, "direccion")},
                    {"label": "Ciudad / departamento", "value": _field(provider, "ciudad_departamento")},
                ],
            },
            {
                "title": "Factura",
                "fields": [
                    {"label": "Número", "value": _field(invoice_info, "numero_factura")},
                    {"label": "Prefijo", "value": _field(invoice_info, "prefijo")},
                    {
                        "label": "Fecha de emisión",
                        "value": _field(invoice_info, "fecha_emision"),
                        "is_date": True,
                    },
                    {"label": "Número de caso", "value": _field(invoice_info, "numero_caso")},
                    {
                        "label": "Fecha de vencimiento",
                        "value": _field(invoice_info, "fecha_vencimiento"),
                        "is_date": True,
                    },
                ],
            },
            {
                "title": "Paciente",
                "fields": [
                    {"label": "Nombre", "value": _field(patient, "nombre_completo")},
                    {"label": "Identificación", "value": _field(patient, "numero_identificacion")},
                    {"label": "Ingreso", "value": _field(patient, "fecha_ingreso"), "is_date": True},
                    {"label": "Egreso", "value": _field(patient, "fecha_egreso"), "is_date": True},
                    {"label": "Estancia", "value": _field(patient, "estancia_hospitalaria")},
                ],
            },
            {
                "title": "Pagador",
                "fields": [
                    {"label": "Aseguradora / EPS", "value": _field(payer, "aseguradora_eps")},
                    {"label": "NIT", "value": _field(payer, "nit_pagador")},
                    {"label": "Tipo de convenio", "value": _field(payer, "tipo_convenio")},
                    {"label": "Autorización", "value": _field(payer, "numero_autorizacion")},
                ],
            },
        ],
        "canonical_lines": canonical,
        "financial": [
            {
                "label": "Total de servicios",
                "value": _display_value(_mapping(invoice.get("analisis_financiero")).get("total_servicios")),
            },
            {
                "label": "Descuentos",
                "value": _display_value(_mapping(invoice.get("analisis_financiero")).get("descuentos")),
            },
            {
                "label": "Copagos",
                "value": _display_value(_mapping(invoice.get("analisis_financiero")).get("copagos")),
            },
            {
                "label": "Total factura",
                "value": _display_value(
                    _mapping(invoice.get("analisis_financiero")).get("valor_total_factura")
                ),
            },
            {
                "label": "Valor en letras",
                "value": _display_value(_mapping(invoice.get("analisis_financiero")).get("valor_en_letras")),
            },
        ],
        "observations": [_text(item) for item in invoice.get("observaciones") or [] if _text(item)],
        "sections": sections,
        "soat": {
            "available": bool(summary),
            "summary": [
                {"label": "Total base", "value": _currency(summary.get("total_base"))},
                {"label": "Total liquidado", "value": _currency(summary.get("total_liquidado"))},
                {"label": "Pendientes", "value": _display_value(summary.get("pendientes"))},
                {"label": "Versión del catálogo", "value": _display_value(summary.get("version_catalogo"))},
            ],
            "note": _text(summary.get("nota")),
            "procedures": soat_procedures,
        },
    }
