"""Behavior contract for the NutriCoach v1.5 seven-day observer."""

from __future__ import annotations

from dataclasses import replace
from datetime import date, datetime, timedelta
from pathlib import Path

from scripts.nutricoach_v150_seven_day_observer import (
    Observation,
    ObserverPhase,
    RuntimeSnapshot,
    evaluate,
)
from scripts.nutricoach_v150_observer_final import (
    FinalVerdict,
    final_verdict,
)
from scripts.nutricoach_v150_observer_runtime import _day_status_days


def _healthy_snapshot() -> RuntimeSnapshot:
    return RuntimeSnapshot(
        active=True,
        candidate_matches=True,
        channel_inbox_off=True,
        committed=True,
        capacity=5,
        journal_clean=True,
        nrestarts=0,
        telegram_sockets=2,
        weekly_enabled=True,
        day_status_days=frozenset(),
    )


def test_observer_passes_healthy_pre_window_snapshot() -> None:
    # Given
    snapshot = _healthy_snapshot()
    now = datetime.fromisoformat("2026-08-30T22:00:00+09:00")

    # When
    result = evaluate(snapshot, now)

    # Then
    assert result == Observation(ObserverPhase.PRE_WINDOW, True, ())


def test_observer_rejects_missing_telegram_connection() -> None:
    # Given
    snapshot = replace(_healthy_snapshot(), telegram_sockets=0)
    now = datetime.fromisoformat("2026-08-31T22:00:00+09:00")

    # When
    result = evaluate(snapshot, now)

    # Then
    assert result.failures == ("telegram_connection",)


def test_observer_requires_first_day_status_after_deadline() -> None:
    # Given
    snapshot = _healthy_snapshot()
    now = datetime.fromisoformat("2026-09-01T00:06:00+09:00")

    # When
    result = evaluate(snapshot, now)

    # Then
    assert result.failures == ("first_day_status",)


def test_observer_selects_configured_candidate_suffixed_authority_root(
    tmp_path: Path,
) -> None:
    # Given
    predecessor = tmp_path / "data/weekly-operations-authority"
    configured = tmp_path / "data/weekly-operations-authority-candidate"
    predecessor.mkdir(parents=True)
    configured.mkdir()
    (predecessor / "customer.day-status-v1.jsonl").write_text(
        '{"kst_day":"2026-08-30"}\n',
        encoding="utf-8",
    )
    (configured / "customer.day-status-v1.jsonl").write_text(
        '{"kst_day":"2026-08-31"}\n',
        encoding="utf-8",
    )
    (tmp_path / "config.yaml").write_text(
        """platforms:
  telegram:
    extra:
      nutrition_coaching:
        weekly_operations_authority_path: data/weekly-operations-authority-candidate
""",
        encoding="utf-8",
    )

    # When
    days = _day_status_days(tmp_path)

    # Then
    assert days == frozenset({date(2026, 8, 31)})


def test_observer_accepts_first_day_status_after_deadline() -> None:
    # Given
    snapshot = replace(
        _healthy_snapshot(),
        day_status_days=frozenset({date(2026, 8, 31)}),
    )
    now = datetime.fromisoformat("2026-09-01T00:06:00+09:00")

    # When
    result = evaluate(snapshot, now)

    # Then
    assert result == Observation(ObserverPhase.IN_WINDOW, True, ())


def test_final_verdict_passes_complete_clean_coverage() -> None:
    # Given
    start = datetime.fromisoformat("2026-09-01T00:00:00+09:00")
    observations = tuple(
        (start + timedelta(minutes=10 * index), True) for index in range(1009)
    )

    # When
    result = final_verdict(observations)

    # Then
    assert result == FinalVerdict(True, ())


def test_final_verdict_rejects_observation_gap() -> None:
    # Given
    start = datetime.fromisoformat("2026-09-01T00:00:00+09:00")
    observations = (
        (start, True),
        (start + timedelta(minutes=40), True),
        (datetime.fromisoformat("2026-09-08T00:00:00+09:00"), True),
    )

    # When
    result = final_verdict(observations)

    # Then
    assert result.failures == ("coverage_gap",)


def test_final_verdict_rejects_failed_observation() -> None:
    # Given
    start = datetime.fromisoformat("2026-09-01T00:00:00+09:00")
    observations = tuple(
        (start + timedelta(minutes=10 * index), index != 12) for index in range(1009)
    )

    # When
    result = final_verdict(observations)

    # Then
    assert result.failures == ("failed_observation",)
