from __future__ import annotations

import json
import os
from pathlib import Path

import pytest

from account_registry import AccountRegistry, AccountRegistryKind, PROTECTED_GROK_SLUG
from grok_auth_operation import GrokAuthOperation
from grok_health_client import GrokHealthClient


def _write_fake_auth(path: Path) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(
        json.dumps(
            {
                "https://auth.x.ai::test": {
                    "key": "access-token",
                    "refresh_token": "refresh-token",
                    "auth_mode": "oidc",
                    "email": "roy@example.com",
                    "principal_id": "principal-roy",
                }
            }
        )
        + "\n",
        encoding="utf-8",
    )
    os.chmod(path, 0o600)


def test_import_from_legacy_is_copy_only(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    base = tmp_path / "tray"
    legacy_home = tmp_path / "legacy-grok"
    legacy_auth = legacy_home / "auth.json"
    _write_fake_auth(legacy_auth)
    original = legacy_auth.read_bytes()

    registry = AccountRegistry(
        base_dir=base,
        kind=AccountRegistryKind.GROK,
        legacy_grok_home=legacy_home,
    )
    op = GrokAuthOperation(registry)

    def boom(*_args: object, **_kwargs: object) -> None:
        raise AssertionError("must not hit network during import")

    monkeypatch.setattr("grok_auth_operation.GrokOAuthSession.get_user", boom)

    account = op.import_from_legacy_home(
        slug=PROTECTED_GROK_SLUG,
        alias=PROTECTED_GROK_SLUG,
        legacy_auth=legacy_auth,
    )

    dest = account.account_home / "auth.json"
    assert dest.read_bytes() == original
    assert registry.default_slug() == PROTECTED_GROK_SLUG
    assert legacy_auth.is_symlink()
    assert legacy_auth.resolve() == dest.resolve()


def test_roy_grok_remove_is_protected(tmp_path: Path) -> None:
    base = tmp_path / "tray"
    legacy_home = tmp_path / "legacy-grok"
    _write_fake_auth(legacy_home / "auth.json")
    registry = AccountRegistry(
        base_dir=base,
        kind=AccountRegistryKind.GROK,
        legacy_grok_home=legacy_home,
    )
    GrokAuthOperation(registry).import_from_legacy_home(legacy_auth=legacy_home / "auth.json")
    with pytest.raises(PermissionError):
        registry.remove(PROTECTED_GROK_SLUG)


def test_roy_grok_rename_relabels_without_moving_the_protected_slug(tmp_path: Path) -> None:
    base = tmp_path / "tray"
    legacy_home = tmp_path / "legacy-grok"
    _write_fake_auth(legacy_home / "auth.json")
    registry = AccountRegistry(
        base_dir=base,
        kind=AccountRegistryKind.GROK,
        legacy_grok_home=legacy_home,
    )
    GrokAuthOperation(registry).import_from_legacy_home(legacy_auth=legacy_home / "auth.json")

    assert registry.rename(PROTECTED_GROK_SLUG, "Roy Grok Work") == PROTECTED_GROK_SLUG

    account = registry.list()[0]
    assert (account.slug, account.alias) == (PROTECTED_GROK_SLUG, "Roy Grok Work")
    assert (registry.accounts_dir / PROTECTED_GROK_SLUG).is_dir()


def test_roy_grok_reauth_is_protected(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.delenv("SYSTRAY_ALLOW_ROY_GROK_MUTATION", raising=False)
    base = tmp_path / "tray"
    legacy_home = tmp_path / "legacy-grok"
    _write_fake_auth(legacy_home / "auth.json")
    registry = AccountRegistry(
        base_dir=base,
        kind=AccountRegistryKind.GROK,
        legacy_grok_home=legacy_home,
    )
    op = GrokAuthOperation(registry)
    account = op.import_from_legacy_home(legacy_auth=legacy_home / "auth.json")

    def boom(*_args: object, **_kwargs: object) -> None:
        raise AssertionError("must not start device login for protected roy-grok")

    monkeypatch.setattr("grok_auth_operation.start_device_code", boom)
    events = list(op.reauthenticate(account))
    assert [e.kind for e in events] == ["failure"]
    assert events[0].message is not None and "protected" in events[0].message


def test_import_identity_never_refreshes_on_auth_error(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    from grok_oauth import GrokOAuthAuthError, GrokOAuthSession

    base = tmp_path / "tray"
    legacy_home = tmp_path / "legacy-grok"
    legacy_auth = legacy_home / "auth.json"
    _write_fake_auth(legacy_auth)
    original = legacy_auth.read_bytes()
    registry = AccountRegistry(
        base_dir=base,
        kind=AccountRegistryKind.GROK,
        legacy_grok_home=legacy_home,
    )
    op = GrokAuthOperation(registry)

    def auth_fail(self: GrokOAuthSession, url: str, access: str) -> dict:
        del self, url, access
        raise GrokOAuthAuthError("simulated 401")

    def refresh_boom(self: GrokOAuthSession, *_args: object, **_kwargs: object) -> None:
        del self
        raise AssertionError("import must not refresh tokens")

    monkeypatch.setattr(GrokOAuthSession, "_get_json", auth_fail)
    monkeypatch.setattr(GrokOAuthSession, "_refresh_if_needed", refresh_boom)

    account = op.import_from_legacy_home(
        slug=PROTECTED_GROK_SLUG,
        alias=PROTECTED_GROK_SLUG,
        legacy_auth=legacy_auth,
    )
    dest = account.account_home / "auth.json"
    assert dest.read_bytes() == original
    assert registry.default_slug() == PROTECTED_GROK_SLUG


def test_add_emits_prompt_ready_then_success(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    registry = AccountRegistry(
        base_dir=tmp_path / "tray",
        kind=AccountRegistryKind.GROK,
        legacy_grok_home=tmp_path / "legacy-grok",
    )
    op = GrokAuthOperation(registry)

    monkeypatch.setattr(
        "grok_auth_operation.start_device_code",
        lambda: {
            "device_code": "dev",
            "user_code": "ABCD",
            "verification_uri": "https://accounts.x.ai/oauth2/device",
            "verification_uri_complete": "https://accounts.x.ai/oauth2/device?user_code=ABCD",
            "interval": 1,
            "expires_in": 60,
        },
    )
    monkeypatch.setattr(
        "grok_auth_operation.poll_device_code",
        lambda _device: {
            "access_token": "access-new",
            "refresh_token": "refresh-new",
            "expires_in": 3600,
        },
    )
    monkeypatch.setattr(
        "grok_auth_operation.GrokOAuthSession.get_user",
        lambda self, **_kwargs: {"email": "new@example.com", "principalId": "p-new"},
    )

    kinds = []
    for event in op.add("new-grok"):
        kinds.append(event.kind)
        if event.kind == "prompt_ready":
            assert event.prompt is not None
            assert "ABCD" in event.prompt.raw_text
            assert event.session is None

    assert kinds == ["started", "prompt_ready", "success"]
    accounts = registry.list()
    assert any(a.alias == "new-grok" for a in accounts)


def test_health_maps_monthly_to_secondary_only() -> None:
    percent, reset = GrokHealthClient._parse_billing(
        {
            "config": {
                "used": {"val": 50},
                "monthlyLimit": {"val": 100},
                "billingPeriodEnd": "2026-08-01T00:00:00+00:00",
            }
        }
    )
    assert percent == 50
    assert reset is not None
