from __future__ import annotations

from datetime import date, datetime, timedelta
from pathlib import Path
from typing import cast

from checkin_cli.nutrition_onboarding_artifacts import (
    adjustment_policy_document,
    optional_float,
    registry_plan,
    registry_profile,
    write_artifact,
)
from checkin_cli.nutrition_onboarding_calculations import generate_initial_plan
from checkin_cli.nutrition_onboarding_contract import QUESTION_FIELDS, canonical_digest
from checkin_cli.nutrition_onboarding_fs import (
    read_private_json,
)
from checkin_cli.nutrition_onboarding_models import NutritionOnboardingBaseline
from checkin_cli.nutrition_onboarding_owner_risk import (
    OwnerRiskAcceptanceFinalizationContext,
    validate_owner_risk_acceptance_history,
)
from checkin_cli.nutrition_onboarding_projection import (
    apply_nutrition_onboarding_projection,
)
from checkin_cli.nutrition_readiness import audit_nutrition_start_readiness
from checkin_cli.nutrition_readiness_contract import DIGEST_PATTERN
from checkin_cli.nutrition_restriction_kb import (
    RestrictionKnowledgeBase,
    reconcile_restriction_terms,
    validate_restriction_kb_template,
)


def preflight_initial_plan(
    *,
    baseline_candidate_path: Path,
    starts_on: date,
) -> None:
    candidate = read_private_json(baseline_candidate_path)
    baseline_payload = candidate.get("baseline")
    if (
        not isinstance(baseline_payload, dict)
        or candidate.get("digest")
        != canonical_digest(baseline_payload)
    ):
        raise ValueError("onboarding baseline candidate is invalid")
    baseline = NutritionOnboardingBaseline.model_validate(
        baseline_payload,
    )
    generate_initial_plan(baseline, starts_on=starts_on)


def validate_clinical_clearance(
    path: Path,
    *,
    baseline_digest: str,
) -> dict[str, object]:
    document = read_private_json(path)
    if set(document) != {
        "schema_version",
        "cleared_for_nonmedical_coaching",
        "external_reference",
        "baseline_digest",
        "digest",
    }:
        raise ValueError("clinical clearance receipt is invalid")
    reference = document.get("external_reference")
    if (
        document.get("schema_version") != "1.0"
        or document.get("cleared_for_nonmedical_coaching") is not True
        or document.get("baseline_digest") != baseline_digest
        or not isinstance(reference, str)
        or not reference
        or len(reference) > 200
        or document.get("digest")
        != canonical_digest(
            {key: value for key, value in document.items() if key != "digest"}
        )
    ):
        raise ValueError("clinical clearance receipt is invalid")
    return document


