from __future__ import annotations

import importlib
import sys
from datetime import date
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts" / "health"))

from dashboard_v5.nutrition_references import build_overview, resolve_profile  # noqa: E402


ON_DATE = date(2026, 7, 22)
PROFILE = {
    "birth_date": "1986-01-01",
    "sex": "male",
    "activity_level": "moderate",
    "body_weight_kg": 80,
}


def _overview(
    nutrients,
    *,
    profile=PROFILE,
    documented_days=1,
    energy=2400,
    on_date=ON_DATE,
    period_from="2026-07-22",
    contributions=None,
):
    return build_overview(
        on_date=on_date,
        profile=profile,
        nutrients=nutrients,
        energy_kcal=energy,
        documented_days=documented_days,
        period_from=period_from,
        period_to=on_date.isoformat(),
        contributions=contributions,
    )


def _row(overview, key):
    return next(item for item in overview["rows"] if item["key"] == key)


def test_blv_catalog_is_complete_grouped_local_and_profile_specific() -> None:
    overview = _overview({})
    assert len(overview["rows"]) == 41
    assert sum(item["group"] == "vitamin" for item in overview["rows"]) == 13
    assert sum(item["group"] == "mineral" for item in overview["rows"]) == 14
    assert overview["profile"]["reference_group"] == "18–65 Jahre, männlich"
    assert overview["source"]["runtime_external_requests"] is False
    assert "http" not in str(overview).casefold()
    assert resolve_profile({"birth_date": "1961-07-22", "sex": "male"}, ON_DATE).group == "18years"
    assert resolve_profile({"birth_date": "1960-07-22", "sex": "male"}, ON_DATE).group == "65years"


def test_profile_life_stage_and_period_group_boundaries_fail_closed() -> None:
    female = {"birth_date": "1986-01-01", "sex": "female"}
    assert resolve_profile(female, ON_DATE).missing == ("life_stage",)
    assert resolve_profile({**female, "life_stage": "none"}, ON_DATE).status == "resolved"

    boundary_profile = {"birth_date": "1960-07-30", "sex": "male"}
    overview = _overview(
        {},
        profile=boundary_profile,
        on_date=date(2026, 8, 5),
        period_from="2026-07-20",
        documented_days=2,
    )
    assert overview["profile"]["status"] == "period_group_boundary"
    assert all(row["percentage"] is None for row in overview["rows"])


def test_vitamin_a_semantic_unit_and_contribution_denominator_are_conservative() -> None:
    overview = _overview(
        {
            "vitamin.a": {"food_value": 700, "item_count": 2, "unknown_item_count": 0, "documented_days": 2},
            "vitamin.c": {"food_value": 100, "item_count": 2, "unknown_item_count": 0, "documented_days": 2},
        },
        documented_days=2,
        period_from="2026-07-21",
        contributions={"vitamin.c": [{"food": "Paprika", "amount": 180, "unit": "mg"}]},
    )
    assert _row(overview, "vitamin.a")["reference"]["kind"] == "not_determinable"
    assert _row(overview, "vitamin.a")["percentage"] is None
    assert _row(overview, "vitamin.c")["food_contributions"][0]["amount"] == 90
    assert _row(overview, "vitamin.c")["reference"]["source_record"]["snapshot_file"] == "18years-both.csv"


def test_reference_math_keeps_values_above_100_and_ranges() -> None:
    overview = _overview(
        {
            "energy.energy": {"food_value": 2400, "item_count": 1, "unknown_item_count": 0},
            "vitamin.c": {"food_value": 150.7, "item_count": 1, "unknown_item_count": 0},
        }
    )
    vitamin_c = _row(overview, "vitamin.c")
    assert vitamin_c["percentage"] == 137.0
    assert vitamin_c["interpretation"] == "über dem Referenzwert"
    assert vitamin_c["reference"]["upper_safety_limit"] is None
    energy = _row(overview, "energy.energy")
    assert energy["percentage"] is None
    assert energy["percentage_range"] == {"from": 89.8, "to": 104.1}
    assert energy["interpretation"] == "Referenzwert erreicht"
    assert energy["alternate_amount"] == pytest.approx(10041.6)
    assert energy["alternate_unit"] == "kJ"


