"""Historical provenance, baseline, and interprocess import tests."""

from __future__ import annotations

import json
from datetime import date
from multiprocessing import get_context
from pathlib import Path

from checkin_cli.adaptive_nutrition import project_canonical_events
from checkin_cli.history_imports import baseline_events, parse_baseline
from checkin_cli.models import (
    ContractCheckin,
    ContractStatus,
    Event,
    EventType,
    ImportManifest,
    Provenance,
    build_schedule_confirmation_event,
    build_schedule_reference_event,
    validate_event,
)
from checkin_cli.store import EventStore
from tests._support import invoke


MANIFEST = Path("/home/cube/.hermes/profiles/physique-coach/data/imports/raw-history-manifest.json")


def _import_history_in_process(home: str, source: str, barrier: object) -> None:
    """Import the same source after every local worker reaches one start gate."""
    barrier.wait()
    EventStore.for_standalone(Path(home)).import_history(Path(source), "D1")


def _import_baseline_in_process(home: str, manifest: str, barrier: object) -> None:
    """Import one approved baseline after every local worker reaches one start gate."""
    barrier.wait()
    EventStore.for_standalone(Path(home)).import_baseline(Path(manifest))


def test_imports_one_history_event_when_32_processes_import_the_same_anchor(tmp_path: Path) -> None:
    # Given: one source heading and thirty-two independent local import workers.
    home = tmp_path / "coach-data"
    history = tmp_path / "D1.md"
    history.write_text("## D+1 — 2026-01-01\\nprivate source text\\n", encoding="utf-8")
    context = get_context("fork")
    barrier = context.Barrier(32)
    workers = [context.Process(target=_import_history_in_process, args=(str(home), str(history), barrier)) for _ in range(32)]

    # When: every worker imports the same historical anchor at the same time.
    for worker in workers:
        worker.start()
    for worker in workers:
        worker.join()

    # Then: one immutable history event survives the transaction race.
    assert all(worker.exitcode == 0 for worker in workers)
    events = (home / "events.jsonl").read_text(encoding="utf-8").splitlines()
    assert len(events) == 1
    assert json.loads(events[0])["event_type"] == "history_imported"


def test_imports_one_dated_baseline_per_observed_day_when_32_processes_run(tmp_path: Path) -> None:
    # Given: the verified source manifest and thirty-two concurrent import workers.
    home = tmp_path / "coach-data"
    context = get_context("fork")
    barrier = context.Barrier(32)
    workers = [context.Process(target=_import_baseline_in_process, args=(str(home), str(MANIFEST), barrier)) for _ in range(32)]

    # When: all workers import the same approved D1-D123 source inventory.
    for worker in workers:
        worker.start()
    for worker in workers:
        worker.join()

    # Then: dated partial events are unique, while live current state stays empty.
    assert all(worker.exitcode == 0 for worker in workers)
    events = [json.loads(line) for line in (home / "events.jsonl").read_text(encoding="utf-8").splitlines()]
    assert len(events) == 103
    assert len({event["event_id"] for event in events}) == 103
    assert all(event["status"] == "archived" for event in events)
    assert all(event["occurred_at_kst"].endswith("T08:00:00+09:00") for event in events)
    assert json.loads((home / "views" / "current.json").read_text())["eligible_checkins"] == []
    historical = json.loads((home / "views" / "historical-baseline.json").read_text())
    assert historical["coverage"]["first_observed_day"] == 21
    assert historical["coverage"]["missing_days"][:20] == list(range(1, 21))


def test_imports_only_hashed_provenance_for_history(tmp_path: Path) -> None:
    # Given: an immutable history file with stable day headings.
    history = tmp_path / "D1-D2.md"
    history.write_text("## D+1 — 2026-01-01\nsecret text\n## D+2 — 2026-01-02\nmore text\n")
    home = tmp_path / "coach-data"

    # When: history import runs.
    response = invoke(home, "import-history", "--source", str(history), "--range-label", "D1-D2")

    # Then: manifest/events retain hashes and anchors, never source body text.
    assert response["outcome"] == "imported"
    manifest = (home / "imports" / "history-import-manifest.json").read_text()
    events = (home / "events.jsonl").read_text()
    assert "secret text" not in manifest + events
    assert "D+1" in events
    assert (home / "imports" / "history-import-manifest.json").stat().st_mode & 0o777 == 0o600


