"""
Tests for device-keyed staff overrides (build plan 02, P4).

Locked semantics (phase-2 design Item 4 / brief section 6):
- visit overrides are patient-scoped and fan out to ALL the patient's lines
  (they ride the confirmed_visits path by construction);
- swo/pa overrides apply only to lines whose device_type matches;
- device_type '' marks a legacy patient-scoped row and matches all devices;
- staff knowledge wins over the CSV cell;
- after a write, the recompute returns {lines, rollup} from the same engine.

PHI CONTRACT: synthetic patient_ids only; overrides carry hashes.
"""

import hashlib
import io
import sys
from datetime import date, timedelta
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent / "python-backend"))

from core.coverage_calculator import ShipmentRecord  # noqa: E402
from core.overrides import (  # noqa: E402
    Override,
    OverrideKey,
    apply_overrides,
    overrides_from_saved,
)
from core.worklist_readiness import build_readiness_index  # noqa: E402

TODAY = date.today()


def _hash(pid: str) -> str:
    return hashlib.sha256(pid.encode()).hexdigest()


def _record(**kwargs) -> ShipmentRecord:
    defaults = dict(
        patient_id="PT-OV-1",
        device_type="dexcom_g7",
        shipment_date=TODAY - timedelta(days=5),
        quantity=3,
        payer="Medicare Part B",
        component="sensor",
        plan_type="medicare",
        csv_visit_date=TODAY - timedelta(days=30),
        csv_pecos_verified="Yes",
        csv_diagnosis_on_file="Yes",
    )
    defaults.update(kwargs)
    return ShipmentRecord(**defaults)


def _swo_override(pid="PT-OV-1", device="dexcom_g7", status="on_file"):
    return Override(
        key=OverrideKey(
            org_id="org", patient_hash=_hash(pid), device_type=device,
            doc_type="swo",
        ),
        status=status,
    )


def test_override_swo_applies_only_to_one_device():
    # Two devices, both missing SWO -> Action Needed. Override device A only.
    g7 = _record(device_type="dexcom_g7")
    libre = _record(device_type="freestyle_libre_2")
    index = build_readiness_index(
        [g7, libre], overrides=[_swo_override(device="dexcom_g7")]
    )
    assert index.by_line[("PT-OV-1", "dexcom_g7", "medicare")].line_status.value == "Clear to Ship"
    assert index.by_line[("PT-OV-1", "freestyle_libre_2", "medicare")].line_status.value == "Action Needed"


def test_legacy_blank_device_row_applies_patient_wide():
    g7 = _record(device_type="dexcom_g7")
    libre = _record(device_type="freestyle_libre_2")
    index = build_readiness_index(
        [g7, libre], overrides=[_swo_override(device="")]
    )
    for key, verdict in index.by_line.items():
        assert verdict.line_status.value == "Clear to Ship", key


def test_device_scoped_row_wins_over_legacy_row():
    g7 = _record(device_type="dexcom_g7")
    index = build_readiness_index(
        [g7],
        overrides=[
            _swo_override(device="", status="on_file"),
            _swo_override(device="dexcom_g7", status="pending"),
        ],
    )
    verdict = index.by_line[("PT-OV-1", "dexcom_g7", "medicare")]
    swo = next(i for i in verdict.doc_items if i.doc_type == "swo")
    assert swo.quality.value == "PENDING"  # device-scoped Pending won


def test_override_triggers_recompute_to_better_status():
    # SWO missing is the last open required item; the override flips the line.
    rec = _record()
    base = build_readiness_index([rec])
    assert base.by_line[("PT-OV-1", "dexcom_g7", "medicare")].line_status.value == "Action Needed"
    fixed = build_readiness_index([rec], overrides=[_swo_override()])
    assert fixed.by_line[("PT-OV-1", "dexcom_g7", "medicare")].line_status.value == "Clear to Ship"