def test_missing_profile_and_unknown_food_values_are_never_zero_or_precise() -> None:
    missing = _overview({}, profile={})
    assert missing["profile"]["status"] == "missing_required"
    assert set(missing["profile"]["missing_fields"]) == {"birth_date", "sex"}
    assert all(item["documented_amount"] is None for item in missing["rows"])
    assert all(item["percentage"] is None for item in missing["rows"])

    partial = _overview(
        {"vitamin.c": {"food_value": 150.7, "item_count": 2, "unknown_item_count": 1}}
    )
    vitamin_c = _row(partial, "vitamin.c")
    assert vitamin_c["data_completeness"]["status"] == "partial_unknown"
    assert vitamin_c["data_completeness"]["message"] == "Wert teilweise unbekannt"
    assert vitamin_c["percentage"] is None
    assert vitamin_c["interpretation"] == "nicht beurteilbar – Daten unvollständig"


def test_period_coverage_uses_documented_days_not_implicit_zero_days() -> None:
    overview = _overview(
        {
            "vitamin.c": {
                "food_value": 110,
                "documented_days": 1,
                "item_count": 1,
                "unknown_item_count": 0,
            }
        },
        documented_days=2,
    )
    row = _row(overview, "vitamin.c")
    assert row["documented_amount"] == 110
    assert row["data_completeness"]["documented_days"] == 1
    assert row["data_completeness"]["period_documented_days"] == 2
    assert row["data_completeness"]["unknown_days"] == 1
    assert row["percentage"] is None


def test_total_sugar_is_not_compared_to_free_sugar() -> None:
    overview = _overview(
        {"nutrient.sugar": {"food_value": 20, "item_count": 1, "unknown_item_count": 0}}
    )
    sugar = _row(overview, "nutrient.sugar")
    assert sugar["percentage"] is None
    assert "Gesamtzucker" in sugar["reference"]["reason"]


def test_pregnancy_rules_require_age_and_context_and_parse_decimal_comma() -> None:
    assert resolve_profile({"sex": "female", "life_stage": "pregnant"}, ON_DATE).status == "missing_required"
    profile = {
        "birth_date": "1986-01-01",
        "sex": "female",
        "life_stage": "pregnant",
        "activity_level": "moderate",
        "body_weight_kg": 70,
        "pregnancy_trimester": 2,
        "phytate_mg_per_day": 600,
    }
    overview = _overview(
        {
            "energy.energy": {"food_value": 2300, "item_count": 1, "unknown_item_count": 0},
            "nutrient.protein": {"food_value": 67.1, "item_count": 1, "unknown_item_count": 0},
            "mineral.zinc": {"food_value": 10.9, "item_count": 1, "unknown_item_count": 0},
        },
        profile=profile,
        energy=2300,
    )
    assert _row(overview, "energy.energy")["reference"]["kind"] == "not_determinable"
    assert _row(overview, "nutrient.protein")["reference"]["minimum"] == pytest.approx(67.1)
    assert _row(overview, "mineral.zinc")["reference"]["target"] == pytest.approx(10.9)


def test_pending_mapping_and_symptom_actions_are_idempotent(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("HEALTH_DASHBOARD_FILE", str(tmp_path / "v4.html"))
    server = importlib.import_module("health_dashboard_server")
    monkeypatch.setattr(server, "ACTION_INBOX", tmp_path / "inbox")
    mapping = {
        "version": 1,
        "action": "nutrition_mapping",
        "queue_key": "a" * 32,
        "decision": "unknown",
        "sighi_food_key": "",
        "personal_status": "unknown",
        "personal_notes": "",
    }
    first = server.write_action_payload(mapping)
    second = server.write_action_payload(mapping)
    assert first == second
    assert len(list((tmp_path / "inbox").glob("*.json"))) == 1
    conflicting = {**mapping, "decision": "ignore"}
    with pytest.raises(server.IdempotencyConflictError):
        server.write_action_payload(conflicting)
    pending = tmp_path / "inbox" / f"{first}.json"
    pending.unlink()
    receipts = tmp_path / "inbox" / "receipts"
    receipts.mkdir(mode=0o700)
    (receipts / f"{first}.json").write_text('{"status":"processed"}', encoding="utf-8")
    assert server.write_action_payload(mapping) == first
    assert not pending.exists()

    symptom = {
        "version": 1,
        "action": "symptom_checkin",
        "date": "2026-07-22",
        "scores": {field: 0 for field in server.SYMPTOM_FIELDS},
        "notes": "",
    }
    symptom_first = server.write_action_payload(symptom)
    symptom_second = server.write_action_payload(symptom)
    assert symptom_first == symptom_second
    assert len(list((tmp_path / "inbox").glob("*.json"))) == 1
