from __future__ import annotations

import unittest
from pathlib import Path

from pydantic import ValidationError

from app.case_epicrisis.application.utils import (
    build_ayudas_diagnosticas,
    build_pdf_primary_ayudas_diagnosticas,
    build_pdf_primary_medicamentos,
    build_procedimientos_clinicos,
    extract_incapacidad_clinica,
    extract_recomendaciones_medicas,
    format_ayuda_diagnostica_interpretacion_exportable,
    format_ayuda_diagnostica_nombre_exportable,
    normalize_context_payload,
)
from app.case_epicrisis.domain.models import RecomendacionMedica
from app.services.clinical_processing import normalizar_medicamento_canonico


class ClinicalProcedureConsolidationTest(unittest.TestCase):
    def test_classifies_cups_and_marks_invoice_only_support(self) -> None:
        procedures = build_procedimientos_clinicos(
            {
                "procedimientos_hc": ["151001 Consulta de primera vez"],
                "procedimientos_factura": [
                    {"codigo_cups": "010101", "codigo_soat": "S-1", "descripcion": "Biopsia"}
                ],
                "codigos_desde_soat": [],
                "pdf_draft": {},
            }
        )

        by_description = {item["descripcion"]: item for item in procedures}
        self.assertEqual(by_description["Consulta de primera vez"]["clasificacion"], "no_quirurgico")
        self.assertTrue(by_description["Consulta de primera vez"]["soporte_clinico"])
        self.assertEqual(by_description["Biopsia"]["clasificacion"], "quirurgico")
        self.assertFalse(by_description["Biopsia"]["soporte_clinico"])

    def test_manual_classification_has_precedence(self) -> None:
        base = build_procedimientos_clinicos(
            {"procedimientos_hc": ["151001 Consulta"], "pdf_draft": {}}
        )[0]
        result = build_procedimientos_clinicos(
            {
                "procedimientos_hc": ["151001 Consulta"],
                "pdf_draft": {
                    "procedure_classification_overrides": {base["key"]: "quirurgico"}
                },
            }
        )[0]
        self.assertEqual(result["clasificacion"], "quirurgico")
        self.assertTrue(result["clasificacion_manual"])

    def test_legacy_context_gets_canonical_procedures(self) -> None:
        context = normalize_context_payload({"procedimientos_hc": ["Procedimiento sin código"]})
        self.assertEqual(context["procedimientos_clinicos"][0]["clasificacion"], "sin_clasificar")


class ClinicalExtractionRulesTest(unittest.TestCase):
    def test_immediate_medication_defaults_missing_quantity_to_one(self) -> None:
        medication = normalizar_medicamento_canonico(
            {"medicamento": "Dipirona", "dosis": "1 g IV aplicar ahora"}
        )
        self.assertEqual(medication["cantidad"], "1")

    def test_explicit_quantity_is_preserved(self) -> None:
        medication = normalizar_medicamento_canonico(
            {"medicamento": "Dipirona", "dosis": "aplicar ahora", "cantidad": "2"}
        )
        self.assertEqual(medication["cantidad"], "2")

    def test_extracts_only_ordered_incapacity(self) -> None:
        ordered = extract_incapacidad_clinica(
            {"historia": {"texto_extraido": "Se ordena incapacidad por 3 días."}}
        )
        historical = extract_incapacidad_clinica(
            {"historia": {"texto_extraido": "Antecedente de incapacidad hace un año."}}
        )
        self.assertTrue(ordered["ordenada"])
        self.assertEqual(ordered["duracion"], "3")
        self.assertFalse(historical["ordenada"])


