"""
Tests for the dedup / grouping foundation of the readiness model.

Source design: docs/dedup-design-2026-06-24.md (Pi, locked by Kisa 2026-06-24)
              docs/readiness-model-brief-2026-06-23.md sections 3, 5, 7

Locked dedup key: (patient_id, device_type, date_of_service).
order_number is a displayed attribute, NEVER part of the dedup key.
"""

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

from datetime import date, timedelta

from core.coverage_calculator import ShipmentRecord
from core.dedup import (
    dedup,
    assign_to_coverage_lines,
    MergedShipment,
    CoverageLine,
    is_cgm_device,
)

DOS = date(2026, 6, 1)


def make_row(**kwargs):
    defaults = dict(
        patient_id="P1",
        device_type="dexcom_g7",
        shipment_date=DOS,
        quantity=3,
        payer="Medicare Part B",
        component="sensor",
        plan_type="medicare",
    )
    defaults.update(kwargs)
    return ShipmentRecord(**defaults)


# ---------------------------------------------------------------------------
# Dedup: grouping by (patient_id, device_type, DOS)
# ---------------------------------------------------------------------------

def test_dedup_component_split():
    """Two rows, same patient+device+DOS, different components -> one MergedShipment, summed qty."""
    rows = [
        make_row(quantity=3, component="sensor", order_number="ORD-100"),
        make_row(quantity=1, component="transmitter", order_number="ORD-101"),
    ]
    result = dedup(rows)
    assert len(result) == 1
    assert result[0].total_quantity == 4
    assert set(result[0].components) == {"sensor", "transmitter"}
    assert len(result[0].order_numbers) == 2


def test_dedup_identical_duplicate():
    """Two identical rows (same component) -> max quantity, not sum (duplicate guard)."""
    rows = [
        make_row(quantity=3, component="sensor"),
        make_row(quantity=3, component="sensor"),
    ]
    result = dedup(rows)
    assert len(result) == 1
    assert result[0].total_quantity == 3  # max within config, not 6


def test_dedup_mixed_duplicate_and_split():
    """Two duplicate sensor rows + one transmitter row -> max(sensor)+max(transmitter) = 4."""
    rows = [
        make_row(quantity=3, component="sensor"),
        make_row(quantity=3, component="sensor"),
        make_row(quantity=1, component="transmitter"),
    ]
    result = dedup(rows)
    assert result[0].total_quantity == 4


def test_dedup_zero_quantity_row_does_not_add():
    """qty=3 sensor + qty=0 sensor -> 3 (zero row is informational)."""
    rows = [
        make_row(quantity=3, component="sensor"),
        make_row(quantity=0, component="sensor"),
    ]
    result = dedup(rows)
    assert result[0].total_quantity == 3


def test_dedup_wildly_different_same_component_takes_max():
    """qty=3 + qty=100 same component -> max 100 (likely error, not a real split)."""
    rows = [
        make_row(quantity=3, component="sensor"),
        make_row(quantity=100, component="sensor"),
    ]
    result = dedup(rows)
    assert result[0].total_quantity == 100


def test_dedup_different_dos():
    """Same patient+device, different DOS -> two separate shipments."""
    rows = [
        make_row(shipment_date=DOS, quantity=3),
        make_row(shipment_date=DOS + timedelta(days=30), quantity=3),
    ]
    result = dedup(rows)
    assert len(result) == 2


def test_dedup_different_device():
    """Same patient+DOS, different device -> two separate shipments."""
    rows = [
        make_row(device_type="dexcom_g7", component="sensor"),
        make_row(device_type="omnipod_5", component="pod"),
    ]
    result = dedup(rows)
    assert len(result) == 2


def test_dedup_no_quantity_defaults_to_one():
    """Missing quantity -> default to 1 (a row without qty is still a shipment)."""
    rows = [make_row(quantity=None, component="sensor")]
    result = dedup(rows)
    assert result[0].total_quantity == 1


def test_dedup_missing_order_number():
    """No order_number -> order_numbers list is empty, no crash."""
    rows = [make_row(order_number=None)]
    result = dedup(rows)
    assert result[0].order_numbers == []


