from __future__ import annotations

import keyring
import pytest
from keyring.backend import KeyringBackend
from keyring.errors import KeyringError

from cli.exceptions import CLIError
from cli.keychain import (
    AuthSource,
    get_api_key,
    get_api_key_with_env_override,
    get_env_api_key_override,
    store_api_key,
)


class InMemoryKeyring(KeyringBackend):
    """Simple keyring backend for tests."""

    priority = 1

    def __init__(self) -> None:
        self._storage: dict[tuple[str, str], str] = {}

    def get_password(self, service: str, username: str) -> str | None:
        return self._storage.get((service, username))

    def set_password(self, service: str, username: str, password: str) -> None:
        self._storage[(service, username)] = password

    def delete_password(self, service: str, username: str) -> None:
        self._storage.pop((service, username), None)


def test_store_and_retrieve_api_key(monkeypatch):
    backend = InMemoryKeyring()
    keyring.set_keyring(backend)

    api_key = "ctxme_" + ("A" * 32)
    store_api_key(api_key)

    assert get_api_key() == api_key


def test_env_override_returns_value_and_source(monkeypatch):
    backend = InMemoryKeyring()
    keyring.set_keyring(backend)

    env_key = "ctxme_" + ("B" * 32)
    keychain_key = "ctxme_" + ("C" * 32)
    store_api_key(keychain_key)

    monkeypatch.setenv("CLI__API_KEY", env_key)
    api_key, source = get_api_key_with_env_override()

    assert api_key == env_key
    assert source == AuthSource.ENV


def test_env_override_invalid_raises_clierror(monkeypatch):
    monkeypatch.setenv("CLI__API_KEY", "invalid-key")

    with pytest.raises(CLIError, match="Invalid API key format in CLI__API_KEY"):
        get_env_api_key_override()


def test_env_override_whitespace_falls_back_to_keychain(monkeypatch):
    backend = InMemoryKeyring()
    keyring.set_keyring(backend)

    keychain_key = "ctxme_" + ("D" * 32)
    store_api_key(keychain_key)

    monkeypatch.setenv("CLI__API_KEY", "   ")
    api_key, source = get_api_key_with_env_override()

    assert api_key == keychain_key
    assert source == AuthSource.KEYCHAIN


def test_env_override_bypasses_keyring_errors(monkeypatch):
    env_key = "ctxme_" + ("E" * 32)
    monkeypatch.setenv("CLI__API_KEY", env_key)

    def _raise_keyring_error(*_args, **_kwargs):
        raise KeyringError("no keyring")

    monkeypatch.setattr(keyring, "get_password", _raise_keyring_error)
    api_key, source = get_api_key_with_env_override()

    assert api_key == env_key
    assert source == AuthSource.ENV