class MedicalRecommendationProjectionTest(unittest.TestCase):
    def test_domain_model_accepts_duration_alias_and_rejects_unknown_category(self) -> None:
        recommendation = RecomendacionMedica.model_validate(
            {
                "categoria": "restriccion",
                "indicacion": "<b>Evitar cargar peso</b>",
                "periodo_duracion": "6 semanas",
                "pagina": 7,
                "evidencia": "<em>Evitar cargar peso por 6 semanas</em>",
            }
        )

        self.assertEqual(recommendation.indicacion, "Evitar cargar peso")
        self.assertEqual(recommendation.duracion, "6 semanas")
        self.assertEqual(recommendation.pagina, 7)
        with self.assertRaises(ValidationError):
            RecomendacionMedica(categoria="dieta", indicacion="Dieta blanda")

    def test_projects_persisted_recommendations_and_formulated_medications_in_stable_order(self) -> None:
        historia = {
            "tipo_documento": "historia_clinica",
            "analysis_structured": {
                "dt": "historia_clinica",
                "pn": "Paciente Demo",
                "rs": "Resumen",
                "rm": [
                    {
                        "ct": "incapacidad",
                        "i": "Incapacidad laboral",
                        "pd": "30 días",
                        "f": "2026-07-30",
                        "pg": 8,
                        "ev": "Se expide incapacidad por 30 días",
                    },
                    {"ct": "restriccion", "i": "Evitar cargar peso"},
                    {"ct": "restriccion", "i": "Evitar cargar peso"},
                ],
                "md": [
                    {"n": "Dipirona", "ds": "1 g", "tu": "administrado"},
                    {
                        "n": "Acetaminofén",
                        "ds": "500 mg",
                        "f": "cada 8 horas",
                        "du": "5 días",
                        "tu": "formulado",
                    },
                ],
            },
        }

        projected = extract_recomendaciones_medicas(historia)

        self.assertEqual(
            [item["categoria"] for item in projected],
            ["incapacidad", "restriccion", "tratamiento_cuidado"],
        )
        self.assertEqual(projected[0]["pagina"], 8)
        self.assertEqual(projected[0]["evidencia"], "Se expide incapacidad por 30 días")
        self.assertIn("Acetaminofén", projected[2]["indicacion"])
        self.assertEqual(projected[2]["duracion"], "5 días")
        self.assertFalse(any("Dipirona" in item["indicacion"] for item in projected))

    def test_missing_context_normalizes_to_empty_without_backfilling_from_history(self) -> None:
        context = normalize_context_payload(
            {
                "historia": {
                    "tipo_documento": "historia_clinica",
                    "analysis_structured": {
                        "dt": "historia_clinica",
                        "pn": "Paciente Demo",
                        "rs": "Resumen",
                        "rm": [{"ct": "otra", "i": "Control ambulatorio"}],
                    },
                }
            }
        )

        self.assertEqual(context["recomendaciones_medicas"], [])

    def test_primary_medications_include_only_administered_and_allow_dual_status(self) -> None:
        medications = build_pdf_primary_medicamentos(
            {
                "medicamentos_caso": [
                    {"nombre": "Dipirona", "medicamento": "Dipirona", "estados": ["administrado"]},
                    {"nombre": "Naproxeno", "medicamento": "Naproxeno", "estados": ["formulado"]},
                    {
                        "nombre": "Acetaminofén",
                        "medicamento": "Acetaminofén",
                        "estados": ["administrado", "formulado"],
                    },
                ]
            }
        )

        self.assertEqual([item["nombre"] for item in medications], ["Dipirona", "Acetaminofén"])


class DiagnosticAidExportTest(unittest.TestCase):
    def test_invoice_aid_uses_code_and_description_as_label_not_interpretation(self) -> None:
        context = {
            "factura": {
                "_id": "fac-1",
                "factura_json": {
                    "servicios_procedimientos": {
                        "imagenologia": [
                            {
                                "estudio": "21602",
                                "descripcion": "Portátiles con fluoroscopia y/o intensificador de imagen",
                            }
                        ],
                        "examenes_laboratorio": [],
                    }
                },
            }
        }

        ayuda = build_ayudas_diagnosticas(context)[0]

        self.assertEqual(
            ayuda["nombre"],
            "21602 - Portátiles con fluoroscopia y/o intensificador de imagen",
        )
        self.assertEqual(ayuda["concepto"], "No interpretado")
        self.assertFalse(ayuda["interpretado"])

        pdf_ayuda = build_pdf_primary_ayudas_diagnosticas({"ayudas_diagnosticas": [ayuda]})[0]
        self.assertEqual(
            format_ayuda_diagnostica_nombre_exportable(pdf_ayuda),
            "21602 - Portátiles con fluoroscopia y/o intensificador de imagen",
        )
        self.assertEqual(format_ayuda_diagnostica_interpretacion_exportable(pdf_ayuda), "")

    def test_legacy_invoice_aid_moves_description_from_concept_to_export_label(self) -> None:
        legacy = {
            "tipo": "imagen",
            "nombre": "21602",
            "concepto": "Portátiles con fluoroscopia y/o intensificador de imagen",
            "fuente": "factura",
            "interpretado": False,
            "facturado": True,
            "exportable": True,
        }

        self.assertEqual(
            format_ayuda_diagnostica_nombre_exportable(legacy),
            "21602 - Portátiles con fluoroscopia y/o intensificador de imagen",
        )
        self.assertEqual(format_ayuda_diagnostica_interpretacion_exportable(legacy), "")


class PdfClinicalExportContractTest(unittest.TestCase):
    def test_procedure_table_exports_cups_without_soat_column(self) -> None:
        pdf_js = Path("static/js/epicrisis/pdf.js").read_text(encoding="utf-8")

        self.assertIn('renderPdfTableContent(["Código CUPS", "Procedimiento"], rows)', pdf_js)
        self.assertNotIn('renderPdfTableContent(["Código CUPS", "Código SOAT", "Procedimiento"], rows)', pdf_js)
        self.assertNotIn("codigoSoat", pdf_js)


if __name__ == "__main__":
    unittest.main()