def test_dedup_order_numbers_collected_and_deduped():
    """Repeated order numbers across rows are collected once, in first-seen order."""
    rows = [
        make_row(component="sensor", order_number="ORD-100"),
        make_row(component="transmitter", order_number="ORD-100"),
    ]
    result = dedup(rows)
    assert result[0].order_numbers == ["ORD-100"]


def test_dedup_hcpcs_collected():
    """HCPCS codes collected across rows in the group."""
    rows = [
        make_row(component="sensor", hcpcs="A4239"),
        make_row(component="transmitter", hcpcs="E2103"),
    ]
    result = dedup(rows)
    assert set(result[0].hcpcs_codes) == {"A4239", "E2103"}


def test_dedup_payer_latest_non_null_wins():
    """payer is display-only; latest non-null row wins."""
    rows = [
        make_row(component="sensor", payer="Old Payer"),
        make_row(component="transmitter", payer="Medicare Part B"),
    ]
    result = dedup(rows)
    assert result[0].payer == "Medicare Part B"


def test_dedup_csv_field_latest_non_null_wins():
    """csv_swo_status: a later non-null value wins over an earlier null."""
    rows = [
        make_row(component="sensor", csv_swo_status=None),
        make_row(component="transmitter", csv_swo_status="On File"),
    ]
    result = dedup(rows)
    assert result[0].csv_swo_status == "On File"


def test_dedup_csv_field_earlier_non_null_kept_when_later_is_null():
    """csv_swo_status: an earlier non-null is kept when the later row is null."""
    rows = [
        make_row(component="sensor", csv_swo_status="On File"),
        make_row(component="transmitter", csv_swo_status=None),
    ]
    result = dedup(rows)
    assert result[0].csv_swo_status == "On File"


def test_dedup_single_row_passthrough():
    """A single row produces a MergedShipment carrying its fields."""
    rows = [make_row(quantity=2, component="sensor", order_number="ORD-1")]
    result = dedup(rows)
    assert len(result) == 1
    assert isinstance(result[0], MergedShipment)
    assert result[0].patient_id == "P1"
    assert result[0].device_type == "dexcom_g7"
    assert result[0].shipment_date == DOS
    assert result[0].total_quantity == 2
    assert result[0].plan_type == "medicare"


def test_dedup_plan_type_never_derived_from_payer():
    """plan_type carries the client-mapped value; None stays None even with a known payer name."""
    rows = [make_row(payer="Aetna PPO", plan_type=None)]
    result = dedup(rows)
    assert result[0].plan_type is None  # never guessed from "Aetna"


# ---------------------------------------------------------------------------
# CoverageLine assignment: group MergedShipments into gradeable lines
# ---------------------------------------------------------------------------

def test_is_cgm_device():
    assert is_cgm_device("dexcom_g7") is True
    assert is_cgm_device("freestyle_libre_3") is True
    assert is_cgm_device("omnipod_5") is False


def test_assign_groups_by_patient_device_plan():
    """One patient, one device, one plan -> one CoverageLine."""
    merged = dedup([
        make_row(shipment_date=DOS, quantity=3),
        make_row(shipment_date=DOS + timedelta(days=30), quantity=3),
    ])
    lines = assign_to_coverage_lines(merged)
    assert len(lines) == 1


def test_assign_latest_dos_is_current():
    """The latest DOS shipment is the current one; older ones are prior."""
    merged = dedup([
        make_row(shipment_date=DOS, quantity=3),
        make_row(shipment_date=DOS + timedelta(days=30), quantity=3),
    ])
    line = list(assign_to_coverage_lines(merged).values())[0]
    assert line.current_shipment.shipment_date == DOS + timedelta(days=30)
    assert len(line.prior_shipments) == 1
    assert line.prior_shipments[0].shipment_date == DOS


def test_assign_cgm_line_is_gradeable():
    merged = dedup([make_row(device_type="dexcom_g7", component="sensor")])
    line = list(assign_to_coverage_lines(merged).values())[0]
    assert line.gradeable is True


