"""
overrides.py
Signal — STTIL Solutions

Device-keyed staff overrides (build plan 02, P4; phase-2 design Item 4).

Pilot scope is LOCKED to two override kinds:
- visit: patient-scoped (the visit is the person). It rides the existing
  confirmed_visits path, which already fans out to every line of the patient
  by construction (evaluate_readiness receives the confirmed date per
  patient). This module does not duplicate that mechanism.
- swo / pa: line-scoped. They apply only to lines whose device_type matches;
  device_type '' marks a legacy patient-scoped row and matches all devices
  until staff re-saves it device-scoped.

Overrides feed the GRADING inputs (staff knowledge wins over the CSV cell,
matching the existing display precedence), then affected lines re-grade.

PHI CONTRACT: overrides carry patient_id_hash only (the storage key). Lines
carry raw patient_id; matching hashes it. Nothing here logs patient data.
"""

from __future__ import annotations

import hashlib
from dataclasses import dataclass
from datetime import date, datetime
from typing import Optional

# Stored status -> display/grading label. Single source of truth; the API's
# display map imports this. The labels feed the readiness vocabularies
# ("On File" grades GOOD, "Denied" grades BAD, "Requested" grades PENDING).
STORED_STATUS_LABELS = {
    "pending": "Pending",
    "requested": "Requested",
    "on_file": "On File",
    "not_required": "Not Required",
    "approved": "Approved",
    "denied": "Denied",
}

# Legacy patient-scoped rows carry an empty device_type and match all devices.
LEGACY_ALL_DEVICES = ""

OVERRIDABLE_DOC_TYPES = ("swo", "pa")


@dataclass(frozen=True)
class OverrideKey:
    org_id: str
    patient_hash: str
    device_type: str  # '' = legacy patient-scoped, applies to all devices
    doc_type: str  # "swo" | "pa" (visit rides confirmed_visits)


@dataclass
class Override:
    key: OverrideKey
    status: str  # stored value, e.g. "on_file" | "approved" | "denied"
    status_date: Optional[date] = None
    expiry_date: Optional[date] = None
    confirmed_by: str = "staff"
    confirmed_at: Optional[datetime] = None


def _hash_pid(patient_id: str) -> str:
    return hashlib.sha256(patient_id.encode()).hexdigest()


def grading_value(override: Override) -> str:
    """The label fed to the readiness vocabularies for this override."""
    return STORED_STATUS_LABELS.get(override.status, override.status)


def overrides_from_saved(saved: dict) -> list[Override]:
    """Build Override objects from the persistence load shape:
    patient_hash -> device_type -> doc_type -> {status, status_date, ...}."""
    out: list[Override] = []
    for patient_hash, by_device in (saved or {}).items():
        for device_type, by_doc in by_device.items():
            for doc_type, row in by_doc.items():
                if doc_type not in OVERRIDABLE_DOC_TYPES:
                    continue
                out.append(
                    Override(
                        key=OverrideKey(
                            org_id="",
                            patient_hash=patient_hash,
                            device_type=device_type,
                            doc_type=doc_type,
                        ),
                        status=row.get("status", ""),
                    )
                )
    return out


def apply_overrides(lines: dict, overrides: list[Override]) -> set[str]:
    """Apply swo/pa overrides onto the grading inputs of matching lines.

    Mutates each matching line's current_shipment csv fields (staff knowledge
    wins over the CSV cell). Device-scoped rows win over legacy '' rows.
    Returns the raw patient_ids of touched lines. Grading (or re-grading)
    happens in the caller — build_readiness_index grades after application.
    """
    if not overrides:
        return set()

    by_patient: dict[str, list[Override]] = {}
    for o in overrides:
        by_patient.setdefault(o.key.patient_hash, []).append(o)

    touched: set[str] = set()
    for line in lines.values():
        patient_overrides = by_patient.get(_hash_pid(line.patient_id))
        if not patient_overrides:
            continue
        # Legacy '' first so a device-scoped row applied after it wins.
        for o in sorted(
            patient_overrides, key=lambda o: o.key.device_type != LEGACY_ALL_DEVICES
        ):
            if o.key.device_type not in (LEGACY_ALL_DEVICES, line.device_type):
                continue
            label = grading_value(o)
            if o.key.doc_type == "swo":
                line.current_shipment.csv_swo_status = label
            elif o.key.doc_type == "pa":
                line.current_shipment.csv_pa_status = label
            touched.add(line.patient_id)
    return touched
