"""
Tests for AuthClient.
"""

from unittest.mock import MagicMock, patch

import grpc
import pytest

from daisi.clients.auth import AuthClient
from daisi.config import DaisiConfig
from daisi.exceptions import AuthenticationError, DaisiError
from protos.v1.models import AuthModels_pb2


def create_grpc_error(details: str = "Test error") -> grpc.RpcError:
    """Create a mock gRPC error with details method."""
    error = grpc.RpcError()
    error.details = MagicMock(return_value=details)
    return error


class TestAuthClientInitialization:
    """Test AuthClient initialization."""

    def test_init_with_config(self) -> None:
        """Test initialization with config."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)
        assert client.config == config
        assert client._stub is None

    def test_stub_lazy_initialization(self) -> None:
        """Test stub is lazily initialized."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        with patch.object(client, "_get_channel") as mock_get_channel:
            mock_channel = MagicMock()
            mock_get_channel.return_value = mock_channel

            stub = client.stub
            assert stub is not None
            mock_get_channel.assert_called_once()


class TestAuthClientCreateClientKey:
    """Test AuthClient create_client_key method."""

    def test_create_client_key_success(self) -> None:
        """Test successful client key creation."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.ClientKey = "new-client-key"
        mock_response.OwnerId = "owner-123"
        mock_stub.CreateClientKey.return_value = mock_response

        client._stub = mock_stub
        client_key, owner_id = client.create_client_key(
            secret_key="secret",
            owner_id="owner-123",
            owner_name="Test Owner",
            owner_role=AuthModels_pb2.SystemRolesUser,
        )

        assert client_key == "new-client-key"
        assert owner_id == "owner-123"
        mock_stub.CreateClientKey.assert_called_once()

    def test_create_client_key_with_access_ids(self) -> None:
        """Test client key creation with access IDs."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.ClientKey = "new-client-key"
        mock_response.OwnerId = "owner-123"
        mock_stub.CreateClientKey.return_value = mock_response

        client._stub = mock_stub
        client.create_client_key(
            secret_key="secret",
            owner_id="owner-123",
            owner_name="Test Owner",
            owner_role=AuthModels_pb2.SystemRolesUser,
            access_to_ids=["id1", "id2"],
        )

        call_args = mock_stub.CreateClientKey.call_args
        request = call_args[0][0]
        assert request.AccessToIds == ["id1", "id2"]

    def test_create_client_key_grpc_error(self) -> None:
        """Test client key creation with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_stub.CreateClientKey.side_effect = create_grpc_error()

        client._stub = mock_stub
        with pytest.raises(AuthenticationError):
            client.create_client_key(
                secret_key="secret",
                owner_id="owner-123",
                owner_name="Test Owner",
                owner_role=AuthModels_pb2.SystemRolesUser,
            )


class TestAuthClientValidateClientKey:
    """Test AuthClient validate_client_key method."""

    def test_validate_client_key_valid(self) -> None:
        """Test validating a valid client key."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.IsValid = True
        mock_stub.ValidateClientKey.return_value = mock_response

        client._stub = mock_stub
        result = client.validate_client_key(secret_key="secret", client_key="test-key")

        assert result is True
        mock_stub.ValidateClientKey.assert_called_once()

    def test_validate_client_key_invalid(self) -> None:
        """Test validating an invalid client key."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.IsValid = False
        mock_stub.ValidateClientKey.return_value = mock_response

        client._stub = mock_stub
        result = client.validate_client_key(
            secret_key="secret", client_key="invalid-key"
        )

        assert result is False

    def test_validate_client_key_grpc_error(self) -> None:
        """Test validate client key with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_stub.ValidateClientKey.side_effect = create_grpc_error()

        client._stub = mock_stub
        with pytest.raises(DaisiError):
            client.validate_client_key(secret_key="secret", client_key="test-key")


class TestAuthClientSendAuthCode:
    """Test AuthClient send_auth_code method."""

    def test_send_auth_code_success(self) -> None:
        """Test successful auth code sending."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Success = True
        mock_stub.SendAuthCode.return_value = mock_response

        client._stub = mock_stub
        result = client.send_auth_code("test@example.com")

        assert result is True
        mock_stub.SendAuthCode.assert_called_once()

    def test_send_auth_code_failure(self) -> None:
        """Test auth code sending failure."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Success = False
        mock_response.ErrorMessage = "Failed to send"
        mock_stub.SendAuthCode.return_value = mock_response

        client._stub = mock_stub
        with pytest.raises(DaisiError, match="Failed to send auth code"):
            client.send_auth_code("test@example.com")

    def test_send_auth_code_grpc_error(self) -> None:
        """Test send auth code with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_stub.SendAuthCode.side_effect = create_grpc_error()

        client._stub = mock_stub
        with pytest.raises(DaisiError):
            client.send_auth_code("test@example.com")


class TestAuthClientValidateAuthCode:
    """Test AuthClient validate_auth_code method."""

    def test_validate_auth_code_success(self) -> None:
        """Test successful auth code validation."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Success = True
        mock_response.ClientKey = "new-key"
        mock_response.UserName = "Test User"
        mock_response.AccountName = "Test Account"
        mock_response.AccountId = "account-123"
        mock_stub.ValidateAuthCode.return_value = mock_response

        client._stub = mock_stub
        client_key, user_name, account_name, account_id = client.validate_auth_code(
            secret_key="secret",
            email_or_phone="test@example.com",
            auth_code="123456",
            app_id="test-app",
        )

        assert client_key == "new-key"
        assert user_name == "Test User"
        assert account_name == "Test Account"
        assert account_id == "account-123"

    def test_validate_auth_code_failure(self) -> None:
        """Test auth code validation failure."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Success = False
        mock_response.ErrorMessage = "Invalid code"
        mock_stub.ValidateAuthCode.return_value = mock_response

        client._stub = mock_stub
        with pytest.raises(AuthenticationError, match="Auth code validation failed"):
            client.validate_auth_code(
                secret_key="secret",
                email_or_phone="test@example.com",
                auth_code="wrong",
                app_id="test-app",
            )

    def test_validate_auth_code_grpc_error(self) -> None:
        """Test validate auth code with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = AuthClient(config)

        mock_stub = MagicMock()
        mock_stub.ValidateAuthCode.side_effect = create_grpc_error()

        client._stub = mock_stub
        with pytest.raises(AuthenticationError):
            client.validate_auth_code(
                secret_key="secret",
                email_or_phone="test@example.com",
                auth_code="123456",
                app_id="test-app",
            )
