"""
Tests for BaseClient.
"""

from unittest.mock import MagicMock, patch

import grpc
import pytest

from daisi.base_client import BaseClient
from daisi.config import DaisiConfig


class TestBaseClient:
    """Test BaseClient."""

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

    def test_init_without_config(self) -> None:
        """Test initialization without config creates default."""
        client = BaseClient()
        assert isinstance(client.config, DaisiConfig)
        assert client._channel is None

    @patch("grpc.secure_channel")
    def test_get_channel_ssl(self, mock_secure_channel: MagicMock) -> None:
        """Test _get_channel with SSL."""
        mock_channel = MagicMock()
        mock_secure_channel.return_value = mock_channel

        config = DaisiConfig(
            client_key="test-key",
            orc_address="test.daisi.net",
            orc_port=443,
            use_ssl=True,
        )
        client = BaseClient(config)
        channel = client._get_channel()

        assert channel == mock_channel
        mock_secure_channel.assert_called_once()
        call_args = mock_secure_channel.call_args
        assert call_args[0][0] == "test.daisi.net:443"

    @patch("grpc.insecure_channel")
    def test_get_channel_no_ssl(self, mock_insecure_channel: MagicMock) -> None:
        """Test _get_channel without SSL."""
        mock_channel = MagicMock()
        mock_insecure_channel.return_value = mock_channel

        config = DaisiConfig(
            client_key="test-key",
            orc_address="localhost",
            orc_port=8080,
            use_ssl=False,
        )
        client = BaseClient(config)
        channel = client._get_channel()

        assert channel == mock_channel
        mock_insecure_channel.assert_called_once_with("localhost:8080")

    @patch("grpc.secure_channel")
    def test_get_channel_cached(self, mock_secure_channel: MagicMock) -> None:
        """Test _get_channel returns cached channel."""
        mock_channel = MagicMock()
        mock_secure_channel.return_value = mock_channel

        config = DaisiConfig(client_key="test-key")
        client = BaseClient(config)

        channel1 = client._get_channel()
        channel2 = client._get_channel()

        assert channel1 == channel2
        mock_secure_channel.assert_called_once()

    @patch("grpc.secure_channel")
    def test_get_channel_with_custom_address(
        self, mock_secure_channel: MagicMock
    ) -> None:
        """Test _get_channel with custom address."""
        mock_channel = MagicMock()
        mock_secure_channel.return_value = mock_channel

        config = DaisiConfig(client_key="test-key", use_ssl=True)
        client = BaseClient(config)
        channel = client._get_channel(address="custom.host:9999")

        assert channel == mock_channel
        mock_secure_channel.assert_called_once()
        call_args = mock_secure_channel.call_args
        assert call_args[0][0] == "custom.host:9999"

    def test_get_metadata(self) -> None:
        """Test _get_metadata includes client key and network name."""
        config = DaisiConfig(client_key="test-key", network_name="devnet")
        client = BaseClient(config)
        metadata = client._get_metadata()

        assert ("x-daisi-client-key", "test-key") in metadata
        assert ("x-daisi-network-name", "devnet") in metadata
        assert len(metadata) == 2

    def test_get_metadata_with_additional(self) -> None:
        """Test _get_metadata with additional metadata."""
        config = DaisiConfig(client_key="test-key", network_name="devnet")
        client = BaseClient(config)
        additional = [("custom-header", "custom-value")]
        metadata = client._get_metadata(additional)

        assert ("x-daisi-client-key", "test-key") in metadata
        assert ("x-daisi-network-name", "devnet") in metadata
        assert ("custom-header", "custom-value") in metadata
        assert len(metadata) == 3

    def test_close_channel(self) -> None:
        """Test close method."""
        config = DaisiConfig(client_key="test-key")
        client = BaseClient(config)

        mock_channel = MagicMock()
        client._channel = mock_channel

        client.close()

        mock_channel.close.assert_called_once()
        assert client._channel is None

    def test_close_no_channel(self) -> None:
        """Test close when no channel exists."""
        config = DaisiConfig(client_key="test-key")
        client = BaseClient(config)
        client.close()  # Should not raise

    def test_context_manager(self) -> None:
        """Test context manager protocol."""
        config = DaisiConfig(client_key="test-key")
        mock_channel = MagicMock()

        with BaseClient(config) as client:
            client._channel = mock_channel
            assert isinstance(client, BaseClient)

        mock_channel.close.assert_called_once()