def test_assign_pump_line_not_gradeable():
    merged = dedup([make_row(device_type="omnipod_5", component="pod")])
    line = list(assign_to_coverage_lines(merged).values())[0]
    assert line.gradeable is False


def test_assign_different_plan_type_separate_lines():
    """Same patient+device, different mapped plan_type -> two CoverageLines (dual-eligible)."""
    merged = dedup([
        make_row(shipment_date=DOS, plan_type="medicare"),
        make_row(shipment_date=DOS + timedelta(days=30), plan_type="medicaid"),
    ])
    lines = assign_to_coverage_lines(merged)
    assert len(lines) == 2


def test_assign_none_plan_type_groups_as_unknown():
    """plan_type None is keyed as 'unknown', not guessed, and still forms a line."""
    merged = dedup([make_row(plan_type=None)])
    lines = assign_to_coverage_lines(merged)
    assert len(lines) == 1
    line = list(lines.values())[0]
    assert line.plan_type == "unknown"


def test_assign_starts_ungraded():
    """A freshly assigned line has no status yet (gradeLine runs later)."""
    merged = dedup([make_row()])
    line = list(assign_to_coverage_lines(merged).values())[0]
    assert line.line_status is None
    assert line.timing_flag is None


# ---------------------------------------------------------------------------
# Hardening (code-auditor findings M1, L1, L3 + edge cases)
# ---------------------------------------------------------------------------

def test_dedup_conflicting_plan_type_routes_to_unknown():
    """Two rows in one group with DIFFERENT non-null plan types -> never guess.

    The plan type is grading-critical and must never be silently picked. When a
    dedup group disagrees, route to None so the line keys as 'plan type needed'.
    """
    rows = [
        make_row(component="sensor", plan_type="medicare"),
        make_row(component="transmitter", plan_type="medicaid"),
    ]
    result = dedup(rows)
    assert len(result) == 1
    assert result[0].plan_type is None
    line = list(assign_to_coverage_lines(result).values())[0]
    assert line.plan_type == "unknown"


def test_dedup_agreeing_plan_type_preserved():
    """Same non-null plan type across rows is kept (no false conflict)."""
    rows = [
        make_row(component="sensor", plan_type="medicare"),
        make_row(component="transmitter", plan_type="medicare"),
    ]
    result = dedup(rows)
    assert result[0].plan_type == "medicare"


def test_dedup_empty_string_csv_field_does_not_clobber():
    """An empty-string csv value must not overwrite an earlier real value."""
    rows = [
        make_row(component="sensor", csv_swo_status="On File"),
        make_row(component="transmitter", csv_swo_status=""),
    ]
    result = dedup(rows)
    assert result[0].csv_swo_status == "On File"


def test_dedup_datetime_dos_coerced_to_date():
    """Same calendar day with different timestamps -> one group (timezone edge)."""
    from datetime import datetime
    rows = [
        make_row(shipment_date=datetime(2026, 6, 1, 9, 0), component="sensor"),
        make_row(shipment_date=datetime(2026, 6, 1, 17, 0), component="transmitter"),
    ]
    result = dedup(rows)
    assert len(result) == 1
    assert result[0].shipment_date == date(2026, 6, 1)


def test_dedup_empty_input():
    assert dedup([]) == []
    assert assign_to_coverage_lines([]) == {}


def test_cgm_device_types_exist_in_payer_rules():
    """Drift guard: every gradeable CGM device must exist in payer_rules.json,
    and the known pump must not be in the gradeable set."""
    import json
    rules_path = (
        Path(__file__).parent.parent
        / "python-backend" / "config" / "payer_rules.json"
    )
    devices = set(json.loads(rules_path.read_text())["devices"].keys())
    from core.dedup import CGM_DEVICE_TYPES
    missing = CGM_DEVICE_TYPES - devices
    assert not missing, f"CGM devices missing from payer_rules.json: {missing}"
    assert "omnipod_5" not in CGM_DEVICE_TYPES