def test_override_visit_applies_to_all_patient_lines():
    # Visit rides confirmed_visits: patient-scoped fan-out by construction.
    g7 = _record(device_type="dexcom_g7", csv_visit_date=None,
                 csv_swo_status="On File")
    libre = _record(device_type="freestyle_libre_2", csv_visit_date=None,
                    csv_swo_status="On File")
    confirmed = {_hash("PT-OV-1"): TODAY - timedelta(days=10)}
    index = build_readiness_index([g7, libre], confirmed_visits=confirmed)
    for key, verdict in index.by_line.items():
        visit = next(i for i in verdict.doc_items if i.doc_type == "visit")
        assert visit.satisfied, key


def test_overrides_from_saved_shape():
    saved = {
        _hash("PT-OV-1"): {
            "": {"swo": {"status": "on_file"}},
            "dexcom_g7": {"pa": {"status": "approved"}},
        }
    }
    overrides = overrides_from_saved(saved)
    assert len(overrides) == 2
    kinds = {(o.key.device_type, o.key.doc_type, o.status) for o in overrides}
    assert kinds == {("", "swo", "on_file"), ("dexcom_g7", "pa", "approved")}


def test_apply_overrides_returns_touched_patients():
    g7 = _record()
    other = _record(patient_id="PT-OV-2")
    index_lines = build_readiness_index([g7, other]).lines
    touched = apply_overrides(index_lines, [_swo_override()])
    assert touched == {"PT-OV-1"}


# --- API level ---------------------------------------------------------------

httpx = pytest.importorskip("httpx", reason="httpx required for TestClient")
from fastapi.testclient import TestClient  # noqa: E402

import api.main as api_main  # noqa: E402

client = TestClient(api_main.app)


def test_doc_status_put_accepts_device_and_returns_line_and_rollup(monkeypatch):
    saved_writes = {}

    def fake_upsert(org_id, patient_hash, doc_type, status,
                    status_date=None, expiry_date=None, device_type=""):
        saved_writes[(patient_hash, doc_type, device_type)] = status
        return True

    def fake_load(org_id):
        # Echo the persisted write back, as a fresh load would.
        out = {}
        for (ph, dt, dev), status in saved_writes.items():
            out.setdefault(ph, {}).setdefault(dev, {})[dt] = {"status": status}
        return out

    monkeypatch.setattr(api_main, "get_or_create_org", lambda **kw: "org-test")
    monkeypatch.setattr(api_main, "upsert_doc_status", fake_upsert)
    monkeypatch.setattr(api_main, "load_doc_statuses_for_org", fake_load)
    monkeypatch.setattr(
        api_main, "load_confirmed_visits_for_org", lambda org: {}
    )

    resp = client.put(
        "/api/doc-status",
        json={
            "patient_id": "PT-API-1",
            "doc_type": "swo",
            "status": "on_file",
            "device_type": "dexcom_g7",
            "lines": [
                {
                    "device_type": "dexcom_g7",
                    "plan_type": "medicare",
                    "csv_visit_date": (TODAY - timedelta(days=20)).isoformat(),
                    "csv_pecos_verified": "Yes",
                    "csv_diagnosis_on_file": "Yes",
                },
                {
                    "device_type": "freestyle_libre_2",
                    "plan_type": "medicare",
                    "csv_visit_date": (TODAY - timedelta(days=20)).isoformat(),
                    "csv_pecos_verified": "Yes",
                    "csv_diagnosis_on_file": "Yes",
                },
            ],
        },
    )
    assert resp.status_code == 200
    body = resp.json()
    assert body["device_type"] == "dexcom_g7"
    by_dev = {l["device_type"]: l for l in body["lines"]}
    # The override satisfied G7's SWO; the Libre line is untouched (no leak).
    assert by_dev["dexcom_g7"]["line_status"] == "Clear to Ship"
    assert by_dev["freestyle_libre_2"]["line_status"] == "Action Needed"
    assert body["rollup_status"] == "Action Needed"  # worst line


