from __future__ import annotations

import json
import subprocess
import sys
import threading
import time
from collections.abc import Callable
from pathlib import Path

import pytest

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

from account_registry import Account, AccountRef, AccountRegistry, AccountRegistryKind
from claude_auth_operation import (
    ClaudeAuthOperation,
    ClaudeLoginPrompt,
    ClaudeLoginSession,
    CommandResult,
    STATUS_TIMEOUT_SECS,
)


def _make_registry(tmp_path: Path) -> AccountRegistry:
    legacy_claude_home = tmp_path / "legacy-claude"
    legacy_codex_home = tmp_path / "legacy-codex"
    legacy_claude_home.mkdir()
    legacy_codex_home.mkdir()
    return AccountRegistry(
        base_dir=tmp_path / "tray",
        legacy_codex_home=legacy_codex_home,
        legacy_claude_home=legacy_claude_home,
        kind=AccountRegistryKind.CLAUDE,
        active_claude_json=tmp_path / ".claude.json",
    )


def _account(
    slug: str,
    alias: str,
    account_home: Path,
    *,
    email: str | None = None,
    plan: str | None = None,
    account_id: str | None = None,
) -> Account:
    return Account(
        ref=AccountRef("claude", slug),
        alias=alias,
        account_home=account_home,
        email=email,
        plan=plan,
        account_id=account_id,
    )


def _read_json(path: Path) -> dict[str, object]:
    return json.loads(path.read_text(encoding="utf-8"))