def finalize_onboarding(
    *,
    profile_root: Path,
    customer_key: str,
    onboarding_root: Path,
    baseline_candidate_path: Path,
    starts_on: date,
    issued_at_kst: datetime,
    privacy_consent_digest: str,
    feature_epoch_digest: str,
    owner_risk_acceptance_receipts: object = None,
) -> dict[str, object]:
    if issued_at_kst.utcoffset() != timedelta(hours=9):
        raise ValueError("finalization timestamp must be KST")
    for field, value in (
        ("privacy_consent_digest", privacy_consent_digest),
        ("feature_epoch_digest", feature_epoch_digest),
    ):
        if len(value) != 64 or any(char not in "0123456789abcdef" for char in value):
            raise ValueError(f"{field} must be a lowercase SHA-256 digest")
    candidate = read_private_json(baseline_candidate_path)
    input_reconciliation_digest = candidate.get(
        "input_reconciliation_digest"
    )
    if (
        not isinstance(input_reconciliation_digest, str)
        or DIGEST_PATTERN.fullmatch(input_reconciliation_digest) is None
    ):
        raise ValueError("resolved input reconciliation digest is required")
    baseline_payload = candidate.get("baseline")
    if (
        not isinstance(baseline_payload, dict)
        or candidate.get("digest")
        != canonical_digest(baseline_payload)
    ):
        raise ValueError("onboarding baseline candidate is invalid")
    baseline = NutritionOnboardingBaseline.model_validate(
        baseline_payload,
    )
    kb_path = (
        profile_root
        / "data"
        / "global"
        / "nutrition-safety"
        / "restriction-kb-v1.json"
    )
    kb_runtime = read_private_json(kb_path)
    source_template = kb_runtime.get("source_template")
    if not isinstance(source_template, dict):
        raise ValueError("approved restriction KB source template is missing")
    kb: RestrictionKnowledgeBase = validate_restriction_kb_template(
        cast(dict[str, object], source_template),
        as_of=issued_at_kst.date(),
    )
    reconciliation = reconcile_restriction_terms(
        kb,
        {
            "allergies": list(baseline.allergies.items),
            "intolerances": list(baseline.intolerances.items),
            "religious_ethical_exclusions": list(
                baseline.religious_ethical_exclusions.items
            ),
            "conditions": list(baseline.conditions.items),
            "medications": list(baseline.medications.items),
        },
    )
    clinical_path = onboarding_root / "clinical-review.json"
    clinical_clearance = (
        validate_clinical_clearance(
            clinical_path,
            baseline_digest=str(candidate["digest"]),
        )
        if clinical_path.exists()
        else None
    )
    owner_risk_history = (
        validate_owner_risk_acceptance_history(
            owner_risk_acceptance_receipts,
            context=OwnerRiskAcceptanceFinalizationContext(
                customer_key=customer_key,
                baseline_digest=str(candidate["digest"]),
                reconciliation_digest=input_reconciliation_digest,
            ),
        )
        if owner_risk_acceptance_receipts is not None
        else []
    )
    if (
        reconciliation.unresolved
        or reconciliation.requires_human_review
        or baseline.pregnancy_breastfeeding is not False
        or baseline.eating_disorder_risk is not False
    ) and clinical_clearance is None and not owner_risk_history:
        raise ValueError("restriction or medical inputs require clinical review")
    plan = generate_initial_plan(baseline, starts_on=starts_on)
    timestamp = issued_at_kst.isoformat()
    owner_receipt = canonical_digest(
        {"customer_key": customer_key, "role": "owner", "baseline": candidate["digest"]}
    )
    baseline_doc = write_artifact(
        onboarding_root / "baseline-v1.json",
        {
            "schema_version": "2.0",
            "customer_key": customer_key,
            "source_baseline_digest": str(candidate["digest"]),
            "input_reconciliation_digest": input_reconciliation_digest,
            "adult_age": baseline.adult_age,
            "equation_sex_basis": baseline.equation_sex_basis.value,
            "height_cm": float(baseline.height_cm),
            "weight_kg": float(baseline.weight_kg),
            "activity_category": baseline.activity_category.value,
            "activity_rationale": baseline.activity_rationale,
            "goal_type": baseline.goal_type.value,
            "target_weight_kg": optional_float(baseline.target_weight_kg),
            "target_date": (
                baseline.target_date.isoformat() if baseline.target_date else None
            ),
            "maintenance_intent": baseline.goal_type.value == "maintain",
            "dietary_preferences": list(baseline.dietary_preferences.items),
            "disliked_foods": list(baseline.disliked_foods.items),
            "allergies": list(baseline.allergies.items),
            "intolerances": list(baseline.intolerances.items),
            "religious_ethical_exclusions": list(
                baseline.religious_ethical_exclusions.items
            ),
            "conditions": list(baseline.conditions.items),
            "medications": list(baseline.medications.items),
            "pregnancy_breastfeeding": baseline.pregnancy_breastfeeding,
            "cooking_access": baseline.cooking_access,
            "budget_band": baseline.budget_band,
            "meal_count": baseline.meal_count,
            "schedule_constraints": baseline.schedule_constraints,
            "customer_attested_at_kst": timestamp,
            "owner_review_receipt": owner_receipt,
                **(
                    {
                        "clinical_clearance_receipt": str(
                            clinical_clearance["digest"]
                        )
                    }
                    if clinical_clearance is not None
                    else {}
                ),
                **(
                    {
                        "owner_risk_acceptance_receipt": (
                            owner_risk_history[-1]
                        )
                    }
                    if owner_risk_history
                    else {}
                ),
        },
    )
    reconciliation_doc = write_artifact(
        onboarding_root / "restriction-reconciliation-v1.json",
        {
            "schema_version": "1.0",
            "baseline_digest": baseline_doc["digest"],
            "restriction_kb_digest": kb_runtime["digest"],
            "resolution_status": "resolved",
            "unresolved_hard_contraindications": [],
            "reviewed_by": owner_receipt,
            "reviewed_at_kst": timestamp,
        },
    )
    calculation_doc = write_artifact(
        onboarding_root / "initial-plan-v1.json",
        {
            "schema_version": "1.0",
            "method_id": plan.method_id,
            "method_version": plan.method_version,
            "baseline_digest": baseline_doc["digest"],
            "restriction_kb_digest": kb_runtime["digest"],
            "normalized_inputs_digest": candidate["digest"],
            "bmr_kcal": float(plan.bmr_kcal),
            "tdee_kcal": float(plan.tdee_kcal),
            "minimum_calories_kcal": 1500,
            "maximum_calories_kcal": 4500,
            "activity_assumption": baseline.activity_category.value,
            "goal_trajectory": baseline.goal_type.value,
            "goal_trajectory_safe": True,
            "safe_rate_guardrail": "loss<=1.0%;gain<=0.5%",
            "requested_target_weight_kg": optional_float(
                baseline.target_weight_kg
            ),
            "requested_target_date": (
                baseline.target_date.isoformat()
                if baseline.target_date is not None
                else None
            ),
            "requested_trajectory_within_guardrail": (
                plan.requested_trajectory_within_guardrail
            ),
            "recommended_target_date": (
                plan.recommended_target_date.isoformat()
                if plan.recommended_target_date is not None
                else None
            ),
            "weeks": [week.model_dump(mode="json") for week in plan.weeks],
            "reviewed_by": owner_receipt,
            "reviewed_at_kst": timestamp,
            "review_decision": "approved",
        },
    )
    policy_doc = write_artifact(
        onboarding_root / "adjustment-policy-v1.json",
        adjustment_policy_document(
            baseline,
            starts_on=starts_on,
            timestamp=timestamp,
            owner_receipt=owner_receipt,
        ),
    )
    receipt_doc = write_artifact(
        onboarding_root / "readiness-receipt-v1.json",
        {
            "schema_version": "2.0",
            "baseline_digest": baseline_doc["digest"],
            "restriction_kb_digest": kb_runtime["digest"],
            "restriction_reconciliation_digest": reconciliation_doc["digest"],
            "calculation_digest": calculation_doc["digest"],
            "adjustment_policy_digest": policy_doc["digest"],
            "input_reconciliation_digest": input_reconciliation_digest,
            "owner_review_receipt": owner_receipt,
            "privacy_consent_version": "privacy-v1",
            "privacy_consent_digest": privacy_consent_digest,
            "feature_epoch_digest": feature_epoch_digest,
            "delivery_enabled": False,
            "activation_enabled": False,
            "issued_at_kst": timestamp,
            "expires_at_kst": (issued_at_kst + timedelta(days=365)).isoformat(),
        },
    )
    audit = audit_nutrition_start_readiness(
        profile_root,
        customer_key,
        now_kst=issued_at_kst,
        require_pointer=False,
    )
    if not audit.ready:
        raise ValueError(f"readiness finalization failed: {','.join(audit.reason_codes)}")
    profile = registry_profile(baseline)
    projected_plan = registry_plan(plan, meal_count=baseline.meal_count)
    bundle_digest = canonical_digest(dict(audit.digests))
    apply_nutrition_onboarding_projection(
        profile_root,
        customer_key=customer_key,
        nutrition_profile=profile,
        plan=projected_plan,
        artifact_bundle_digest=bundle_digest,
        readiness_receipt_digest=str(receipt_doc["digest"]),
    )
    current = write_artifact(
        onboarding_root / "readiness-current.json",
        {
            "schema_version": "nutrition_readiness_pointer_v1",
            "revision": 1,
            "bundle_digest": bundle_digest,
            "readiness_receipt_digest": receipt_doc["digest"],
            "input_reconciliation_digest": input_reconciliation_digest,
        },
    )
    return {
        "state": "ready",
        "cursor": len(QUESTION_FIELDS),
        "answers": {},
        "baseline_digest": baseline_doc["digest"],
        "schema_version": "2.0",
        "review_flow": "owner_v1",
        "owner_reviewed": True,
        "readiness_pointer_digest": current["digest"],
        "input_reconciliation_digest": input_reconciliation_digest,
        "owner_risk_acceptance_receipts": owner_risk_history,
    }
