"""Sprint-5B Explorer registry and contract regressions."""
from __future__ import annotations

import copy
import sys
from pathlib import Path

import pytest

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

from dashboard_v5_fixture import build_dashboard_v5_fixture  # noqa: E402

from dashboard_v5.contracts import validate_bundle  # noqa: E402
from dashboard_v5.data_provider import build_bundle  # noqa: E402
from dashboard_v5.metric_registry import EXPLORER  # noqa: E402
from health_dashboard_v5 import generate  # noqa: E402
from test_health_dashboard_v5 import database  # noqa: E402


EXPECTED_PRESETS = [
    ("sleep_recovery", "Schlaf und Erholung", "baseline_index", ["apple.sleep", "apple.hrv", "apple.resting_heart_rate"]),
    ("heart_recovery", "Herz und Erholung", "baseline_index", ["apple.hrv", "apple.resting_heart_rate"]),
    ("activity_sleep", "Aktivität und Schlaf", "baseline_index", ["apple.steps", "apple.sleep"]),
    ("daily_symptoms", "Dokumentierte Symptome", "raw", ["symptom.total"]),
    ("histamine_documentation", "Dokumentierter Histamin-Zuordnungsindex", "raw", ["nutrition.histamine"]),
]


def test_sprint5b_exposes_exactly_five_allowlisted_presets() -> None:
    actual = [
        (preset["id"], preset["label"], preset["mode"], preset["metric_ids"])
        for preset in EXPLORER["presets"]
    ]
    assert actual == EXPECTED_PRESETS


def test_sprint5b_presets_remain_inside_strict_bundle_contract() -> None:
    bundle = build_bundle(database(), today="2026-06-15", generated_at="2026-06-15T12:00:00+00:00")
    assert len(bundle["explorer"]["presets"]) == 5
    validate_bundle(bundle)


def test_sprint5b_contract_rejects_client_supplied_registry_drift() -> None:
    bundle = build_bundle(database(), today="2026-06-15", generated_at="2026-06-15T12:00:00+00:00")
    missing_preset = copy.deepcopy(bundle)
    missing_preset["explorer"]["presets"].pop()
    with pytest.raises(ValueError, match="Explorer registry differs"):
        validate_bundle(missing_preset)

    relabelled_metric = copy.deepcopy(bundle)
    relabelled_metric["metrics"][0]["label"] = "Unregistered metric label"
    with pytest.raises(ValueError, match="metric registry differs"):
        validate_bundle(relabelled_metric)


def test_sprint5b_fixture_and_html_are_byte_deterministic(tmp_path: Path) -> None:
    first_db = tmp_path / "first.db"
    second_db = tmp_path / "second.db"
    build_dashboard_v5_fixture(first_db)
    build_dashboard_v5_fixture(second_db)
    assert first_db.read_bytes() == second_db.read_bytes()

    generated_at = "2026-06-15T12:00:00+00:00"
    first_html = tmp_path / "first.html"
    second_html = tmp_path / "second.html"
    generate(first_db, first_html, today="2026-06-15", generated_at=generated_at)
    generate(second_db, second_html, today="2026-06-15", generated_at=generated_at)
    assert first_html.read_bytes() == second_html.read_bytes()
