"""Adversarial authorized-host tests for the weekly dispatcher."""

from __future__ import annotations

from dataclasses import replace
from datetime import datetime
from pathlib import Path
import subprocess
import sys
from typing import Literal
from zoneinfo import ZoneInfo

import pytest

from checkin_cli.customer_coaching import AiProcessingConsent
from tests.gateway._nutrition_weekly_dispatcher_cases import (
    ProviderMode,
    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")
PROJECT = Path(__file__).resolve().parents[2]
PROFILE = PROJECT / "dualcoach" / "profile"


def _run_fresh(script: str) -> None:
    completed = subprocess.run(
        [sys.executable, "-c", script],
        check=True,
        capture_output=True,
        text=True,
        env={"PYTHONPATH": f"{PROFILE}:{PROJECT}"},
    )
    assert completed.stdout.strip() == "PASS"


def test_absent_sunday_daily_task_retains_legacy_weekday_guard() -> None:
    _run_fresh(
        """
from datetime import datetime
from types import SimpleNamespace
from zoneinfo import ZoneInfo
from checkin_cli.weekly_operations_schedule_host_models_r4 import CustomerScheduleTask
from gateway.platforms.telegram import TelegramAdapter
task = CustomerScheduleTask('client_001', 'daily', datetime(2026, 7, 19).date())
customer = SimpleNamespace(spec=SimpleNamespace(schedule=SimpleNamespace(daily_time='08:00')))
current = TelegramAdapter._nutrition_daily_task_is_current(
    task, customer, datetime(2026, 7, 19, 8, 17, tzinfo=ZoneInfo('Asia/Seoul'))
)
assert current is False
print('PASS')
"""
    )


def test_absent_schedule_policy_imports_zero_weekly_modules() -> None:
    _run_fresh(
        """
import sys
from gateway.config import PlatformConfig
from gateway.platforms.telegram import TelegramAdapter
adapter = object.__new__(TelegramAdapter)
adapter.config = PlatformConfig(enabled=True, token='test', extra={})
for name in tuple(sys.modules):
    if name.startswith('gateway.platforms.telegram_weekly_reminder') or name.startswith('gateway.platforms.nutrition_weekly_'):
        del sys.modules[name]
assert adapter._weekly_operations_schedule_policy() is None
assert not [name for name in sys.modules if name.startswith('gateway.platforms.telegram_weekly_reminder') or name.startswith('gateway.platforms.nutrition_weekly_')]
print('PASS')
"""
    )


@pytest.mark.asyncio
async def test_reminder_config_off_after_snapshot_blocks_provider(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
    context = ReminderContextSource()
    fixture = dispatcher_fixture(tmp_path, "reminder", 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, 20, tzinfo=KST)
    )

    assert result.success is False
    assert fixture.provider.calls == []


Drift = Literal["candidate", "config", "authority_owner", "route", "consent"]


@pytest.mark.asyncio
@pytest.mark.parametrize(
    "drift", ("candidate", "config", "authority_owner", "route", "consent")
)
async def test_reminder_mutable_authority_drift_blocks_provider(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, drift: Drift,
) -> None:
    context = ReminderContextSource()
    fixture = dispatcher_fixture(tmp_path, "reminder", context)
    fixture.install_due(monkeypatch)

    if drift == "candidate":
        context.after_snapshot = lambda: fixture.host.replace_candidate("b" * 64)
    elif drift == "config":
        context.after_snapshot = lambda: fixture.host.replace_config(
            weekly_platform_config(
                fixture.reminder, WeeklyConfigState(reminder_time="20:01:00")
            )
        )
    elif drift == "authority_owner":
        context.after_snapshot = lambda: fixture.host.replace_config(
            weekly_platform_config(
                fixture.reminder,
                WeeklyConfigState(owner_chat_id="wrong-owner-chat"),
            )
        )
    else:
        runtime = fixture.reminder.runtime
        if drift == "route":
            changed_route = runtime.spec.telegram.model_copy(
                update={"chat_id": "wrong-customer-chat"}
            )
            changed_spec = runtime.spec.model_copy(update={"telegram": changed_route})
        else:
            changed_spec = runtime.spec.model_copy(
                update={"ai_processing_consent": AiProcessingConsent(granted=False)}
            )
        changed_runtime = replace(runtime, spec=changed_spec)
        changed_registry = replace(
            fixture.reminder.registry, customers=(changed_runtime,)
        )
        context.after_snapshot = lambda: fixture.coordinator.queue_registry(
            changed_registry
        )

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

    assert result.success is False
    assert fixture.provider.calls == []


@pytest.mark.asyncio
@pytest.mark.parametrize(
    "mode", (ProviderMode.KNOWN, ProviderMode.UNKNOWN)
)
async def test_reminder_provider_outcome_is_terminal_on_real_host_replay(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, mode: ProviderMode,
) -> None:
    from tests.gateway._nutrition_weekly_dispatcher_cases import FakeTelegramProvider

    provider = FakeTelegramProvider(mode)
    fixture = dispatcher_fixture(tmp_path, "reminder", provider=provider)
    fixture.install_due(monkeypatch)
    now = datetime(2026, 8, 17, 20, tzinfo=KST)

    first = await fixture.host.run_authorized(now)
    replay = await fixture.host.run_authorized(now)

    assert first.success is False and replay.success is False
    assert len(provider.calls) == 1


def test_feature_absent_authorized_host_is_inert(tmp_path: Path) -> None:
    _run_fresh(
        f"""
import asyncio
from datetime import datetime
from pathlib import Path
from types import SimpleNamespace
from zoneinfo import ZoneInfo
import checkin_cli
from gateway.config import PlatformConfig
from gateway.platforms.telegram import TelegramAdapter
root = Path({str(tmp_path)!r})
root.mkdir(parents=True, exist_ok=True)
sentinel = root / 'baseline-receipt.json'
sentinel.write_bytes(b'baseline')
checkin_cli.initialize_schedule_delivery_fence(root)
checkin_cli.build_due_customer_tasks = lambda *_args, **_kwargs: ()
coordinator = SimpleNamespace(
    profile_root=root,
    registry=SimpleNamespace(
        owner=SimpleNamespace(user_id='owner', chat_id='owner', topic_id='owner'),
        customers=(),
    ),
)
adapter = object.__new__(TelegramAdapter)
adapter._bot = SimpleNamespace()
adapter._get_nutrition_coaching = lambda: coordinator
adapter.config = PlatformConfig(enabled=True, token='test', extra={{}})
result = asyncio.run(adapter._send_nutrition_coaching_tick_authorized(
    datetime(2026, 8, 17, 23, tzinfo=ZoneInfo('Asia/Seoul'))
))
assert result.success is True
assert sentinel.read_bytes() == b'baseline'
assert not tuple(root.rglob('*weekly-operations*'))
assert not tuple(root.rglob('*monthly*'))
print('PASS')
"""
    )
