"""Tests for the KeyManager -- encrypted BYOK key storage."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from llmhosts.keys.manager import KeyManager
from llmhosts.keys.models import SUPPORTED_PROVIDERS

if TYPE_CHECKING:
    from pathlib import Path

# -------------------------------------------------------------------
# Helpers
# -------------------------------------------------------------------


@pytest.fixture()
def key_mgr(tmp_path: Path) -> KeyManager:
    """A KeyManager using a temporary directory for isolation."""
    return KeyManager(data_dir=tmp_path / ".llmhosts")


# -------------------------------------------------------------------
# Add / list / remove
# -------------------------------------------------------------------


@pytest.mark.unit
def test_add_and_list_key(key_mgr: KeyManager) -> None:
    key_mgr.add_key("openai", "sk-test-abc123456789012345")
    providers = key_mgr.list_providers()
    assert len(providers) == 1
    assert providers[0].provider == "openai"


@pytest.mark.unit
def test_add_multiple_providers(key_mgr: KeyManager) -> None:
    key_mgr.add_key("openai", "sk-test-abc123456789012345")
    key_mgr.add_key("anthropic", "sk-ant-test1234567890123")
    providers = key_mgr.list_providers()
    assert len(providers) == 2
    provider_names = {p.provider for p in providers}
    assert provider_names == {"openai", "anthropic"}


@pytest.mark.unit
def test_remove_key(key_mgr: KeyManager) -> None:
    key_mgr.add_key("openai", "sk-test-abc123456789012345")
    assert key_mgr.has_key("openai") is True
    removed = key_mgr.remove_key("openai")
    assert removed is True
    assert key_mgr.has_key("openai") is False


@pytest.mark.unit
def test_remove_nonexistent_key(key_mgr: KeyManager) -> None:
    removed = key_mgr.remove_key("openai")
    assert removed is False


@pytest.mark.unit
def test_has_key(key_mgr: KeyManager) -> None:
    assert key_mgr.has_key("openai") is False
    key_mgr.add_key("openai", "sk-test-abc123456789012345")
    assert key_mgr.has_key("openai") is True


# -------------------------------------------------------------------
# Encryption at rest
# -------------------------------------------------------------------


@pytest.mark.unit
def test_keys_encrypted_at_rest(key_mgr: KeyManager) -> None:
    """The raw key should NOT appear in the on-disk file."""
    raw_key = "sk-SUPER-SECRET-KEY-12345678"
    key_mgr.add_key("openai", raw_key)
    # Find the keys.enc file
    keys_file = key_mgr._keys_file
    assert keys_file.exists()
    raw_bytes = keys_file.read_bytes()
    # The plaintext key must not appear in the encrypted blob
    assert raw_key.encode() not in raw_bytes


@pytest.mark.unit
def test_key_decrypt_round_trip(key_mgr: KeyManager) -> None:
    """Adding then retrieving a key should return the original value."""
    raw_key = "sk-roundtrip-test-key-123456"
    key_mgr.add_key("openai", raw_key)
    decrypted = key_mgr.get_key("openai")
    assert decrypted == raw_key


@pytest.mark.unit
def test_list_providers_masks_key(key_mgr: KeyManager) -> None:
    """list_providers should return masked keys, not plaintext."""
    key_mgr.add_key("openai", "sk-test-abc123456789012345")
    providers = key_mgr.list_providers()
    masked = providers[0].masked_key
    assert "..." in masked
    assert masked != "sk-test-abc123456789012345"


# -------------------------------------------------------------------
# Validation: unsupported provider
# -------------------------------------------------------------------


@pytest.mark.unit
def test_unsupported_provider_raises(key_mgr: KeyManager) -> None:
    with pytest.raises(ValueError, match="Unsupported provider"):
        key_mgr.add_key("not-a-real-provider", "some-key")


@pytest.mark.unit
def test_empty_key_raises(key_mgr: KeyManager) -> None:
    with pytest.raises(ValueError, match="empty"):
        key_mgr.add_key("openai", "")


@pytest.mark.unit
def test_whitespace_only_key_raises(key_mgr: KeyManager) -> None:
    with pytest.raises(ValueError, match="empty"):
        key_mgr.add_key("openai", "   ")


# -------------------------------------------------------------------
# Duplicate key = update, not duplicate
# -------------------------------------------------------------------


@pytest.mark.unit
def test_duplicate_key_updates(key_mgr: KeyManager) -> None:
    """Adding a key for the same provider twice should update, not duplicate."""
    key_mgr.add_key("openai", "sk-first-key-abcdefghijk")
    key_mgr.add_key("openai", "sk-second-key-1234567890")
    providers = key_mgr.list_providers()
    assert len(providers) == 1
    # The retrieved key should be the second one
    decrypted = key_mgr.get_key("openai")
    assert decrypted == "sk-second-key-1234567890"


# -------------------------------------------------------------------
# Provider normalisation
# -------------------------------------------------------------------


@pytest.mark.unit
def test_provider_case_insensitive(key_mgr: KeyManager) -> None:
    """Provider name should be normalised to lowercase."""
    key_mgr.add_key("OpenAI", "sk-test-abc123456789012345")
    assert key_mgr.has_key("openai") is True
    assert key_mgr.has_key("OPENAI") is True


@pytest.mark.unit
def test_all_supported_providers_accepted(key_mgr: KeyManager) -> None:
    """Every provider in SUPPORTED_PROVIDERS should be accepted."""
    for provider in SUPPORTED_PROVIDERS:
        key_mgr.add_key(provider, f"test-key-for-{provider}-12345")
    providers = key_mgr.list_providers()
    assert len(providers) == len(SUPPORTED_PROVIDERS)
