"""Privacy-minimizing, deterministic historical-baseline extraction tests."""

from __future__ import annotations

import json
from pathlib import Path

from checkin_cli.history_baseline import HistoryBaselineParser, SourceInventory


ATTACHMENTS = Path("/home/cube/.codex/attachments/6f30b58c-f695-4a47-a27f-f964394a371e")
PROFILE = Path("/home/cube/.hermes/profiles/physique-coach")


def _parse_actual_sources() -> tuple[HistoryBaselineParser, object]:
    """Parse only the three immutable sources enumerated by the profile manifest."""
    inventory = SourceInventory.from_manifest(PROFILE / "data" / "imports" / "raw-history-manifest.json")
    parser = HistoryBaselineParser(inventory)
    return parser, parser.parse()


def test_extracts_only_observed_d21_through_d123_without_inventing_d1_to_d20() -> None:
    # Given: the exact immutable attachment inventory.
    _parser, baseline = _parse_actual_sources()

    # When: a canonical daily baseline is derived.
    observed_days = {entry.day for entry in baseline.entries}

    # Then: the coverage begins at the first actual daily record, never the filename label.
    assert min(observed_days) == 21
    assert max(observed_days) == 123
    assert not (set(range(1, 21)) & observed_days)
    assert baseline.coverage.first_observed_day == 21
    assert baseline.coverage.missing_days[:20] == tuple(range(1, 21))
    assert {100, 101, 102, 119, 120, 121, 122}.issubset(baseline.coverage.days_without_measurements)


def test_derives_only_deterministic_metrics_and_canonical_status_tokens() -> None:
    # Given: the D50 historical heading has explicit numerical fields.
    _parser, baseline = _parse_actual_sources()

    # When: its canonical record is selected.
    entry = next(item for item in baseline.entries if item.day == 50)

    # Then: only permitted structured values are retained, without narrative text.
    assert entry.recorded_date == "2026-04-14"
    assert entry.weight_kg == 68.16
    assert entry.sleep_hours == 7.0
    assert entry.calories_kcal == 2400
    assert entry.training_tokens == ("pull",)
    assert entry.digestion_status == "normal"
    assert entry.pain_tokens == ()

    # And: an explicitly labelled pain report stays a category, not copied prose.
    d113 = next(item for item in baseline.entries if item.day == 113)
    assert d113.pain_tokens == ("anterior_tibial", "knee")


def test_leaves_ambiguous_ranges_and_unrecorded_fields_missing() -> None:
    # Given: D52 has an ambiguous calorie expression and D100 has no measurement record.
    _parser, baseline = _parse_actual_sources()

    # When: records are parsed.
    d52 = next(item for item in baseline.entries if item.day == 52)
    d100 = next(item for item in baseline.entries if item.day == 100)

    # Then: it declines to guess either value.
    assert d52.calories_kcal is None
    assert d100.weight_kg is None
    assert d100.sleep_hours is None
    assert d100.calories_kcal is None


def test_reports_duplicate_day_anchors_without_silently_merging_conflicts() -> None:
    # Given: overlapping D38-D44 and repeated summary sections in the source set.
    _parser, baseline = _parse_actual_sources()

    # When: reconciliation is produced.
    duplicate_days = {item.day for item in baseline.reconciliation.duplicate_days}

    # Then: duplicate anchors are explicitly surfaced for audit.
    assert {38, 44, 86, 123}.issubset(duplicate_days)
    d38 = next(item for item in baseline.reconciliation.duplicate_days if item.day == 38)
    assert len(d38.source_anchors) >= 2
    assert all(anchor.startswith("D+") for anchor in d38.source_anchors)


def test_serialized_baseline_never_contains_free_text_checkin_content() -> None:
    # Given: extracted structured data from the real historical source set.
    _parser, baseline = _parse_actual_sources()

    # When: it is serialized for profile-local storage.
    serialized = json.dumps(baseline.to_storage_dict(), ensure_ascii=False, sort_keys=True)

    # Then: known narrative fragments and raw section text cannot leak into the baseline.
    assert "화장실" not in serialized
    assert "특이사항" not in serialized
    assert "가족 외식" not in serialized
    assert "D+50 — 2026-04-14" not in serialized
    assert "D+50@2026-04-14" in serialized
