from __future__ import annotations

import pytest
from pydantic import ValidationError

from app.llm.renderers import render_clinical_document_html
from app.llm.schemas import HistoriaClinicaStructured
from app.services.clinical_document_projection import serialize_analysis_document
from app.services.clinical_extraction_quality import assess_clinical_extraction
from app.services.deterministic_signals import extract_deterministic_signals
from app.services.historia_recomendaciones import (
    build_historia_recommendation_context,
    extract_historia_recommendation_fragments,
)
from modules.processing.resumen_google import _build_historia_prompt_pack


_REPRESENTATIVE_RECOMMENDATIONS = """
Plan de egreso médico
Incapacidad: 30 días
Fórmula médica
Acetaminofén 500 mg vía oral cada 8 horas por 5 días
Naproxeno 250 mg vía oral cada 12 horas por 3 días

Usar cabestrillo permanente por 3 semanas.
Control con ortopedia en 10 días.
Realizar curación cada 3 días y retiro de puntos en 15 días.
Evitar cargar peso por 6 semanas.
Se explican signos de alarma.
"""


def _historia_model(**overrides) -> HistoriaClinicaStructured:
    payload = {
        "pn": "PACIENTE DEMO",
        "rs": "Paciente atendido por lesión musculoesquelética, con manejo y egreso estable.",
    }
    payload.update(overrides)
    return HistoriaClinicaStructured.model_validate(payload)


def test_recommendation_model_supports_aliases_sanitizes_and_deduplicates_stably() -> None:
    model = _historia_model(
        rm=[
            {
                "ct": "incapacidad",
                "i": "<b>Incapacidad laboral</b>",
                "pd": "<em>30 días</em>",
                "f": "2026-07-30",
                "pg": 8,
                "ev": "<script>Incapacidad por 30 días</script>",
            },
            {
                "ct": "incapacidad",
                "i": "incapacidad laboral",
                "pd": "30 DÍAS",
                "pg": 9,
            },
            {"ct": "otra"},
        ]
    )

    dumped = model.model_dump(by_alias=True, exclude_none=True)

    assert len(model.recomendaciones_medicas) == 2
    assert dumped["rm"][0] == {
        "ct": "incapacidad",
        "i": "Incapacidad laboral",
        "pd": "30 días",
        "f": "2026-07-30",
        "pg": 8,
        "ev": "Incapacidad por 30 días",
    }
    assert dumped["rm"][1] == {"ct": "otra"}


def test_recommendation_model_rejects_unknown_category_and_invalid_page() -> None:
    with pytest.raises(ValidationError):
        _historia_model(rm=[{"ct": "dieta", "i": "Dieta blanda"}])
    with pytest.raises(ValidationError):
        _historia_model(rm=[{"ct": "otra", "i": "Indicación", "pg": 0}])


def test_deterministic_fragments_scan_all_pages_and_exclude_completed_or_generic_alerts() -> None:
    source = (
        "Antecedentes\nUsó cabestrillo hace 5 años.\n"
        "Se administró dipirona 1 g IV.\nProcedimiento realizado y finalizado.\n"
        + ("Evolución estable sin nuevas indicaciones.\n" * 400)
        + "\f\n"
        + _REPRESENTATIVE_RECOMMENDATIONS
    )

    fragments = extract_historia_recommendation_fragments(source)
    context = "\n".join(fragments)

    assert fragments == extract_historia_recommendation_fragments(source)
    assert "[página 2] Incapacidad: 30 días" in context
    assert "Acetaminofén 500 mg" in context
    assert "Naproxeno 250 mg" in context
    assert "cabestrillo permanente" in context
    assert "Control con ortopedia" in context
    assert "curación cada 3 días" in context
    assert "retiro de puntos" in context
    assert "Evitar cargar peso" in context
    assert "dipirona" not in context.casefold()
    assert "hace 5 años" not in context
    assert "signos de alarma" not in context.casefold()


