"""23:00 cutoff, restart, concurrency, and late-response tests."""

from __future__ import annotations

from dataclasses import replace
from datetime import datetime
from pathlib import Path

import anyio

from checkin_cli.customer_schedule import (
    mark_customer_task_delivered,
    mark_customer_task_sending,
    reserve_missing_checkin_reminder,
    schedule_delivery_ledger,
)
from checkin_cli.weekly_operations import DayState
from checkin_cli.weekly_operations_correlation import (
    CanonicalCheckinCorrelationTransaction,
    CorrelationAction,
    CorrelationOutcome,
    CorrelationRequest,
    CorrelationScope,
)
from checkin_cli.weekly_operations_cutoff import CutoffClassification, run_missed_cutoff
from checkin_cli.weekly_reminder_ledger_authority import LedgerReservation, LedgerTransition
from checkin_cli.weekly_operations_lifecycle import (
    ReminderDependencies,
    ReminderOutcome,
    ReminderResult,
    run_due_reminder,
)
from tests._weekly_operations_correlation_support import event
from tests._weekly_operations_lifecycle_support import (
    DAY,
    KST,
    FakeReminderProvider,
    ProviderMode,
    fixture_at,
)


def test_cutoff_boundary_is_exact_and_missed_is_independent_of_receipt(tmp_path: Path) -> None:
    # Given
    before = fixture_at(tmp_path / "before", datetime(2026, 8, 17, 22, 59, 59, tzinfo=KST))
    at = fixture_at(tmp_path / "at", datetime(2026, 8, 17, 23, 0, 0, tzinfo=KST))

    # When
    before_result = run_missed_cutoff(before.request)
    at_result = run_missed_cutoff(at.request)

    # Then
    assert before_result.classification is CutoffClassification.NOT_DUE
    assert at_result.classification is CutoffClassification.REMINDER_DELIVERY_INCIDENT
    assert at_result.appended is True
    assert at.stores.store.read()[-1].state is DayState.MISSED
    before.close()
    at.close()


def test_audited_reminder_binds_reminded_no_response_to_missed(tmp_path: Path) -> None:
    # Given
    fixture = fixture_at(tmp_path, datetime(2026, 8, 17, 20, tzinfo=KST))
    provider = FakeReminderProvider()
    sent = anyio.run(run_due_reminder, fixture.request, ReminderDependencies(provider, lambda: fixture.request.bound_customer))
    cutoff_request = replace(fixture.request, now=datetime(2026, 8, 17, 23, tzinfo=KST))

    # When
    cutoff = run_missed_cutoff(cutoff_request)

    # Then
    assert sent.outcome is ReminderOutcome.SENT_AUDITED
    assert cutoff.classification is CutoffClassification.REMINDED_NO_RESPONSE
    row = fixture.request.bound_customer.store.read()[-1]
    assert sent.receipt is not None
    assert row.state is DayState.MISSED
    assert row.reminder_reservation_id == sent.receipt.reservation_id
    assert row.reminder_audit_id is not None
    fixture.close()


def test_unknown_reminder_still_appends_missed_without_fabricating_success(tmp_path: Path) -> None:
    # Given
    fixture = fixture_at(tmp_path, datetime(2026, 8, 17, 20, tzinfo=KST))
    provider = FakeReminderProvider(ProviderMode.UNKNOWN)
    reminder = anyio.run(run_due_reminder, fixture.request, ReminderDependencies(provider, lambda: fixture.request.bound_customer))

    # When
    cutoff = run_missed_cutoff(replace(fixture.request, now=datetime(2026, 8, 17, 23, tzinfo=KST)))

    # Then
    assert reminder.outcome is ReminderOutcome.UNKNOWN
    assert cutoff.classification is CutoffClassification.REMINDER_DELIVERY_INCIDENT
    row = fixture.request.bound_customer.store.read()[-1]
    assert row.state is DayState.MISSED
    assert row.reminder_reservation_id is None
    fixture.close()


