"""Fail-closed authority for pre-activation nutrition readiness."""

from __future__ import annotations

from datetime import datetime
from pathlib import Path
from types import MappingProxyType

from checkin_cli.nutrition_readiness_contract import (
    CUSTOMER_KEY,
    DIGEST_PATTERN,
    KST,
    NUTRITION_READINESS_REASON_CODES,
    NutritionReadinessAudit,
    NutritionReadinessError,
)
from checkin_cli.nutrition_readiness_io import (
    canonical_digest,
    load_document,
    valid_digest,
)
from checkin_cli.nutrition_readiness_validators import (
    Validation,
    validate_baseline,
    validate_calculation,
    validate_clinical_clearance,
    validate_kb,
    validate_policy,
    validate_receipt,
    validate_reconciliation,
)

__all__ = [
    "NUTRITION_READINESS_REASON_CODES",
    "NutritionReadinessAudit",
    "NutritionReadinessError",
    "audit_nutrition_start_readiness",
    "require_nutrition_start_readiness",
]


def audit_nutrition_start_readiness(
    profile_root: Path | str,
    customer_key: str,
    *,
    now_kst: datetime | None = None,
    require_pointer: bool = True,
) -> NutritionReadinessAudit:
    root = Path(profile_root).expanduser().resolve()
    if CUSTOMER_KEY.fullmatch(customer_key) is None:
        raise NutritionReadinessError("nutrition readiness customer key is invalid")
    now = now_kst or datetime.now(KST)
    if now.tzinfo is None:
        raise NutritionReadinessError("nutrition readiness time must be timezone-aware")
    now = now.astimezone(KST)

    customer_root = root / "data" / "customers" / customer_key / "nutrition-onboarding"
    paths = {
        "restriction_kb": root
        / "data"
        / "global"
        / "nutrition-safety"
        / "restriction-kb-v1.json",
        "baseline": customer_root / "baseline-v1.json",
        "restriction_reconciliation": customer_root
        / "restriction-reconciliation-v1.json",
        "calculation": customer_root / "initial-plan-v1.json",
        "adjustment_policy": customer_root / "adjustment-policy-v1.json",
        "receipt": customer_root / "readiness-receipt-v1.json",
    }
    documents = {name: load_document(path, root) for name, path in paths.items()}
    validations: dict[str, Validation] = {}
    validations["restriction_kb"] = validate_kb(documents["restriction_kb"])
    validations["baseline"] = validate_baseline(documents["baseline"], customer_key)
    if (
        documents["baseline"] is not None
        and isinstance(
            documents["baseline"].get("clinical_clearance_receipt"),
            str,
        )
    ):
        clinical_document = load_document(
            customer_root / "clinical-review.json",
            root,
        )
        validations["clinical_clearance"] = validate_clinical_clearance(
            clinical_document,
            documents["baseline"],
        )
    validations["restriction_reconciliation"] = validate_reconciliation(
        documents["restriction_reconciliation"],
        documents["baseline"],
        documents["restriction_kb"],
    )
    validations["calculation"] = validate_calculation(
        documents["calculation"],
        documents["baseline"],
        documents["restriction_kb"],
    )
    validations["adjustment_policy"] = validate_policy(documents["adjustment_policy"])
    expected_receipt = {
        "baseline_digest": validations["baseline"].digest,
        "restriction_kb_digest": validations["restriction_kb"].digest,
        "restriction_reconciliation_digest": validations[
            "restriction_reconciliation"
        ].digest,
        "calculation_digest": validations["calculation"].digest,
        "adjustment_policy_digest": validations["adjustment_policy"].digest,
        "input_reconciliation_digest": (
            documents["baseline"].get("input_reconciliation_digest")
            if documents["baseline"] is not None
            else None
        ),
    }
    validations["receipt"] = validate_receipt(
        documents["receipt"],
        expected_receipt,
        now,
    )
    pointer = load_document(customer_root / "readiness-current.json", root)
    if pointer is None and require_pointer and documents["receipt"] is not None:
        validations["pointer"] = Validation(
            ("readiness_receipt_invalid",),
            None,
        )
    elif pointer is not None:
        pointer_fields = {
            "schema_version",
            "revision",
            "bundle_digest",
            "readiness_receipt_digest",
            "input_reconciliation_digest",
            "digest",
        }
        current_digests = {
            name: result.digest
            for name, result in validations.items()
            if result.digest is not None
        }
        input_reconciliation_digest = expected_receipt[
            "input_reconciliation_digest"
        ]
        if (
            isinstance(input_reconciliation_digest, str)
            and DIGEST_PATTERN.fullmatch(input_reconciliation_digest)
            is not None
        ):
            current_digests["input_reconciliation"] = (
                input_reconciliation_digest
            )
        pointer_valid = (
            set(pointer) == pointer_fields
            and pointer.get("schema_version") == "nutrition_readiness_pointer_v1"
            and pointer.get("revision") == 1
            and valid_digest(pointer)
            and pointer.get("bundle_digest") == canonical_digest(current_digests)
            and pointer.get("readiness_receipt_digest")
            == validations["receipt"].digest
            and pointer.get("input_reconciliation_digest")
            == expected_receipt["input_reconciliation_digest"]
        )
        validations["pointer"] = Validation(
            () if pointer_valid else ("readiness_receipt_invalid",),
            str(pointer["digest"]) if pointer_valid else None,
        )

    reasons = tuple(
        sorted({reason for result in validations.values() for reason in result.reasons})
    )
    checks = {name: not result.reasons for name, result in validations.items()}
    digests = {
        name: result.digest
        for name, result in validations.items()
        if result.digest is not None
    }
    input_reconciliation_digest = expected_receipt[
        "input_reconciliation_digest"
    ]
    if (
        isinstance(input_reconciliation_digest, str)
        and DIGEST_PATTERN.fullmatch(input_reconciliation_digest) is not None
    ):
        digests["input_reconciliation"] = input_reconciliation_digest
    return NutritionReadinessAudit(
        ready=not reasons and all(checks.values()),
        checks=MappingProxyType(checks),
        digests=MappingProxyType(digests),
        reason_codes=reasons,
    )


def require_nutrition_start_readiness(
    profile_root: Path | str,
    customer_key: str,
) -> NutritionReadinessAudit:
    audit = audit_nutrition_start_readiness(profile_root, customer_key)
    if not audit.ready:
        joined = ",".join(audit.reason_codes)
        raise NutritionReadinessError(f"nutrition readiness failed: {joined}")
    return audit
