from __future__ import annotations

import json
from datetime import date
from pathlib import Path

import pytest

from checkin_cli import adaptive_nutrition


def _require_contract() -> None:
    if not hasattr(adaptive_nutrition, "customer_policy_from_onboarding_artifact"):
        pytest.fail("nutrition onboarding adaptive adapter missing")
    if not hasattr(adaptive_nutrition, "validate_onboarding_policy_compatibility"):
        pytest.fail("nutrition onboarding adaptive adapter missing")


def _artifact(**overrides: object) -> dict[str, object]:
    values: dict[str, object] = {
        "schema_version": "1.0",
        "policy_version": "1.0",
        "effective_at_kst": "2026-08-01T09:00:00+09:00",
        "effective_from": "2026-08-03",
        "goal_mode": "loss",
        "approved": True,
        "minimum_observation_days": 7,
        "weight_trend_method": "two_non_overlapping_seven_day_means",
        "adherence_inputs": ["calories", "meal_plan"],
        "actual_intake_inputs": ["calories"],
        "activity_change_inputs": ["training"],
        "adjustment_thresholds": {
            "minimum_current_samples": 4,
            "minimum_total_samples": 10,
            "minimum_adherent_days": 5,
            "goal_mode": "loss",
        },
        "calorie_step": 100,
        "maximum_step_kcal": 100,
        "calorie_floor": 1500,
        "calorie_ceiling": 4500,
        "cooldown_days": 7,
        "minimum_current_samples": 4,
        "minimum_total_samples": 10,
        "minimum_adherent_days": 5,
        "desired_weekly_change_min": "-1.00",
        "desired_weekly_change_max": "-0.25",
        "safety_hold": False,
        "escalation_rules": ["contradictory_adherence", "safety_hold"],
        "safety_stop_rules": ["medical_review", "eating_disorder_risk"],
        "manual_override_receipt_required": True,
    }
    values.update(overrides)
    return values


def test_adaptive_adapter_contract_is_present() -> None:
    _require_contract()


def test_artifact_maps_to_existing_customer_policy() -> None:
    _require_contract()
    policy = adaptive_nutrition.customer_policy_from_onboarding_artifact(
        _artifact(),
    )
    assert policy.starts_on == date(2026, 8, 3)
    assert policy.goal_mode == "fat_loss"
    assert policy.calorie_step == 100
    assert policy.calorie_floor == 1500
    assert policy.calorie_ceiling == 4500
    assert policy.cooldown_days == 7


@pytest.mark.parametrize(
    "field,value,match",
    (
        ("calorie_step", 50, "100"),
        ("cooldown_days", 6, "7"),
        ("calorie_floor", 1499, "1500"),
        ("calorie_ceiling", 4501, "4500"),
        ("minimum_current_samples", 3, "current"),
        ("minimum_total_samples", 9, "total"),
        ("minimum_adherent_days", 4, "adherent"),
    ),
)
def test_incompatible_policy_is_rejected(
    field: str,
    value: object,
    match: str,
) -> None:
    _require_contract()
    with pytest.raises(ValueError, match=match):
        adaptive_nutrition.validate_onboarding_policy_compatibility(
            _artifact(**{field: value}),
        )


def test_safety_hold_requires_human_review() -> None:
    _require_contract()
    result = adaptive_nutrition.validate_onboarding_policy_compatibility(
        _artifact(safety_hold=True),
    )
    assert result.requires_human_review is True
    assert result.may_propose is False
    with pytest.raises(ValueError, match="safety hold"):
        adaptive_nutrition.customer_policy_from_onboarding_artifact(
            _artifact(safety_hold=True),
        )


@pytest.mark.parametrize(
    "overrides,match",
    (
        ({"schema_version": "2.0"}, "schema"),
        ({"policy_version": "2.0"}, "version"),
        ({"approved": False}, "approved"),
        ({"goal_mode": "bulk"}, "goal"),
        ({"calorie_step": True}, "100"),
        ({"maximum_step_kcal": 200}, "maximum"),
        ({"desired_weekly_change_min": "NaN"}, "rate"),
        ({"adjustment_thresholds": {"goal_mode": "gain"}}, "threshold"),
        ({"escalation_rules": ["manual-review"]}, "escalation"),
        ({"safety_stop_rules": ["manual-review"]}, "safety stop"),
    ),
)
def test_production_adapter_rejects_untrusted_policy_values(
    overrides: dict[str, object],
    match: str,
) -> None:
    with pytest.raises(ValueError, match=match):
        adaptive_nutrition.customer_policy_from_onboarding_artifact(
            _artifact(**overrides),
        )


def test_adapter_is_pure_and_does_not_enable_runtime_flags() -> None:
    _require_contract()
    artifact = _artifact()
    original = dict(artifact)
    adaptive_nutrition.customer_policy_from_onboarding_artifact(artifact)
    assert artifact == original
    assert "activation" not in artifact
    assert "delivery" not in artifact


def test_approved_loader_dispatches_promoted_onboarding_policy(
    tmp_path: Path,
) -> None:
    adaptive_nutrition.initialize_adaptive_customer(tmp_path)
    root = tmp_path / "nutrition-plans"
    policy_value = _artifact()
    constraints_value = {
        "meal_count": 1,
        "budget_tier": "standard",
        "cooking_access": "home",
    }
    catalog_value = [
        {
            "food_id": "safe",
            "label": "safe",
            "calories": 600,
            "carbs_g": 70,
            "protein_g": 40,
            "fat_g": 18,
        }
    ]
    for filename, key, value in (
        ("policy.json", "policy", policy_value),
        ("meal-constraints.json", "meal_constraints", constraints_value),
        ("food-catalog.json", "catalog", catalog_value),
    ):
        path = root / filename
        path.write_text(
            json.dumps(
                {
                    "version": "1.0",
                    "digest": adaptive_nutrition.digest(value),
                    "approved": True,
                    "approved_by": {
                        "user_id": "1",
                        "chat_id": "-100",
                        "topic_id": "10",
                    },
                    "approved_at_kst": "2026-08-01T09:00:00+09:00",
                    key: value,
                }
            ),
            encoding="utf-8",
        )
        path.chmod(0o600)

    artifacts = adaptive_nutrition.load_approved_adaptive_artifacts(tmp_path)

    assert artifacts.policy.goal_mode == "fat_loss"
    assert artifacts.policy.starts_on == date(2026, 8, 3)
    assert artifacts.policy_digest == adaptive_nutrition.digest(policy_value)
