"""Unit tests for Azure Vault Secrets client."""

import pytest
from unittest.mock import Mock, patch, MagicMock
from azure_vault_secrets import SecretClient, SecretCache, SecretNotFoundError, AuthenticationError
from azure_vault_secrets.exceptions import AzureVaultException


class TestSecretClient:
    """Test cases for the SecretClient class."""

    @pytest.fixture
    def mock_azure_client(self):
        """Create a mock Azure secret client."""
        return MagicMock()

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_client_initialization(self, mock_secret_client, mock_credential):
        """Test client initialization."""
        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url)
        assert client.vault_url == vault_url
        assert client.enable_cache is True

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    def test_client_initialization_without_vault_url(self, mock_credential):
        """Test client initialization fails without vault URL."""
        with pytest.raises(AzureVaultException):
            SecretClient(vault_url="")

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_get_secret(self, mock_secret_client_class, mock_credential):
        """Test retrieving a secret."""
        mock_client = MagicMock()
        mock_secret_client_class.return_value = mock_client
        mock_secret = MagicMock()
        mock_secret.value = "test-secret-value"
        mock_client.get_secret.return_value = mock_secret

        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url)
        result = client.get_secret("test-secret")

        assert result == "test-secret-value"
        mock_client.get_secret.assert_called_once_with("test-secret")

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_get_secret_not_found(self, mock_secret_client_class, mock_credential):
        """Test retrieving a non-existent secret."""
        mock_client = MagicMock()
        mock_secret_client_class.return_value = mock_client
        mock_client.get_secret.side_effect = Exception("SecretNotFound")

        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url)

        with pytest.raises(SecretNotFoundError):
            client.get_secret("non-existent-secret")

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_cache_functionality(self, mock_secret_client_class, mock_credential):
        """Test that caching reduces API calls."""
        mock_client = MagicMock()
        mock_secret_client_class.return_value = mock_client
        mock_secret = MagicMock()
        mock_secret.value = "cached-secret-value"
        mock_client.get_secret.return_value = mock_secret

        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url, cache_ttl_seconds=300)

        # First call should hit Azure
        result1 = client.get_secret("test-secret")
        assert result1 == "cached-secret-value"
        assert mock_client.get_secret.call_count == 1

        # Second call should use cache
        result2 = client.get_secret("test-secret")
        assert result2 == "cached-secret-value"
        assert mock_client.get_secret.call_count == 1  # Should not increase

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_set_secret(self, mock_secret_client_class, mock_credential):
        """Test setting a secret."""
        mock_client = MagicMock()
        mock_secret_client_class.return_value = mock_client

        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url)
        client.set_secret("new-secret", "secret-value")

        mock_client.set_secret.assert_called_once_with("new-secret", "secret-value")

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_delete_secret(self, mock_secret_client_class, mock_credential):
        """Test deleting a secret."""
        mock_client = MagicMock()
        mock_secret_client_class.return_value = mock_client

        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url)
        client.delete_secret("secret-to-delete")

        mock_client.begin_delete_secret.assert_called_once_with("secret-to-delete")

    @patch("azure_vault_secrets.client.DefaultAzureCredential")
    @patch("azure_vault_secrets.client.AzureSecretClient")
    def test_list_secrets(self, mock_secret_client_class, mock_credential):
        """Test listing secrets."""
        mock_client = MagicMock()
        mock_secret_client_class.return_value = mock_client

        mock_secret_props_1 = MagicMock()
        mock_secret_props_1.name = "secret-1"
        mock_secret_props_2 = MagicMock()
        mock_secret_props_2.name = "secret-2"

        mock_client.list_properties_of_secrets.return_value = [
            mock_secret_props_1,
            mock_secret_props_2,
        ]

        vault_url = "https://test-vault.vault.azure.net/"
        client = SecretClient(vault_url=vault_url)
        secrets = client.list_secrets()

        assert secrets == ["secret-1", "secret-2"]


class TestSecretCache:
    """Test cases for the SecretCache class."""

    def test_cache_set_and_get(self):
        """Test setting and getting values from cache."""
        cache = SecretCache(ttl_seconds=300)
        cache.set("key1", "value1")
        assert cache.get("key1") == "value1"

    def test_cache_get_nonexistent_key(self):
        """Test getting a non-existent key from cache."""
        cache = SecretCache()
        assert cache.get("nonexistent") is None

    def test_cache_clear(self):
        """Test clearing the cache."""
        cache = SecretCache()
        cache.set("key1", "value1")
        cache.set("key2", "value2")
        cache.clear()
        assert cache.get("key1") is None
        assert cache.get("key2") is None

    def test_cache_remove(self):
        """Test removing a specific key from cache."""
        cache = SecretCache()
        cache.set("key1", "value1")
        cache.set("key2", "value2")
        cache.remove("key1")
        assert cache.get("key1") is None
        assert cache.get("key2") == "value2"


if __name__ == "__main__":
    pytest.main([__file__])