def test_exact_cutoff_response_is_submitted_not_missed(tmp_path: Path) -> None:
    # Given
    fixture = fixture_at(
        tmp_path,
        datetime(2026, 8, 17, 23, tzinfo=KST),
        event("boundary-response", occurred_at="2026-08-17T23:00:00+09:00"),
    )

    # When
    result = run_missed_cutoff(fixture.request)

    # Then
    assert result.classification is CutoffClassification.ANSWERED
    assert fixture.request.bound_customer.store.read()[-1].state is DayState.SUBMITTED
    fixture.close()


def test_post_cutoff_response_becomes_late_submitted(tmp_path: Path) -> None:
    # Given
    fixture = fixture_at(tmp_path, datetime(2026, 8, 17, 23, tzinfo=KST))
    _ = run_missed_cutoff(fixture.request)
    _ = fixture.canonical.transaction.append(
        event("late-response", occurred_at="2026-08-17T23:00:01+09:00")
    )
    transaction = CanonicalCheckinCorrelationTransaction(
        fixture.request.bound_customer.store,
        CorrelationRequest(
            CorrelationScope(
                fixture.request.bound_customer.customer_identity_digest,
                DAY,
                CorrelationAction.CHECKIN,
            ),
            fixture.canonical.source,
        ),
    )

    # When
    result = transaction.commit()

    # Then
    assert result.outcome is CorrelationOutcome.LATE_SUBMITTED
    assert fixture.request.bound_customer.store.read()[-1].state is DayState.LATE_SUBMITTED
    fixture.close()


def test_restart_from_delivered_audits_without_provider_call(tmp_path: Path) -> None:
    # Given
    fixture = fixture_at(tmp_path, datetime(2026, 8, 17, 20, tzinfo=KST))
    bound = fixture.request.bound_customer
    from checkin_cli.weekly_operations_lifecycle import reminder_reservation_authority

    authority = reminder_reservation_authority(bound)
    prepared = bound.ledger.reserve(LedgerReservation(
        bound.customer_identity_digest, DAY,
        {"chat_id": bound.route_chat_id, "topic_id": bound.route_topic_id},
        bound.runtime_registry_digest, bound.config_digest, bound.authority_digest,
        bound.canonical.sequence, bound.canonical.digest, authority,
    ))
    sending = bound.ledger.transition(LedgerTransition("sending", prepared, authority=authority))
    _ = bound.ledger.transition(LedgerTransition(
        "delivered", sending, provider_receipt="provider-receipt-1", message_id="message-1"
    ))
    provider = FakeReminderProvider()

    # When
    result = anyio.run(
        run_due_reminder,
        fixture.request,
        ReminderDependencies(provider, lambda: bound),
    )

    # Then
    assert result.outcome is ReminderOutcome.SENT_AUDITED
    assert provider.calls == 0
    fixture.close()


def test_concurrent_ticks_share_one_provider_authority(tmp_path: Path) -> None:
    # Given
    fixture = fixture_at(tmp_path, datetime(2026, 8, 17, 20, tzinfo=KST))
    provider = FakeReminderProvider(ProviderMode.BLOCKED)
    dependencies = ReminderDependencies(provider, lambda: fixture.request.bound_customer)
    outcomes: list[ReminderResult] = []

    async def exercise() -> None:
        async def tick() -> None:
            outcomes.append(await run_due_reminder(fixture.request, dependencies))

        async with anyio.create_task_group() as tasks:
            tasks.start_soon(tick)
            await provider.entered.wait()
            tasks.start_soon(tick)
            provider.release.set()

    # When
    anyio.run(exercise)

    # Then
    assert provider.calls == 1
    assert len({receipt.reservation_id for receipt in fixture.request.bound_customer.ledger.receipts()}) == 1
    assert any(result.outcome is ReminderOutcome.SENT_AUDITED for result in outcomes)
    fixture.close()
