from __future__ import annotations

import shutil
import subprocess
import textwrap
from pathlib import Path

import pytest
from fastapi.templating import Jinja2Templates


ROOT = Path(__file__).resolve().parents[1]
SUBVIEWS = (
    "auditoria",
    "resumen-clinico",
    "preparar-pdf",
    "codificacion-cirugia",
    "factura",
    "glosas",
    "metadatos",
)


def render_epicrisis(**context: object) -> str:
    template = Jinja2Templates(directory=str(ROOT / "web")).env.get_template("epicrisis.html")
    return template.render(nombre_paciente="Paciente de prueba", **context)


def test_epicrisis_renders_accessible_ordered_subviews_with_audit_selected() -> None:
    rendered = render_epicrisis()

    assert rendered.count('role="tab"') == 7
    assert rendered.count('role="tabpanel"') == 7
    positions = [rendered.index(f'data-subview="{subview}"') for subview in SUBVIEWS]
    assert positions == sorted(positions)
    assert 'role="tab" aria-selected="true" aria-controls="auditoria"' in rendered
    for subview in SUBVIEWS:
        assert f'id="tab-{subview}"' in rendered
        assert f'id="{subview}"' in rendered
        assert f'aria-labelledby="tab-{subview}"' in rendered


def test_epicrisis_keeps_all_panels_in_dom_and_renders_safe_empty_states() -> None:
    rendered = render_epicrisis()

    assert 'data-subview-panel="auditoria"' in rendered
    assert 'data-subview-panel="factura"' in rendered
    assert 'data-subview-panel="glosas"' in rendered
    assert "Auditoría aún sin datos de conciliación" in rendered
    assert "Factura no disponible" in rendered
    assert "Sin evaluación estructurada de glosas" in rendered
    assert "Este estado no descarta condiciones pendientes de evaluación." in rendered
    assert 'id="seleccion_pdf_card"' in rendered
    assert 'id="form_codigos_soat"' in rendered


def test_audit_cards_group_codes_and_show_consistent_pending_state() -> None:
    rendered = render_epicrisis(
        case_key="case-1",
        curation_schema_version="v1",
        diagnosticos_curados=[
            {
                "item_id": "diagnosis-1",
                "code": "S202",
                "description": "Contusión del tórax",
                "status": "pendiente_revision",
                "evidence": [],
            }
        ],
        procedimientos_curados=[],
        conflictos_curacion=[],
        catalogos_utilizados=[],
    )

    assert 'class="curation-item__codes"' in rendered
    assert 'curation-status--pending">\n            Revisión pendiente' in rendered
    assert 'class="curation-actions__label">Código corregido' in rendered


def test_epicrisis_tab_indicators_use_only_reliable_semantic_states() -> None:
    neutral = render_epicrisis()
    assert 'id="tab-auditoria" class="epicrisis-subview-tab epicrisis-subview-tab--neutral"' in neutral
    assert 'id="tab-glosas" class="epicrisis-subview-tab epicrisis-subview-tab--neutral"' in neutral
    assert 'id="tab-metadatos" class="epicrisis-subview-tab epicrisis-subview-tab--neutral"' in neutral

    success = render_epicrisis(
        curation_schema_version="v1",
        diagnosticos_curados=[],
        procedimientos_curados=[],
        conflictos_curacion=[],
        historia={
            "tipo_documento": "historia_clinica",
            "extraction_metadata": {"total_pages": 1, "pages_with_text": 1},
            "historia_processing": {"coverage_ratio": 1.0, "chunks_succeeded": 1, "chunks_total": 1},
            "analysis_provider": "gemini",
            "analysis_model_name": "gemini-2.5-flash",
            "review_required": False,
        },
    )
    assert 'id="tab-auditoria" class="epicrisis-subview-tab epicrisis-subview-tab--success"' in success
    assert 'id="tab-metadatos" class="epicrisis-subview-tab epicrisis-subview-tab--success"' in success

    alert = render_epicrisis(
        curation_schema_version="v1",
        curation_generation_blocked=True,
        diagnosticos_curados=[],
        procedimientos_curados=[],
        conflictos_curacion=[],
        medicamentos_glosa=[
            {
                "estado": "revisar",
                "medicamento": "Medicamento",
                "cadena": {"orden": "ausente", "administracion": "ausente", "facturacion": "ausente"},
                "fuentes_por_eslabon": {"orden": [], "administracion": [], "facturacion": []},
            }
        ],
        historia={
            "tipo_documento": "historia_clinica",
            "analysis_quality": {"status": "degraded"},
            "review_required": True,
        },
    )
    assert 'id="tab-auditoria" class="epicrisis-subview-tab epicrisis-subview-tab--alert"' in alert
    assert 'id="tab-glosas" class="epicrisis-subview-tab epicrisis-subview-tab--alert"' in alert
    assert 'id="tab-metadatos" class="epicrisis-subview-tab epicrisis-subview-tab--alert"' in alert