def test_long_history_prompt_pack_prioritizes_recommendations_from_the_end() -> None:
    source = (
        "Nombre del paciente: PACIENTE DEMO\nMotivo de consulta: trauma de hombro.\n"
        + ("Nota clínica repetida sin indicaciones vigentes.\n" * 800)
        + "\f\n"
        + _REPRESENTATIVE_RECOMMENDATIONS
    )
    snapshot = extract_deterministic_signals(source, "historia_clinica")

    prompt_pack = _build_historia_prompt_pack(
        source,
        snapshot=snapshot,
        compacted_text=snapshot.compacted_text,
        max_chars=5000,
    )

    assert "[RECOMENDACIONES MEDICAS CANDIDATAS]" in prompt_pack
    assert "Incapacidad: 30 días" in prompt_pack
    assert "Acetaminofén 500 mg" in prompt_pack
    assert "Control con ortopedia" in prompt_pack
    assert "retiro de puntos" in prompt_pack
    assert "Evitar cargar peso" in prompt_pack


def test_quality_marks_missing_recommendations_for_retry_only_when_source_is_concrete() -> None:
    missing = assess_clinical_extraction(
        document_type="historia_clinica",
        raw_text="Nombre del paciente: PACIENTE DEMO\nIncapacidad: 30 días",
        analysis_model=_historia_model(),
    )
    generic_alarm = assess_clinical_extraction(
        document_type="historia_clinica",
        raw_text="Nombre del paciente: PACIENTE DEMO\nSe explican signos de alarma.",
        analysis_model=_historia_model(),
    )

    assert "recomendaciones_medicas_missing" in missing.reason_codes
    assert missing.should_retry is True
    assert "recomendaciones_medicas_missing" not in generic_alarm.reason_codes


def test_document_api_serializes_rm_and_renders_it_after_summary() -> None:
    document = serialize_analysis_document(
        {
            "tipo_documento": "historia_clinica",
            "analysis_structured": _historia_model(
                rm=[
                    {
                        "ct": "incapacidad",
                        "i": "Incapacidad laboral",
                        "pd": "30 días",
                        "pg": 8,
                        "ev": "Se expide incapacidad por 30 días",
                    },
                    {"ct": "restriccion", "i": "Evitar cargar peso"},
                ],
                md=[
                    {"n": "Dipirona", "ds": "1 g", "v": "IV", "tu": "administrado"},
                    {
                        "n": "Acetaminofén",
                        "ds": "500 mg",
                        "f": "Cada 8 horas",
                        "du": "5 días",
                        "tu": "formulado",
                    },
                    {"n": "Cefazolina", "ds": "1 g", "tu": "ordenado"},
                ],
            ).model_dump(by_alias=True, exclude_none=True),
        }
    )

    assert document is not None
    structured = document["analysis_structured"]
    html = document["analisis_html"]
    assert structured["rm"][0]["ct"] == "incapacidad"
    assert html.index("<p><b>Resumen</b></p>") < html.index("<p><b>Recomendaciones médicas</b></p>")
    assert html.index("<p><b>Recomendaciones médicas</b></p>") < html.index("<p><b>Antecedentes</b></p>")
    assert "Tratamientos y cuidados" in html
    assert "Acetaminofén" in html
    administered_section = html.split("<p><b>Medicamentos administrados</b></p>", 1)[1]
    assert "Dipirona" in administered_section
    assert "Acetaminofén" not in administered_section
    assert "Cefazolina" not in html


def test_renderer_omits_recommendations_section_for_legacy_history_without_rm() -> None:
    html = render_clinical_document_html(
        _historia_model(md=[{"n": "Acetaminofén", "ds": "500 mg", "tu": "formulado"}])
    )

    assert "Recomendaciones médicas" not in html
    assert "Tratamientos y cuidados" not in html


def test_context_deduplicates_repeated_recommendations_in_stable_order() -> None:
    source = "\n".join(
        [
            "Plan de egreso médico",
            "Control con ortopedia en 10 días.",
            "Control con ortopedia en 10 días.",
            "Incapacidad: 30 días",
        ]
    )

    context = build_historia_recommendation_context(source)

    assert context.count("Control con ortopedia") == 1
    assert context.index("Control con ortopedia") < context.index("Incapacidad")


def test_deterministic_fragments_keep_concrete_alarm_signs_but_not_generic_heading() -> None:
    source = """
    Plan de egreso
    Signos de alarma
    Fiebre, dolor intenso o secreción por la herida.
    """

    context = build_historia_recommendation_context(source)

    assert "Fiebre, dolor intenso o secreción" in context
    assert context.count("Signos de alarma") == 0
