"""
Tests for secure keychain storage.
"""

import os
import platform
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from kvict.keychain import (
    delete_api_key,
    get_api_key,
    store_api_key,
)


class TestKeychain:
    """Test keychain storage functionality."""

    def test_store_and_get_api_key_with_keyring(self):
        """Test storing and retrieving API key with keyring available."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", True):
            with patch("kvict.keychain.keyring") as mock_keyring:
                # Test store
                result = store_api_key("test_service", "sk-test123")
                assert result is True
                mock_keyring.set_password.assert_called_once_with(
                    "test_service", "api_key", "sk-test123"
                )

                # Test get
                mock_keyring.get_password.return_value = "sk-test123"
                key = get_api_key("test_service")
                assert key == "sk-test123"
                mock_keyring.get_password.assert_called_with("test_service", "api_key")

    def test_store_and_get_api_key_without_keyring(self):
        """Test fallback to file storage when keyring unavailable."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", False):
            with tempfile.TemporaryDirectory() as tmpdir:
                with patch("kvict.keychain._get_config_dir") as mock_dir:
                    config_dir = Path(tmpdir)
                    mock_dir.return_value = config_dir

                    # Test store
                    result = store_api_key("test_service", "sk-test123")
                    assert result is True

                    key_file = config_dir / "test_service_key.txt"
                    assert key_file.exists()
                    assert key_file.read_text() == "sk-test123"

                    # Test get
                    key = get_api_key("test_service")
                    assert key == "sk-test123"

    def test_delete_api_key_with_keyring(self):
        """Test deleting API key with keyring."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", True):
            with patch("kvict.keychain.keyring") as mock_keyring:
                result = delete_api_key("test_service")
                assert result is True
                mock_keyring.delete_password.assert_called_once_with(
                    "test_service", "api_key"
                )

    def test_delete_api_key_without_keyring(self):
        """Test deleting API key from file storage."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", False):
            with tempfile.TemporaryDirectory() as tmpdir:
                with patch("kvict.keychain._get_config_dir") as mock_dir:
                    config_dir = Path(tmpdir)
                    mock_dir.return_value = config_dir

                    # Create a key file first
                    key_file = config_dir / "test_service_key.txt"
                    key_file.write_text("sk-test123")
                    assert key_file.exists()

                    # Test delete
                    result = delete_api_key("test_service")
                    assert result is True
                    assert not key_file.exists()

    def test_keyring_fallback_on_error(self):
        """Test that file storage is used when keyring fails."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", True):
            with patch("kvict.keychain.keyring") as mock_keyring:
                with tempfile.TemporaryDirectory() as tmpdir:
                    with patch("kvict.keychain._get_config_dir") as mock_dir:
                        config_dir = Path(tmpdir)
                        mock_dir.return_value = config_dir

                        # Make keyring.set_password raise an exception
                        mock_keyring.set_password.side_effect = Exception(
                            "Keyring error"
                        )

                        # Should fall back to file storage
                        result = store_api_key("test_service", "sk-test123")
                        assert result is True

                        key_file = config_dir / "test_service_key.txt"
                        assert key_file.exists()

    def test_get_nonexistent_key(self):
        """Test getting a key that doesn't exist."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", True):
            with patch("kvict.keychain.keyring") as mock_keyring:
                mock_keyring.get_password.return_value = None
                key = get_api_key("nonexistent_service")
                assert key is None

    def test_custom_username(self):
        """Test using custom username parameter."""
        with patch("kvict.keychain.KEYRING_AVAILABLE", True):
            with patch("kvict.keychain.keyring") as mock_keyring:
                store_api_key("test_service", "sk-test123", username="custom_user")
                mock_keyring.set_password.assert_called_once_with(
                    "test_service", "custom_user", "sk-test123"
                )

                mock_keyring.get_password.return_value = "sk-test123"
                key = get_api_key("test_service", username="custom_user")
                assert key == "sk-test123"
                mock_keyring.get_password.assert_called_with(
                    "test_service", "custom_user"
                )

    def test_file_permissions(self):
        """Test that file storage sets correct permissions (Unix-like systems)."""
        if platform.system() == "Windows":
            pytest.skip("File permissions not applicable on Windows")

        with patch("kvict.keychain.KEYRING_AVAILABLE", False):
            with tempfile.TemporaryDirectory() as tmpdir:
                with patch("kvict.keychain._get_config_dir") as mock_dir:
                    config_dir = Path(tmpdir)
                    mock_dir.return_value = config_dir

                    store_api_key("test_service", "sk-test123")
                    key_file = config_dir / "test_service_key.txt"

                    # Check file permissions (should be 0o600)
                    stat_info = key_file.stat()
                    assert oct(stat_info.st_mode)[-3:] == "600"
