"""Task A regressions for sequence ordering and point updates."""
from __future__ import annotations

import threading
import time
from unittest.mock import MagicMock

import pytest

from antigravity_provider.router.event_bus import (
    EventBus,
    EVENT_ACCOUNT_ADDED,
    EVENT_ACCOUNT_REMOVED,
    EVENT_QUOTA_UPDATED,
)
from antigravity_provider.router.profile_manager import ProfileAuthManager
from antigravity_provider.router.scheduler import HermesRefreshScheduler
from antigravity_provider.router.state_store import HubSnapshot, HubStateStore
from antigravity_provider.router.model_registry import ModelRegistry
from antigravity_provider.router.unified_health import ProfileViewModel, SystemReadiness


def _readiness() -> SystemReadiness:
    return SystemReadiness(
        state="limited",
        title_ru="Тест",
        summary_ru="",
        roles_ready_count=0,
        total_roles=0,
        accounts_connected_count=0,
        total_accounts=0,
        providers_ready_count=0,
        total_providers=0,
    )


def _profile(profile_id: str) -> ProfileViewModel:
    return ProfileViewModel(
        profile_id=profile_id,
        display_name=profile_id,
        account_identity=f"{profile_id}@example.test",
        provider="antigravity",
        provider_display_name="Antigravity",
        assigned_roles=[],
        primary_role=None,
        is_main_account=False,
        is_main_orchestrator=False,
        auth_state="AUTHENTICATED",
        health_state="healthy",
        health_label_ru="Работает",
        model_states={},
        cooldown_remaining_sec=0,
        last_checked_at=None,
        enabled=True,
        is_cold_spare=False,
        is_empty_slot=False,
    )


@pytest.mark.unit
def test_late_refresh_result_cannot_overwrite_newer_seq(monkeypatch: pytest.MonkeyPatch) -> None:
    slow_started = threading.Event()
    release_slow = threading.Event()

    class FakeHealth:
        def scan_all(self, force: bool = False):
            if threading.current_thread().name == "slow-refresh":
                slow_started.set()
                assert release_slow.wait(2)
            return {}

        def get_system_readiness(self):
            return _readiness()

        def get_agent_view_models(self):
            return []

        def get_provider_summaries(self):
            return []

        def get_routing_pipelines(self):
            return {}

    monkeypatch.setattr(
        "antigravity_provider.router.state_store.UnifiedHealthService.get",
        lambda: FakeHealth(),
    )

    store = HubStateStore()
    slow = threading.Thread(target=lambda: store.refresh(seq=1), name="slow-refresh")
    slow.start()
    assert slow_started.wait(1)
    fast_snapshot = store.refresh(seq=2)
    release_slow.set()
    slow.join(timeout=2)

    assert fast_snapshot.seq == 2
    assert store.get_snapshot().seq == 2
    assert store.get_snapshot().generation == 1
    assert store.refresh_skipped_total == 1


@pytest.mark.unit
def test_quota_delta_updates_only_target_profile_and_emits_key() -> None:
    profile_a = _profile("account-a")
    profile_b = _profile("account-b")
    store = HubStateStore()
    store._current_snapshot = HubSnapshot(
        generation=1,
        seq=1,
        timestamp=time.time(),
        profiles_by_provider={"antigravity": [profile_a, profile_b]},
        all_profiles={"account-a": profile_a, "account-b": profile_b},
        readiness=_readiness(),
        agents=[],
        providers=[],
        routing={},
        quotas={},
    )
    received = []
    bus = EventBus.get()
    bus.subscribe(EVENT_QUOTA_UPDATED, lambda _name, payload: received.append(payload))
    try:
        quota = object()
        store.apply_delta_quota_updated("antigravity", "account-a", quota)
    finally:
        bus._listeners.clear()

    snapshot = store.get_snapshot()
    assert snapshot.all_profiles["account-b"] is profile_b
    assert snapshot.all_profiles["account-a"] is not profile_a
    assert snapshot.quotas["account-a"] is quota
    assert received[-1]["profile_id"] == "account-a"
    assert received[-1]["seq"] == snapshot.seq


@pytest.mark.unit
def test_single_scheduler_refresh_waits_for_quota_before_delta(monkeypatch: pytest.MonkeyPatch) -> None:
    order: list[str] = []
    quota_service = MagicMock()
    quota_service.fetch_account_quota.side_effect = lambda *_args, **_kwargs: order.append("fetch") or "quota"
    store = MagicMock()
    store.apply_delta_quota_updated.side_effect = lambda *_args: order.append("quota_delta")
    store.apply_delta_account_updated.side_effect = lambda *_args: order.append("account_delta")
    monkeypatch.setattr(
        "antigravity_provider.router.scheduler.AccountQuotaService.get",
        lambda: quota_service,
    )
    monkeypatch.setattr("antigravity_provider.router.scheduler.HubStateStore.get", lambda: store)

    done = threading.Event()
    scheduler = HermesRefreshScheduler()
    scheduler.trigger_refresh_account("antigravity", "account-a", on_complete=done.set)
    assert done.wait(2)
    assert order == ["fetch", "quota_delta", "account_delta"]


@pytest.mark.unit
def test_model_score_rejects_exhausted_pool_and_prefers_more_quota() -> None:
    registry = ModelRegistry.get()
    descriptor = registry.get_model("gemini-2.5-pro")
    requirements = registry.get_role_requirements("research")
    assert descriptor is not None

    ok_empty, _, reason = registry.evaluate_model_score(
        descriptor,
        requirements,
        quota_remaining_percent=0,
    )
    ok_low, score_low, _ = registry.evaluate_model_score(
        descriptor,
        requirements,
        quota_remaining_percent=10,
    )
    ok_high, score_high, _ = registry.evaluate_model_score(
        descriptor,
        requirements,
        quota_remaining_percent=90,
    )

    assert not ok_empty and reason == "Quota bucket exhausted"
    assert ok_low and ok_high
    assert score_high > score_low


@pytest.mark.unit
def test_auth_storage_emits_secret_free_targeted_lifecycle_events(
    tmp_path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes"))
    bus = EventBus.get()
    bus._listeners.clear()
    received: list[tuple[str, dict]] = []
    bus.subscribe("*", lambda name, payload: received.append((name, payload)))

    ProfileAuthManager.save_profile_auth(
        "antigravity",
        "account-a",
        {"access_token": "must-not-leak", "email": "person@example.test"},
    )
    assert received[-1] == (
        EVENT_ACCOUNT_ADDED,
        {"provider": "antigravity", "profile_id": "account-a"},
    )
    assert "must-not-leak" not in repr(received)

    assert ProfileAuthManager.delete_profile_auth("antigravity", "account-a")
    assert received[-1] == (
        EVENT_ACCOUNT_REMOVED,
        {"provider": "antigravity", "profile_id": "account-a"},
    )
    bus._listeners.clear()
