from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Callable

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

from claude_credentials import (
    CredentialAuthority,
    credentials_fingerprint,
    identity_pin_path,
    pin_account_uuid,
    pinned_account_uuid,
    resolve_credentials,
)


def _write_credentials(path: Path, access_token: str, refresh_token: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(
        json.dumps(
            {
                "claudeAiOauth": {
                    "accessToken": access_token,
                    "refreshToken": refresh_token,
                    "expiresAt": 0,
                }
            }
        ),
        encoding="utf-8",
    )


def _write_identity(home: Path, account_uuid: str) -> None:
    home.mkdir(parents=True, exist_ok=True)
    pin_account_uuid(home, account_uuid)


def _live_token_owned_by(account_uuid: str) -> Callable[[Path], str | None]:
    return lambda _path: account_uuid


def _never_probed(_path: Path) -> str | None:
    raise AssertionError("the live credentials file must not be probed here")


def test_the_live_grant_wins_when_the_token_itself_names_this_account(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    _write_identity(account_home, "same-uuid")
    _write_credentials(account_home / ".credentials.json", "own", "own-refresh")
    _write_credentials(live_home / ".credentials.json", "live", "live-refresh")

    resolved = resolve_credentials(
        account_home, live_home, identify=_live_token_owned_by("same-uuid")
    )

    assert resolved.path == live_home / ".credentials.json"
    assert resolved.authority is CredentialAuthority.VENDOR_MANAGED
    assert resolved.read_only is True


def test_a_live_grant_belonging_to_another_account_is_never_read(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    _write_identity(account_home, "same-uuid")
    _write_credentials(account_home / ".credentials.json", "own", "own-refresh")
    _write_credentials(live_home / ".credentials.json", "live", "live-refresh")

    resolved = resolve_credentials(
        account_home, live_home, identify=_live_token_owned_by("other-uuid")
    )

    assert resolved.path == account_home / ".credentials.json"
    assert resolved.authority is CredentialAuthority.VENDOR_MANAGED


def test_an_unverified_live_grant_is_never_claimed(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    _write_identity(account_home, "same-uuid")
    _write_credentials(live_home / ".credentials.json", "live", "live-refresh")

    resolved = resolve_credentials(account_home, live_home, identify=lambda _path: None)

    assert resolved.path == account_home / ".credentials.json"
    assert resolved.authority is CredentialAuthority.VENDOR_MANAGED


def test_an_account_without_its_own_identity_never_claims_the_live_grant(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    account_home.mkdir()
    _write_credentials(live_home / ".credentials.json", "live", "live-refresh")

    resolved = resolve_credentials(account_home, live_home, identify=_never_probed)

    assert resolved.path == account_home / ".credentials.json"
    assert resolved.authority is CredentialAuthority.VENDOR_MANAGED


def test_an_empty_live_credentials_file_is_never_probed(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    _write_identity(account_home, "same-uuid")
    _write_credentials(account_home / ".credentials.json", "own", "own-refresh")
    _write_credentials(live_home / ".credentials.json", "", "")

    resolved = resolve_credentials(account_home, live_home, identify=_never_probed)

    assert resolved.path == account_home / ".credentials.json"
    assert resolved.authority is CredentialAuthority.VENDOR_MANAGED


def test_account_home_that_is_the_live_home_is_read_only(tmp_path: Path) -> None:
    live_home = tmp_path / "live"
    _write_identity(live_home, "same-uuid")
    _write_credentials(live_home / ".credentials.json", "", "")

    resolved = resolve_credentials(live_home, live_home)

    assert resolved.path == live_home / ".credentials.json"
    assert resolved.authority is CredentialAuthority.VENDOR_MANAGED


def test_fingerprint_changes_with_the_grant_and_survives_unrelated_edits(tmp_path: Path) -> None:
    path = tmp_path / ".credentials.json"
    _write_credentials(path, "access", "refresh")
    before = credentials_fingerprint(path)

    path.write_text(
        json.dumps(
            {
                "claudeAiOauth": {
                    "accessToken": "access",
                    "refreshToken": "refresh",
                    "expiresAt": 999,
                }
            }
        ),
        encoding="utf-8",
    )
    assert credentials_fingerprint(path) == before

    _write_credentials(path, "access-new", "refresh-new")
    assert credentials_fingerprint(path) != before
    assert credentials_fingerprint(tmp_path / "missing.json") is None


def test_a_poisoned_claude_json_without_a_pin_never_wins_the_live_grant(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    account_home.mkdir()
    (account_home / ".claude.json").write_text(
        json.dumps({"oauthAccount": {"accountUuid": "live-owner-uuid"}}),
        encoding="utf-8",
    )
    _write_credentials(account_home / ".credentials.json", "own", "own-refresh")
    _write_credentials(live_home / ".credentials.json", "live", "live-refresh")

    resolved = resolve_credentials(
        account_home, live_home, identify=_live_token_owned_by("live-owner-uuid")
    )

    assert resolved.path == account_home / ".credentials.json"


def test_a_pin_matching_the_live_identity_wins_the_live_grant(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    live_home = tmp_path / "live"
    account_home.mkdir()
    pin_account_uuid(account_home, "same-uuid")
    _write_credentials(account_home / ".credentials.json", "own", "own-refresh")
    _write_credentials(live_home / ".credentials.json", "live", "live-refresh")

    resolved = resolve_credentials(
        account_home, live_home, identify=_live_token_owned_by("same-uuid")
    )

    assert resolved.path == live_home / ".credentials.json"


def test_pin_account_uuid_is_write_once(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    account_home.mkdir()

    pin_account_uuid(account_home, "first-uuid")
    pin_account_uuid(account_home, "second-uuid")

    assert pinned_account_uuid(account_home) == "first-uuid"


def test_a_malformed_pin_file_resolves_to_no_pin(tmp_path: Path) -> None:
    account_home = tmp_path / "account"
    account_home.mkdir()
    identity_pin_path(account_home).write_text("not json", encoding="utf-8")

    assert pinned_account_uuid(account_home) is None