def test_subview_controller_handles_hash_history_top_anchor_and_keyboard() -> None:
    node = shutil.which("node") or shutil.which("nodejs")
    if not node:
        pytest.skip("node no está disponible en el entorno de pruebas")

    source_path = ROOT / "static" / "js" / "epicrisis" / "subviews.js"
    script = textwrap.dedent(
        f"""
        const fs = require("node:fs");
        const vm = require("node:vm");
        const ids = {list(SUBVIEWS)!r};
        const listeners = {{}};
        const makeClassList = () => ({{ add() {{}}, remove() {{}}, contains() {{ return false; }} }});
        const tabs = ids.map((id) => ({{
          dataset: {{ subview: id }},
          tabIndex: -1,
          attrs: {{}},
          events: {{}},
          setAttribute(name, value) {{ this.attrs[name] = value; }},
          addEventListener(name, handler) {{ this.events[name] = handler; }},
          focus(options) {{ this.focusOptions = options; }},
        }}));
        const panels = ids.map((id) => ({{ dataset: {{ subviewPanel: id }}, hidden: false }}));
        const header = {{ getBoundingClientRect() {{ return {{ height: 73.2 }}; }} }};
        const styleValues = {{}};
        const document = {{
          body: {{ classList: makeClassList() }},
          documentElement: {{ style: {{ setProperty(name, value) {{ styleValues[name] = value; }} }} }},
          querySelector(selector) {{
            if (selector.includes("tablist")) return {{ querySelectorAll() {{ return tabs; }} }};
            if (selector === ".ui-header") return header;
            return null;
          }},
          querySelectorAll() {{ return panels; }},
        }};
        const context = {{
          document,
          location: {{ hash: "#glosas" }},
          history: {{ pushState(_state, _title, hash) {{ context.location.hash = hash; }} }},
          addEventListener(name, handler) {{ listeners[name] = handler; }},
          EpicrisisApp: {{}},
          ResizeObserver: class {{ observe() {{}} disconnect() {{}} }},
        }};
        context.globalThis = context;
        vm.runInNewContext(fs.readFileSync({str(source_path)!r}, "utf8"), context);
        context.EpicrisisApp.subviews.init();

        if (tabs[5].attrs["aria-selected"] !== "true" || !panels[0].hidden || panels[5].hidden) {{
          throw new Error("El hash directo no activó Glosas");
        }}
        if (styleValues["--epicrisis-header-height"] !== "74px") {{
          throw new Error("La barra sticky no usa la altura real del encabezado");
        }}

        let prevented = false;
        tabs[0].events.keydown({{ key: "End", currentTarget: tabs[0], preventDefault() {{ prevented = true; }} }});
        if (!prevented || context.location.hash !== "#metadatos" || tabs[6].focusOptions?.preventScroll !== true) {{
          throw new Error("End no activó la última pestaña sin forzar scroll");
        }}

        context.location.hash = "#epicrisis_top";
        listeners.hashchange();
        if (tabs[6].attrs["aria-selected"] !== "true") {{
          throw new Error("epicrisis_top modificó la subvista activa");
        }}

        context.location.hash = "#desconocida";
        listeners.hashchange();
        if (tabs[0].attrs["aria-selected"] !== "true") {{
          throw new Error("Un hash desconocido no volvió a Auditoría");
        }}

        context.location.hash = "#factura";
        listeners.popstate();
        if (tabs[4].attrs["aria-selected"] !== "true") {{
          throw new Error("La navegación atrás/adelante no restauró Factura");
        }}
        """
    )
    completed = subprocess.run([node, "-e", script], capture_output=True, text=True, check=False)
    assert completed.returncode == 0, completed.stderr or completed.stdout