class _Runner:
    def __init__(self, handler) -> None:
        self._handler = handler
        self.calls: list[dict[str, object]] = []

    def __call__(
        self,
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        self.calls.append(
            {
                "command": list(command),
                "config_dir": config_dir,
                "timeout": timeout,
            }
        )
        return self._handler(command, config_dir=config_dir, timeout=timeout)


class _FakeLoginStdout:
    def __init__(self, process: "_FakeLoginProcess") -> None:
        self._process = process

    def read(self, _size: int = -1) -> str:
        return self._process._read_stdout()

    def close(self) -> None:
        pass


class _FakeLoginStdin:
    def __init__(self, process: "_FakeLoginProcess") -> None:
        self._process = process
        self.writes: list[str] = []

    def write(self, text: str) -> int:
        self.writes.append(text)
        return len(text)

    def flush(self) -> None:
        self._process.submit_code("".join(self.writes))
        self.writes.clear()

    def close(self) -> None:
        pass


class _FakeLoginProcess:
    def __init__(
        self,
        *,
        output: str = "",
        returncode: int | None = 0,
        close_on_start: bool = True,
    ) -> None:
        self.stdout = _FakeLoginStdout(self)
        self.stdin = _FakeLoginStdin(self)
        self.wait_calls: list[float | None] = []
        self.submitted_codes: list[str] = []
        self.terminated = False
        self.killed = False
        self.returncode: int | None = None
        self._exit_event = threading.Event()
        self._stdout_queue: list[str | None] = list(output)
        self._returncode_on_submit = returncode
        for _chunk in output:
            pass
        if close_on_start:
            self.complete(returncode if returncode is not None else 0)

    def _read_stdout(self) -> str:
        while not self._stdout_queue:
            self._exit_event.wait(0.001)
        chunk = self._stdout_queue.pop(0)
        if chunk is None:
            return ""
        return chunk

    def submit_code(self, code: str) -> None:
        if self.returncode is not None:
            return
        self.submitted_codes.append(code)
        self.complete(self._returncode_on_submit if self._returncode_on_submit is not None else 0)

    def complete(self, returncode: int) -> None:
        if self.returncode is not None:
            return
        self.returncode = returncode
        self._stdout_queue.append(None)
        self._exit_event.set()

    def poll(self) -> int | None:
        return self.returncode

    def wait(self, timeout: float | None = None) -> int:
        self.wait_calls.append(timeout)
        if self.returncode is not None:
            return self.returncode
        if not self._exit_event.wait(timeout):
            raise subprocess.TimeoutExpired(cmd=["claude", "auth", "login", "--claudeai"], timeout=timeout)
        assert self.returncode is not None
        return self.returncode

    def terminate(self) -> None:
        self.terminated = True
        self.complete(-15)

    def kill(self) -> None:
        self.killed = True
        self.complete(-9)


class _LoginPopenFactory:
    def __init__(self, handler: Callable[..., _FakeLoginProcess]) -> None:
        self._handler = handler
        self.calls: list[dict[str, object]] = []

    def __call__(
        self,
        command: list[str],
        *,
        stdin=None,
        stdout=None,
        stderr=None,
        text: bool | None = None,
        bufsize: int | None = None,
        env: dict[str, str] | None = None,
    ) -> _FakeLoginProcess:
        self.calls.append(
            {
                "command": list(command),
                "stdin": stdin,
                "stdout": stdout,
                "stderr": stderr,
                "text": text,
                "bufsize": bufsize,
                "env": dict(env or {}),
            }
        )
        return self._handler(
            command,
            stdin=stdin,
            stdout=stdout,
            stderr=stderr,
            text=text,
            bufsize=bufsize,
            env=env,
        )


def test_add_runs_login_with_email_hint_persists_identity_and_commits(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    login_output = "If the browser didn't open, visit: https://auth.example.com/login\n"

    def status_handler(
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        assert command == ["claude", "auth", "status", "--json"]
        assert timeout == STATUS_TIMEOUT_SECS
        return CommandResult(
            returncode=0,
            stdout=json.dumps({"loggedIn": True}),
        )

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai", "--email", "login@example.com"]
        assert env is not None
        assert "CLAUDE_CONFIG_DIR" in env
        config_dir = Path(env["CLAUDE_CONFIG_DIR"])
        (config_dir / ".credentials.json").write_text(
            json.dumps({"claudeAiOauth": {"subscriptionType": "pro"}}),
            encoding="utf-8",
        )
        (config_dir / "claude.json").write_text(
            json.dumps(
                {
                    "oauthAccount": {
                        "emailAddress": "User@Example.com",
                        "organizationUuid": "Org-123",
                        "organizationRateLimitTier": "max",
                    }
                }
            ),
            encoding="utf-8",
        )
        return _FakeLoginProcess(output=login_output, returncode=0, close_on_start=True)

    runner = _Runner(status_handler)
    login_factory = _LoginPopenFactory(login_handler)

    events = list(
        ClaudeAuthOperation(registry, runner=runner, login_popen=login_factory).add(
            "Team Account",
            login_hint="login@example.com",
        )
    )

    assert [event.kind for event in events] == ["started", "browser-waiting", "success"]
    assert login_factory.calls[0]["command"] == [
        "claude",
        "auth",
        "login",
        "--claudeai",
        "--email",
        "login@example.com",
    ]
    assert runner.calls[0]["command"] == ["claude", "auth", "status", "--json"]
    assert runner.calls[0]["timeout"] == STATUS_TIMEOUT_SECS
    account = events[-1].account
    assert account is not None
    assert account.slug == "team-account"
    assert account.email == "user@example.com"
    assert account.account_id == "org-123"
    assert account.plan == "max"
    assert _read_json(account.account_home / "account_identity.json") == {
        "email": "user@example.com",
        "org_id": "org-123",
        "subscription_type": "max",
    }
    assert [call["command"][2] for call in runner.calls] == ["status"]


def test_add_omits_blank_email_hint_and_rejects_duplicate_identity(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    existing_home = registry.add_dir("existing", "Existing")
    (existing_home / "account_identity.json").write_text(
        json.dumps(
            {
                "email": "existing@example.com",
                "org_id": "org-123",
                "subscription_type": "max",
            }
        ),
        encoding="utf-8",
    )
    existing = registry.list()[0]

    def status_handler(
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        return CommandResult(returncode=0, stdout=json.dumps({"loggedIn": True}))

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        assert env is not None
        assert "CLAUDE_CONFIG_DIR" in env
        config_dir = Path(env["CLAUDE_CONFIG_DIR"])
        (config_dir / ".credentials.json").write_text("{}", encoding="utf-8")
        (config_dir / "claude.json").write_text(
            json.dumps(
                {
                    "oauthAccount": {
                        "emailAddress": "Existing@Example.com",
                        "organizationUuid": "ORG-123",
                        "organizationRateLimitTier": "team",
                    }
                }
            ),
            encoding="utf-8",
        )
        return _FakeLoginProcess(
            output="If the browser didn't open, visit: https://auth.example.com/login\n",
            returncode=0,
            close_on_start=True,
        )

    runner = _Runner(status_handler)
    login_factory = _LoginPopenFactory(login_handler)

    events = list(
        ClaudeAuthOperation(registry, runner=runner, login_popen=login_factory).add("Duplicate", login_hint="  ")
    )

    assert [event.kind for event in events] == ["started", "browser-waiting", "rollback", "collision"]
    assert login_factory.calls[0]["command"] == ["claude", "auth", "login", "--claudeai"]
    assert events[-1].collision == existing
    assert [account.slug for account in registry.list()] == ["existing"]
    assert not (registry.accounts_dir / "duplicate").exists()


def test_add_failure_does_not_leak_subprocess_output_and_rolls_back(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    secret = "secret@example.com token-123"

    def login_handler(
        command: list[str],
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        return _FakeLoginProcess(output=secret, returncode=7, close_on_start=True)

    login_factory = _LoginPopenFactory(login_handler)

    events = list(ClaudeAuthOperation(registry, login_popen=login_factory).add("Broken", login_hint=None))

    assert [event.kind for event in events] == ["started", "browser-waiting", "rollback", "failure"]
    assert secret not in (events[-1].message or "")
    assert events[-1].error is not None
    assert registry.list() == []
    assert not (registry.accounts_dir / "broken").exists()


def test_add_login_timeout_rolls_back_and_reports_timeout_message(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    registry = _make_registry(tmp_path)
    monkeypatch.setattr("claude_auth_operation.LOGIN_TIMEOUT_SECS", 0.5)

    def login_handler(
        command: list[str],
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        return _FakeLoginProcess(
            output="If the browser didn't open, visit: https://auth.example.com/login\nPaste code here if prompted > ",
            returncode=0,
            close_on_start=False,
        )

    login_factory = _LoginPopenFactory(login_handler)
    events = ClaudeAuthOperation(registry, login_popen=login_factory).add("Stuck", login_hint=None)

    assert next(events).kind == "started"
    assert next(events).kind == "browser-waiting"
    code_required = next(events)
    assert code_required.kind == "code_required"
    assert code_required.prompt == ClaudeLoginPrompt(
        url="https://auth.example.com/login",
        raw_text="If the browser didn't open, visit: https://auth.example.com/login\n",
    )

    time.sleep(0.02)
    remaining = list(events)

    assert [event.kind for event in remaining] == ["rollback", "failure"]
    assert "timed out" in (remaining[-1].message or "")
    assert remaining[-1].error is not None
    assert registry.list() == []
    assert not (registry.accounts_dir / "stuck").exists()


def test_add_code_required_when_login_url_is_ready_before_prompt(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    registry = _make_registry(tmp_path)
    monkeypatch.setattr("claude_auth_operation.LOGIN_TIMEOUT_SECS", 5.0)
    login_output = "If the browser didn't open, visit: https://auth.example.com/login\n"

    def login_handler(
        command: list[str],
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        return _FakeLoginProcess(output=login_output, returncode=0, close_on_start=False)

    events = ClaudeAuthOperation(
        registry,
        login_popen=_LoginPopenFactory(login_handler),
    ).add("Needs Code", login_hint=None)

    assert next(events).kind == "started"
    assert next(events).kind == "browser-waiting"
    code_required = next(events)

    assert code_required.kind == "code_required"
    assert code_required.prompt == ClaudeLoginPrompt(
        url="https://auth.example.com/login",
        raw_text=login_output,
    )
    assert code_required.session is not None

    code_required.session.process.terminate()
    assert [event.kind for event in events] == ["rollback", "failure"]


def test_add_code_required_when_current_cli_emits_no_prompt(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    registry = _make_registry(tmp_path)
    monkeypatch.setattr("claude_auth_operation.CODE_ENTRY_FALLBACK_SECS", 0.01)

    def login_handler(
        command: list[str],
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        return _FakeLoginProcess(output="", returncode=0, close_on_start=False)

    events = ClaudeAuthOperation(
        registry,
        login_popen=_LoginPopenFactory(login_handler),
    ).add("Silent CLI", login_hint=None)

    assert next(events).kind == "started"
    assert next(events).kind == "browser-waiting"
    code_required = next(events)

    assert code_required.kind == "code_required"
    assert code_required.prompt == ClaudeLoginPrompt(url="", raw_text="")
    assert code_required.session is not None

    code_required.session.process.terminate()
    assert [event.kind for event in events] == ["rollback", "failure"]


def test_add_code_required_submit_code_succeeds(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    login_output = "If the browser didn't open, visit: https://auth.example.com/login\nPaste code here if prompted > "
    code_prompt_text = "If the browser didn't open, visit: https://auth.example.com/login\n"

    def status_handler(
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        assert command == ["claude", "auth", "status", "--json"]
        assert timeout == STATUS_TIMEOUT_SECS
        return CommandResult(
            returncode=0,
            stdout=json.dumps({"loggedIn": True}),
        )

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai", "--email", "login@example.com"]
        assert env is not None
        config_dir = Path(env["CLAUDE_CONFIG_DIR"])
        (config_dir / ".credentials.json").write_text(
            json.dumps({"claudeAiOauth": {"subscriptionType": "pro"}}),
            encoding="utf-8",
        )
        (config_dir / "claude.json").write_text(
            json.dumps(
                {
                    "oauthAccount": {
                        "emailAddress": "User@Example.com",
                        "organizationUuid": "Org-123",
                        "organizationRateLimitTier": "max",
                    }
                }
            ),
            encoding="utf-8",
        )
        return _FakeLoginProcess(output=login_output, returncode=0, close_on_start=False)

    runner = _Runner(status_handler)
    login_factory = _LoginPopenFactory(login_handler)
    operation = ClaudeAuthOperation(registry, runner=runner, login_popen=login_factory)

    events = operation.add("Team Account", login_hint="login@example.com")

    assert next(events).kind == "started"
    assert next(events).kind == "browser-waiting"
    code_required = next(events)
    assert code_required.kind == "code_required"
    assert code_required.prompt == ClaudeLoginPrompt(
        url="https://auth.example.com/login",
        raw_text=code_prompt_text,
    )
    assert isinstance(code_required.session, ClaudeLoginSession)

    operation.submit_code(code_required.session, " 1234 ")
    assert code_required.session.process.submitted_codes == ["1234\n"]

    remaining = list(events)

    assert [event.kind for event in remaining] == ["success"]
    account = remaining[-1].account
    assert account is not None
    assert account.slug == "team-account"
    assert account.email == "user@example.com"
    assert account.account_id == "org-123"
    assert account.plan == "max"
    assert runner.calls[0]["command"] == ["claude", "auth", "status", "--json"]
    assert login_factory.calls[0]["command"] == [
        "claude",
        "auth",
        "login",
        "--claudeai",
        "--email",
        "login@example.com",
    ]


def test_add_code_required_cancel_rolls_back(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        assert env is not None
        return _FakeLoginProcess(
            output="If the browser didn't open, visit: https://auth.example.com/login\nPaste code here if prompted > ",
            returncode=0,
            close_on_start=False,
        )

    login_factory = _LoginPopenFactory(login_handler)
    events = ClaudeAuthOperation(registry, login_popen=login_factory).add("Cancelled", login_hint=None)

    assert next(events).kind == "started"
    assert next(events).kind == "browser-waiting"
    code_required = next(events)
    assert code_required.kind == "code_required"
    assert code_required.session is not None
    code_required.session.process.terminate()

    remaining = list(events)

    assert [event.kind for event in remaining] == ["rollback", "failure"]
    assert remaining[-1].error is not None
    assert registry.list() == []


def test_reauthenticate_keeps_known_identity_and_updates_subscription(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    claude_home = registry.add_dir("rafa", "Rafa")
    original_credentials = b'{"before":"credentials"}'
    (claude_home / ".credentials.json").write_bytes(original_credentials)
    (claude_home / "account_identity.json").write_text(
        json.dumps(
            {
                "email": "rafa@example.com",
                "org_id": "org-123",
                "subscription_type": "old-plan",
            }
        ),
        encoding="utf-8",
    )
    (claude_home / "claude.json").write_text(
        json.dumps(
            {
                "oauthAccount": {
                    "emailAddress": "rafa@example.com",
                    "organizationUuid": "org-123",
                    "organizationRateLimitTier": "old-plan",
                }
            }
        ),
        encoding="utf-8",
    )
    account = registry.list()[0]

    def status_handler(
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        return CommandResult(returncode=0, stdout=json.dumps({"loggedIn": True}))

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        assert env is not None
        config_dir = Path(env["CLAUDE_CONFIG_DIR"])
        (config_dir / ".credentials.json").write_text(
            json.dumps({"claudeAiOauth": {"subscriptionType": "new-plan"}}),
            encoding="utf-8",
        )
        (config_dir / "claude.json").write_text(
            json.dumps(
                {
                    "oauthAccount": {
                        "emailAddress": "RAFA@example.com",
                        "organizationUuid": "ORG-123",
                        "organizationRateLimitTier": "new-plan",
                    }
                }
            ),
            encoding="utf-8",
        )
        return _FakeLoginProcess(output="If the browser didn't open, visit: https://auth.example.com/login\n", returncode=0, close_on_start=True)

    events = list(
        ClaudeAuthOperation(registry, runner=_Runner(status_handler), login_popen=_LoginPopenFactory(login_handler)).reauthenticate(account)
    )

    assert [event.kind for event in events] == ["started", "browser-waiting", "success"]
    refreshed = events[-1].account
    assert refreshed is not None
    assert refreshed.email == "rafa@example.com"
    assert refreshed.account_id == "org-123"
    assert refreshed.plan == "new-plan"
    assert _read_json(claude_home / "account_identity.json") == {
        "email": "rafa@example.com",
        "org_id": "org-123",
        "subscription_type": "new-plan",
    }


def test_reauthenticate_restores_original_files_on_identity_collision(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    current_home = registry.add_dir("rafa", "Rafa")
    other_home = registry.add_dir("roy", "Roy")
    original_credentials = b'{"before":"credentials"}'
    original_identity = b'{"email":"rafa@example.com","org_id":"org-rafa","subscription_type":"max"}'
    original_metadata = b'{"oauthAccount":{"emailAddress":"rafa@example.com","organizationUuid":"org-rafa"}}'
    (current_home / ".credentials.json").write_bytes(original_credentials)
    (current_home / "account_identity.json").write_bytes(original_identity)
    (current_home / "claude.json").write_bytes(original_metadata)
    (other_home / "account_identity.json").write_text(
        json.dumps(
            {
                "email": "roy@example.com",
                "org_id": "org-roy",
                "subscription_type": "team",
            }
        ),
        encoding="utf-8",
    )
    current = registry.list()[0]
    other = registry.list()[1]

    def status_handler(
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        return CommandResult(returncode=0, stdout=json.dumps({"loggedIn": True}))

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        assert env is not None
        config_dir = Path(env["CLAUDE_CONFIG_DIR"])
        (config_dir / ".credentials.json").write_text('{"after":"credentials"}', encoding="utf-8")
        (config_dir / "account_identity.json").write_text('{"mutated":true}', encoding="utf-8")
        (config_dir / "claude.json").write_text(
            json.dumps(
                {
                    "oauthAccount": {
                        "emailAddress": "ROY@example.com",
                        "organizationUuid": "ORG-ROY",
                        "organizationRateLimitTier": "team",
                    }
                }
            ),
            encoding="utf-8",
        )
        return _FakeLoginProcess(output="If the browser didn't open, visit: https://auth.example.com/login\n", returncode=0, close_on_start=True)

    events = list(
        ClaudeAuthOperation(registry, runner=_Runner(status_handler), login_popen=_LoginPopenFactory(login_handler)).reauthenticate(current)
    )

    assert [event.kind for event in events] == ["started", "browser-waiting", "rollback", "collision"]
    assert events[-1].collision == other
    assert (current_home / ".credentials.json").read_bytes() == original_credentials
    assert (current_home / "account_identity.json").read_bytes() == original_identity
    assert (current_home / "claude.json").read_bytes() == original_metadata


def test_reauthenticate_legacy_missing_identity_accepts_unique_verified_baseline(tmp_path: Path) -> None:
    registry = _make_registry(tmp_path)
    claude_home = registry.add_dir("legacy", "Legacy")
    (claude_home / ".credentials.json").write_text("{}", encoding="utf-8")
    (claude_home / "claude.json").write_text(
        json.dumps(
            {
                "oauthAccount": {
                    "emailAddress": "legacy@example.com",
                    "organizationUuid": "legacy-org",
                    "organizationRateLimitTier": "free",
                }
            }
        ),
        encoding="utf-8",
    )
    legacy_account = _account(
        "legacy",
        "Legacy",
        claude_home,
        email="legacy@example.com",
        plan="free",
        account_id="legacy-account",
    )

    def status_handler(
        command: list[str],
        *,
        config_dir: Path,
        timeout: float | None = None,
    ) -> CommandResult:
        return CommandResult(returncode=0, stdout=json.dumps({"loggedIn": True}))

    def login_handler(
        command: list[str],
        *,
        env: dict[str, str] | None = None,
        **_kwargs,
    ) -> _FakeLoginProcess:
        assert command == ["claude", "auth", "login", "--claudeai"]
        assert env is not None
        config_dir = Path(env["CLAUDE_CONFIG_DIR"])
        (config_dir / ".credentials.json").write_text(
            json.dumps({"claudeAiOauth": {"subscriptionType": "team"}}),
            encoding="utf-8",
        )
        (config_dir / "claude.json").write_text(
            json.dumps(
                {
                    "oauthAccount": {
                        "emailAddress": "legacy@example.com",
                        "organizationUuid": "legacy-org",
                        "organizationRateLimitTier": "team",
                    }
                }
            ),
            encoding="utf-8",
        )
        return _FakeLoginProcess(output="If the browser didn't open, visit: https://auth.example.com/login\n", returncode=0, close_on_start=True)

    events = list(
        ClaudeAuthOperation(registry, runner=_Runner(status_handler), login_popen=_LoginPopenFactory(login_handler)).reauthenticate(legacy_account)
    )

    assert [event.kind for event in events] == ["started", "browser-waiting", "success"]
    assert _read_json(claude_home / "account_identity.json") == {
        "email": "legacy@example.com",
        "org_id": "legacy-org",
        "subscription_type": "team",
    }
