"""Utilities for loading PA denial reason taxonomy."""

from __future__ import annotations

import csv
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Dict, Iterable, List, Mapping, Optional


@dataclass(frozen=True)
class PADenialReason:
    code: str
    category: str
    operator_text: str
    member_text: str
    provider_text: str
    ui_string: str
    policy_anchor_template: str
    evidence_slot: str
    severity: str

    def as_payload(self) -> Dict[str, str]:
        """Return a serialisable payload for receipts."""
        return {
            "code": self.code,
            "category": self.category,
            "operator_text": self.operator_text,
            "member_text": self.member_text,
            "provider_text": self.provider_text,
            "ui_string": self.ui_string,
            "policy_anchor_template": self.policy_anchor_template,
            "evidence_slot": self.evidence_slot,
            "severity": self.severity,
        }


_REQUIRED_FIELDS: tuple[str, ...] = (
    "code",
    "category",
    "operator_text",
    "member_text",
    "provider_text",
    "ui_string",
    "policy_anchor_template",
    "evidence_slot",
    "severity",
)


def _taxonomy_path() -> Path:
    root = Path(__file__).resolve().parents[1]
    return root / "docs" / "taxonomy" / "pa_denial_reasons.csv"


def _normalise_row(row: Mapping[str, str]) -> PADenialReason:
    missing = [field for field in _REQUIRED_FIELDS if not row.get(field)]
    if missing:
        missing_list = ", ".join(missing)
        code = row.get("code", "<unknown>")
        raise ValueError(f"pa_denial_reasons.csv entry {code} missing required fields: {missing_list}")
    return PADenialReason(
        code=row["code"].strip(),
        category=row["category"].strip(),
        operator_text=row["operator_text"].strip(),
        member_text=row["member_text"].strip(),
        provider_text=row["provider_text"].strip(),
        ui_string=row["ui_string"].strip(),
        policy_anchor_template=row["policy_anchor_template"].strip(),
        evidence_slot=row["evidence_slot"].strip(),
        severity=row["severity"].strip(),
    )


@lru_cache(maxsize=1)
def load_pa_denial_reasons() -> Dict[str, PADenialReason]:
    """Load PA denial reason taxonomy keyed by code."""
    path = _taxonomy_path()
    if not path.is_file():
        raise FileNotFoundError(f"PA denial taxonomy not found at {path}")

    reasons: Dict[str, PADenialReason] = {}
    with path.open("r", encoding="utf-8", newline="") as handle:
        reader = csv.DictReader(handle)
        for row in reader:
            reason = _normalise_row(row)
            reasons[reason.code] = reason
    return reasons


def get_pa_denial_reason(code: str) -> Optional[PADenialReason]:
    """Return a single denial reason by code."""
    return load_pa_denial_reasons().get(code)


def iter_pa_denial_reasons(codes: Optional[Iterable[str]] = None) -> List[PADenialReason]:
    """Return denial reasons filtered to the provided codes (or all if omitted)."""
    reasons = load_pa_denial_reasons()
    if codes is None:
        return list(reasons.values())
    return [reasons[code] for code in codes if code in reasons]
