from __future__ import annotations

import sys
from pathlib import Path
from types import SimpleNamespace

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

import gateway_account_manager as gateway
from account_registry import AccountRegistry, AccountRegistryKind, AuthorityMode
from authority_migration import AuthorityProvision, MigrationError
from health_client import AccountSnapshot, HealthStatus


class FakeAdmin:
    def __init__(self, registry: AccountRegistry) -> None:
        self.registry = registry
        self.generation = 1
        self.disabled: list[str] = []
        self.removed_grants: list[str] = []
        self.removed_routes: list[str] = []
        self.removed_accounts: list[tuple[str, str]] = []

    def login_and_provision(self, account, authority_name: str) -> AuthorityProvision:
        material = self.registry.base_dir / "fixture-authority"
        material.mkdir(parents=True, exist_ok=True, mode=0o700)
        grant = material / f"{account.tool}-{account.slug}.grant"
        grant.write_text("synthetic-grant\n", encoding="utf-8")
        grant.chmod(0o600)
        return AuthorityProvision(authority_name, "routefixture1234567890", grant.resolve(), self.generation)

    def status_generation(self, _provider: str, _account_id: str) -> int:
        self.generation += 1
        return self.generation

    def disable_route(self, route_id: str) -> None:
        self.disabled.append(route_id)

    def remove_route_grants(self, route_id: str) -> None:
        self.removed_grants.append(route_id)

    def remove_route(self, route_id: str) -> None:
        self.removed_routes.append(route_id)

    def remove_account(self, provider: str, account_id: str) -> None:
        self.removed_accounts.append((provider, account_id))


def _registry(tmp_path: Path, kind: AccountRegistryKind) -> AccountRegistry:
    base = tmp_path / "runtime"
    base.mkdir(parents=True, mode=0o700)
    base.chmod(0o700)
    registry = AccountRegistry(base_dir=base, kind=kind)
    registry.add_dir("fixture", "Fixture")
    return registry


@pytest.mark.parametrize("kind", [AccountRegistryKind.CODEX, AccountRegistryKind.CLAUDE])
def test_stage_and_activate_keep_systray_as_registry_and_use_loopback_authority(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, kind: AccountRegistryKind
) -> None:
    registry = _registry(tmp_path, kind)
    manager = gateway.LocalGatewayAccountManager(registry, admin=FakeAdmin(registry))
    account = registry.list()[0]

    staged = manager.stage(account)
    current = manager.current(account.slug)
    assert staged.state == "dark"
    assert gateway.gateway_account_state(current) is gateway.GatewayAccountState.TESTING
    assert current.authority_binding is not None
    assert current.authority_binding.mode is AuthorityMode.SUBROUTER_DARK
    endpoint = registry.base_dir / "authority_endpoints.json"
    assert "http://127.0.0.1:31415" in endpoint.read_text(encoding="utf-8")

    monkeypatch.setattr(
        gateway,
        "gateway_health_snapshot",
        lambda *_args, **_kwargs: AccountSnapshot(
            HealthStatus.OK, None, None, checked_at=1.0, detail="Gateway: ready"
        ),
    )
    receipt = manager.activate(current)
    active = manager.current(account.slug)
    assert receipt.state == "authority"
    assert gateway.gateway_account_state(active) is gateway.GatewayAccountState.ACTIVE
    assert active.authority_binding is not None
    assert active.authority_binding.mode is AuthorityMode.SUBROUTER
    assert receipt.provider_credential_copied is False
    assert receipt.native_fallback_used is False


def test_activate_refuses_nonready_gateway(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    registry = _registry(tmp_path, AccountRegistryKind.CODEX)
    manager = gateway.LocalGatewayAccountManager(registry, admin=FakeAdmin(registry))
    manager.stage(registry.list()[0])
    current = manager.current("fixture")
    monkeypatch.setattr(
        gateway,
        "gateway_health_snapshot",
        lambda *_args, **_kwargs: AccountSnapshot(
            HealthStatus.BROKEN, None, None, checked_at=1.0, detail="Gateway: repair required"
        ),
    )
    with pytest.raises(MigrationError, match="Gateway: repair required"):
        manager.activate(current)
    assert gateway.gateway_account_state(manager.current("fixture")) is gateway.GatewayAccountState.TESTING


def test_cancel_dark_returns_account_to_native_and_removes_authority_material(tmp_path: Path) -> None:
    registry = _registry(tmp_path, AccountRegistryKind.CODEX)
    admin = FakeAdmin(registry)
    manager = gateway.LocalGatewayAccountManager(registry, admin=admin)
    manager.stage(registry.list()[0])
    current = manager.current("fixture")
    grant = current.authority_binding.proxy_grant_ref if current.authority_binding else None

    receipt = manager.cancel(current)

    assert receipt.state == "native-cancelled"
    assert gateway.gateway_account_state(manager.current("fixture")) is gateway.GatewayAccountState.NATIVE
    assert grant is not None and not grant.exists()
    assert admin.disabled == ["routefixture1234567890"]
    assert admin.removed_grants == ["routefixture1234567890"]
    assert admin.removed_routes == ["routefixture1234567890"]
    assert admin.removed_accounts == [("codex", "fixture")]