def test_import_baseline_command_creates_dated_history_view(tmp_path: Path) -> None:
    # Given: the exact profile-approved immutable source manifest.
    home = tmp_path / "coach-data"

    # When: the public CLI imports the verified baseline.
    response = invoke(home, "import-baseline", "--manifest", str(MANIFEST))

    # Then: it reports observed records and materializes a dated, coverage-aware view.
    assert response == {"outcome": "baseline_imported", "event_count": 103}
    view = json.loads((home / "views" / "historical-baseline.json").read_text())
    assert view["coverage"]["first_observed_day"] == 21
    assert view["coverage"]["missing_days"] == list(range(1, 21))
    assert view["observations"][0]["date"] == "2026-03-16"
    assert view["weekly_weight_trend"][0]["week_start"] == "2026-04-13"
    assert view["weekly_weight_trend"][0]["sample_count"] >= 1
def test_schema2_history_writer_preserves_schema1_rows_and_projects_by_source_day() -> None:
    # Given: one immutable schema-1 row and a future schema-2 source-day mapping.
    legacy = Event(
        event_id="history_legacy01",
        event_type=EventType.HISTORY_IMPORTED,
        schema_version="1.0",
        occurred_at_kst="2026-01-01T08:00:00+09:00",
        recorded_at_kst="2026-01-02T08:00:00+09:00",
        provenance=Provenance(
            source_type="historical_markdown",
            source_ref="history.md",
            content_sha256="a" * 64,
        ),
        status=ContractStatus.ARCHIVED,
        check_in=ContractCheckin(body_weight_kg=80),
    )
    root = Event(
        event_id="history_schema02",
        event_type=EventType.HISTORY_IMPORTED,
        schema_version="2.0",
        occurred_at_kst="2026-01-01T08:00:00+09:00",
        recorded_at_kst="2026-01-02T08:00:00+09:00",
        provenance=Provenance(
            source_type="historical_markdown",
            source_ref="history.md",
            content_sha256="b" * 64,
        ),
        status=ContractStatus.ARCHIVED,
        check_in=ContractCheckin(body_weight_kg=80),
        import_manifest=ImportManifest(observation_kst_day=date(2026, 1, 1)),
    )
    correction = Event(
        event_id="history_correct01",
        event_type=EventType.CORRECTION,
        occurred_at_kst="2026-12-31T08:00:00+09:00",
        recorded_at_kst="2026-12-31T08:00:00+09:00",
        provenance=Provenance(source_type="manual", source_ref="operator", content_sha256="c" * 64),
        status=ContractStatus.ACCEPTED,
        supersedes=root.event_id,
        check_in=ContractCheckin(body_weight_kg=79),
    )

    # Then: schema-1 stays manifest-free, while schema-2 validates and keeps its root day.
    assert legacy.import_manifest is None
    assert validate_event(root).import_manifest == root.import_manifest
    snapshot = project_canonical_events([root, correction], date(2026, 1, 1), date(2026, 1, 1))
    assert snapshot.current_samples == 1
    assert snapshot.current_mean_kg == 79


def test_schedule_events_validate_against_the_canonical_schema() -> None:
    # Given: each schedule event form with its required payload identity.
    digest = "a" * 64
    reference = build_schedule_reference_event(
        "customer",
        "2026-01-01",
        "08:00:00",
        customer_confirmed=True,
        owner_confirmed=True,
        last_change_note="confirmed",
        recorded_at_kst="2026-01-01T08:00:00+09:00",
    )
    correction = build_schedule_reference_event(
        "customer",
        "2026-01-01",
        "09:00:00",
        customer_confirmed=True,
        owner_confirmed=True,
        last_change_note="corrected",
        supersedes=reference.event_id,
        predecessor_digest=digest,
        recorded_at_kst="2026-01-01T09:00:00+09:00",
    )
    confirmation = build_schedule_confirmation_event(
        "customer",
        reference.event_id,
        digest,
        "operator",
        digest,
        occurred_at_kst="2026-01-01T09:00:00+09:00",
        recorded_at_kst="2026-01-01T09:00:00+09:00",
    )

    # Then: no schedule event bypasses the JSON Schema validator.
    assert validate_event(reference) == reference
    assert validate_event(correction) == correction
    assert validate_event(confirmation) == confirmation


def test_schema2_baseline_events_include_typed_source_day_manifest() -> None:
    baseline = parse_baseline(MANIFEST)

    events = baseline_events(baseline, "2026-07-29T08:00:00+09:00", schema_version="2.0")

    assert events
    assert all(event.import_manifest is not None for event in events)
    assert all(
        event.import_manifest.observation_kst_day.isoformat() == event.occurred_at_kst[:10]
        for event in events
    )
