"""Real-authority fixtures for weekly-operations aggregate tests."""

from __future__ import annotations

from dataclasses import dataclass
from types import MappingProxyType
from datetime import date, datetime, time
from pathlib import Path
from collections.abc import Mapping
from zoneinfo import ZoneInfo

from checkin_cli.models import ContractCheckin, Event
from checkin_cli.weekly_operations import (
    DayState,
    ReminderIdentity,
    SourceLineage,
    WeeklyOperationInput,
)
from checkin_cli.weekly_operations_layout import customer_data_name
from checkin_cli.weekly_operations_lineage import canonical_prefix_pin
from tests._weekly_operations_correlation_support import (
    CUSTOMER,
    CanonicalFixture,
    StoreFixture,
    event,
    registered_source_at,
    store_at,
)

KST = ZoneInfo("Asia/Seoul")
_STATUS_TIMES: Mapping[DayState, time] = MappingProxyType(
    {
        DayState.SUBMITTED: time(22, 59, 59),
        DayState.MISSED: time(23),
        DayState.LATE_SUBMITTED: time(22, 59, 59),
    }
)


@dataclass(frozen=True, slots=True)
class SyntheticStatus:
    day: date
    state: DayState
    sequence: int
    source_event_id: str | None = None
    reminder: ReminderIdentity | None = None
    occurred_time: time | None = None


@dataclass(frozen=True, slots=True)
class SummaryFixture:
    canonical: CanonicalFixture
    sidecar: StoreFixture

    def close(self) -> None:
        self.canonical.close()
        self.sidecar.close()


def source_lineage(fixture: CanonicalFixture, event_id: str) -> SourceLineage:
    """Build one exact source pin from the registered canonical snapshot."""
    with fixture.source.read_locked() as snapshot:
        digest = next(
            row["event_digest"]
            for row in snapshot.sequence_rows
            if row["event_id"] == event_id
        )
    assert isinstance(digest, str)
    return SourceLineage(event_id, digest)


def append_status(fixture: SummaryFixture, status: SyntheticStatus) -> None:
    """Append a directly pinned status through the real private sidecar."""
    with fixture.canonical.source.read_locked() as snapshot:
        pin = canonical_prefix_pin(snapshot, status.sequence)
    source = (
        None
        if status.source_event_id is None
        else source_lineage(fixture.canonical, status.source_event_id)
    )
    occurred_time = _STATUS_TIMES[status.state]
    _ = fixture.sidecar.store.append(
        WeeklyOperationInput.for_customer(
            CUSTOMER,
            status.day,
            status.state,
            pin,
            datetime.combine(
                status.day,
                occurred_time if status.occurred_time is None else status.occurred_time,
                KST,
            ),
            source,
            status.reminder,
        )
    )


def fixture_for_events(
    tmp_path: Path, events: tuple[Event, ...]
) -> SummaryFixture:
    """Create an empty sidecar bound to one registered canonical stream."""
    sidecar = store_at(tmp_path / "sidecar")
    canonical = registered_source_at(
        tmp_path / "canonical", CUSTOMER, *events, registry_authority=sidecar.authority
    )
    return SummaryFixture(canonical, sidecar)


def sidecar_data_path(fixture: SummaryFixture) -> Path:
    """Return the test-only durable data path for an authority-bound sidecar."""
    return fixture.sidecar.authority.observed_path / customer_data_name(
        fixture.sidecar.store.customer_identity_digest
    )


def approved_synthetic_week(tmp_path: Path) -> SummaryFixture:
    """Build the approved 2026-08-17 seven-day authority fixture."""
    prior_events = (
        event("summary-prior-mon", occurred_at="2026-08-10T08:00:00+09:00", check_in=ContractCheckin(body_weight_kg=80.0)),
        event("summary-prior-tue", occurred_at="2026-08-11T08:00:00+09:00", check_in=ContractCheckin(body_weight_kg=80.1)),
        event("summary-prior-fri", occurred_at="2026-08-14T08:00:00+09:00", check_in=ContractCheckin(body_weight_kg=80.2)),
    )
    current_events = (
        event("summary-current-mon", occurred_at="2026-08-17T08:00:00+09:00", check_in=ContractCheckin(body_weight_kg=81.111)),
        event("summary-current-tue", occurred_at="2026-08-18T08:00:00+09:00", check_in=ContractCheckin(body_weight_kg=81.222)),
        event("summary-current-thu", occurred_at="2026-08-20T08:00:00+09:00", check_in=ContractCheckin(body_weight_kg=81.333)),
        event("summary-current-fri", occurred_at="2026-08-21T23:59:59+09:00", check_in=ContractCheckin(body_weight_kg=81.444)),
        event(
            "summary-current-sun",
            occurred_at="2026-08-23T08:00:00+09:00",
            check_in=ContractCheckin(body_weight_kg=81.555, meal_summary="RAW-PAYLOAD-PII-SENTINEL"),
        ),
    )
    sidecar = store_at(tmp_path / "sidecar")
    canonical = registered_source_at(
        tmp_path / "canonical",
        CUSTOMER,
        *prior_events,
        *current_events,
        registry_authority=sidecar.authority,
    )
    fixture = SummaryFixture(canonical, sidecar)
    statuses = (
        SyntheticStatus(date(2026, 8, 10), DayState.SUBMITTED, 1, "summary-prior-mon"),
        SyntheticStatus(date(2026, 8, 11), DayState.SUBMITTED, 2, "summary-prior-tue"),
        SyntheticStatus(date(2026, 8, 12), DayState.MISSED, 2),
        SyntheticStatus(date(2026, 8, 13), DayState.MISSED, 2),
        SyntheticStatus(date(2026, 8, 14), DayState.SUBMITTED, 3, "summary-prior-fri"),
        SyntheticStatus(date(2026, 8, 15), DayState.MISSED, 3),
        SyntheticStatus(date(2026, 8, 16), DayState.MISSED, 3),
        SyntheticStatus(date(2026, 8, 17), DayState.SUBMITTED, 4, "summary-current-mon"),
        SyntheticStatus(date(2026, 8, 18), DayState.SUBMITTED, 5, "summary-current-tue", ReminderIdentity("summary-reminder-01", "summary-audit-01")),
        SyntheticStatus(date(2026, 8, 19), DayState.MISSED, 5, reminder=ReminderIdentity("summary-reminder-02", "summary-audit-02")),
        SyntheticStatus(date(2026, 8, 20), DayState.SUBMITTED, 6, "summary-current-thu"),
        SyntheticStatus(date(2026, 8, 21), DayState.MISSED, 6, reminder=ReminderIdentity("summary-reminder-03", "summary-audit-03")),
        SyntheticStatus(date(2026, 8, 21), DayState.LATE_SUBMITTED, 7, "summary-current-fri"),
        SyntheticStatus(date(2026, 8, 22), DayState.MISSED, 7, reminder=ReminderIdentity("summary-reminder-04", "summary-audit-04")),
        SyntheticStatus(date(2026, 8, 23), DayState.SUBMITTED, 8, "summary-current-sun"),
    )
    for status in statuses:
        append_status(fixture, status)
    return fixture