def test_doc_status_put_without_lines_keeps_legacy_shape(monkeypatch):
    monkeypatch.setattr(api_main, "get_or_create_org", lambda **kw: "org-test")
    monkeypatch.setattr(
        api_main, "upsert_doc_status", lambda *a, **kw: True
    )
    resp = client.put(
        "/api/doc-status",
        json={"patient_id": "PT-API-2", "doc_type": "pa", "status": "approved"},
    )
    assert resp.status_code == 200
    body = resp.json()
    assert body["display"] == "Approved"
    assert "lines" not in body


def test_confirm_visit_with_lines_returns_rerolled_patient(monkeypatch):
    monkeypatch.setattr(api_main, "get_or_create_org", lambda **kw: "org-test")
    monkeypatch.setattr(
        api_main, "upsert_confirmed_visit", lambda *a, **kw: True
    )
    monkeypatch.setattr(
        api_main, "load_doc_statuses_for_org", lambda org: {}
    )
    ship = (TODAY - timedelta(days=5)).isoformat()
    resp = client.post(
        "/api/confirm-visit",
        json={
            "patient_id": "PT-API-3",
            "confirmed_date": (TODAY - timedelta(days=15)).isoformat(),
            "shipment_date": ship,
            "payer": "Medicare Part B",
            "device_type": "dexcom_g7",
            "quantity": 3,
            "csv_swo_status": "On File",
            "csv_pecos_verified": "Yes",
            "csv_diagnosis_on_file": "Yes",
            "plan_type": "medicare",
            "lines": [
                {
                    "device_type": "dexcom_g7",
                    "plan_type": "medicare",
                    "csv_swo_status": "On File",
                    "csv_pecos_verified": "Yes",
                    "csv_diagnosis_on_file": "Yes",
                },
                {
                    "device_type": "freestyle_libre_2",
                    "plan_type": "medicare",
                    "csv_swo_status": "On File",
                    "csv_pecos_verified": "Yes",
                    "csv_diagnosis_on_file": "Yes",
                },
            ],
        },
    )
    assert resp.status_code == 200
    body = resp.json()
    # Legacy record fields still present
    assert body["readiness_status"] == "Clear to Ship"
    # The confirmed visit fanned out to BOTH echoed lines
    assert all(l["line_status"] == "Clear to Ship" for l in body["lines"])
    assert body["rollup_status"] == "Clear to Ship"


def test_upload_grades_with_saved_device_scoped_override(monkeypatch):
    """An SWO override saved for device A feeds the verdict for A's line only."""
    pid_hash = hashlib.sha256("PT-UP-1".encode()).hexdigest()
    monkeypatch.setattr(
        api_main,
        "load_doc_statuses_for_org",
        lambda org: {
            pid_hash: {"dexcom_g7": {"swo": {"status": "on_file"}}}
        },
    )
    monkeypatch.setattr(api_main, "get_or_create_org", lambda **kw: "org-test")
    monkeypatch.setattr(
        api_main, "load_confirmed_visits_for_org", lambda org: {}
    )
    ship = (TODAY - timedelta(days=5)).isoformat()
    visit = (TODAY - timedelta(days=30)).isoformat()
    header = (
        "patient_id,device_type,shipment_date,quantity,payer,plan_type,"
        "visit_date,pecos_verified,diagnosis_on_file"
    )
    csv_text = (
        header
        + f"\nPT-UP-1,Dexcom G7,{ship},3,Medicare Part B,medicare,{visit},Yes,Yes"
        + f"\nPT-UP-1,Libre 2,{ship},1,Medicare Part B,medicare,{visit},Yes,Yes"
    )
    resp = client.post(
        "/api/upload",
        files={"file": ("f.csv", io.BytesIO(csv_text.encode()), "text/csv")},
    )
    assert resp.status_code == 200
    recs = {r["device_type"]: r for r in resp.json()["records"]}
    assert recs["dexcom_g7"]["readiness_status"] == "Clear to Ship"
    assert recs["freestyle_libre_2"]["readiness_status"] == "Action Needed"
    # Display follows the same device scope
    assert recs["dexcom_g7"]["doc_state"]["swo"] == "On File"
