"""Lifecycle and exactly-once dispatcher integration tests."""

from __future__ import annotations

from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo

import anyio
import pytest

from gateway.platforms.base import SendResult
from tests.gateway._nutrition_weekly_dispatcher_cases import (
    AuthorizedTelegramHost,
    FakeTelegramProvider,
    dispatcher_fixture,
)
from tests.gateway._nutrition_weekly_dispatcher_config_support import (
    WeeklyConfigState,
    weekly_platform_config,
)
from tests.gateway._nutrition_weekly_reminder_support import ReminderContextSource

KST = ZoneInfo("Asia/Seoul")


@pytest.mark.asyncio
async def test_real_authorized_tick_projects_cutoff_once_across_restart(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
    fixture = dispatcher_fixture(tmp_path, "cutoff")
    fixture.install_due(monkeypatch)
    now = datetime(2026, 8, 17, 23, tzinfo=KST)

    first = await fixture.host.run_authorized(now)
    restarted = AuthorizedTelegramHost.create(
        fixture.coordinator,
        fixture.provider,
        weekly_platform_config(fixture.reminder),
    )
    second = await restarted.run_authorized(now)

    rows = fixture.reminder.store.read()
    assert first.success is True and second.success is True
    assert len(rows) == 1 and rows[0].state.value == "missed"
    assert len(fixture.provider.calls) == 1
    call = fixture.provider.calls[0]
    assert call.topic_id == 59
    assert tuple(fixture.coordinator.drafts) == ("weekly-1",)


@pytest.mark.asyncio
async def test_toggle_off_after_snapshot_blocks_topic59_provider(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
    context = ReminderContextSource()
    fixture = dispatcher_fixture(tmp_path, "cutoff", context)
    fixture.install_due(monkeypatch)
    context.after_snapshot = lambda: fixture.host.replace_config(
        weekly_platform_config(
            fixture.reminder, WeeklyConfigState(enabled=False)
        )
    )

    result = await fixture.host.run_authorized(
        datetime(2026, 8, 17, 23, tzinfo=KST)
    )

    assert result.success is False
    assert len(fixture.reminder.store.read()) == 1
    assert fixture.provider.calls == []


@pytest.mark.asyncio
async def test_wrong_topic_fails_planning_without_sidecar_or_provider(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
    fixture = dispatcher_fixture(tmp_path, "cutoff")
    fixture.install_due(monkeypatch)
    fixture.host.replace_config(
        weekly_platform_config(
            fixture.reminder, WeeklyConfigState(review_topic_id=58)
        )
    )

    result = await fixture.host.run_authorized(
        datetime(2026, 8, 17, 23, tzinfo=KST)
    )

    assert result.success is False
    assert fixture.reminder.store.read() == ()
    assert fixture.provider.calls == []


@pytest.mark.anyio
async def test_concurrent_ticks_share_one_topic59_provider_authority(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
    entered = anyio.Event()
    release = anyio.Event()

    async def hold_provider() -> None:
        entered.set()
        await release.wait()

    provider = FakeTelegramProvider(before_send=hold_provider)
    fixture = dispatcher_fixture(tmp_path, "cutoff", provider=provider)
    fixture.install_due(monkeypatch)
    now = datetime(2026, 8, 17, 23, tzinfo=KST)
    first_results: list[SendResult] = []
    duplicate: SendResult | None = None

    async def first_tick() -> None:
        first_results.append(await fixture.host.run_authorized(now))

    async with anyio.create_task_group() as tasks:
        tasks.start_soon(first_tick)
        await entered.wait()
        duplicate = await fixture.host.run_authorized(now)
        release.set()

    assert len(first_results) == 1 and first_results[0].success is True
    assert duplicate is not None and duplicate.success is False
    assert len(provider.calls) == 1
    assert len(fixture.reminder.store.read()) == 1
