"""Tests for SQLServerConnectionConfig with automatic driver detection."""

from unittest.mock import MagicMock, patch

import pytest

from data_exchange_agent.config.sections.connections.odbc.sqlserver import (
    SQLServerConnectionConfig,
)


@pytest.fixture
def mock_available_drivers():
    """Mock available SQL Server drivers."""
    return [
        "ODBC Driver 18 for SQL Server",
        "ODBC Driver 17 for SQL Server",
        "PostgreSQL Unicode",
    ]


class TestAutoDriverDetection:
    """Tests for automatic ODBC driver detection in SQLServerConnectionConfig."""

    def test_no_driver_specified_auto_detects(self, mock_available_drivers):
        """Test that when no driver is specified, it auto-detects the best one."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # User doesn't specify any driver
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
            )

            # Should auto-detect and use driver 18
            assert config.odbc_driver == "ODBC Driver 18 for SQL Server"

    def test_available_driver_specified_uses_it(self, mock_available_drivers):
        """Test that when an available driver is specified, it uses it."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # User specifies driver 17
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
                odbc_driver="ODBC Driver 17 for SQL Server",
            )

            # Should use the specified driver
            assert config.odbc_driver == "ODBC Driver 17 for SQL Server"

    def test_unavailable_driver_falls_back_with_auto_detect(self, mock_available_drivers):
        """Test that unavailable driver falls back to best available when auto_detect=True."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # User specifies driver 13 which is not available
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
                odbc_driver="ODBC Driver 13 for SQL Server",
                # auto_detect_driver=True by default
            )

            # Should fall back to driver 18
            assert config.odbc_driver == "ODBC Driver 18 for SQL Server"

    def test_unavailable_driver_accepted_without_auto_detect(self, mock_available_drivers):
        """Test that unavailable driver is accepted when auto_detect=False (for testing)."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # User specifies driver 13 with auto_detect disabled
            # This should work without validation (useful for testing)
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
                odbc_driver="ODBC Driver 13 for SQL Server",
                auto_detect_driver=False,
            )

            # Should use the specified driver without validation
            assert config.odbc_driver == "ODBC Driver 13 for SQL Server"

    def test_no_drivers_available_raises_helpful_error(self):
        """Test that helpful error is raised when no drivers are available."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = []
            mock_get_pyodbc.return_value = mock_pyodbc

            with pytest.raises(ValueError) as exc_info:
                SQLServerConnectionConfig(
                    database="testdb",
                    host="localhost",
                    username="sa",
                    password="password",
                )

            error_message = str(exc_info.value)
            assert "No SQL Server ODBC drivers found" in error_message
            # Should include reference to installation documentation
            assert "microsoft.com" in error_message.lower()
            assert "download-odbc-driver-for-sql-server" in error_message.lower()

    def test_fallback_prefers_driver_17_when_18_unavailable(self):
        """Test that fallback correctly selects driver 17 when 18 is not available."""
        drivers = ["ODBC Driver 17 for SQL Server", "PostgreSQL Unicode"]
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # User doesn't specify driver
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
            )

            # Should auto-detect driver 17
            assert config.odbc_driver == "ODBC Driver 17 for SQL Server"

    def test_windows_auth_with_auto_detect(self, mock_available_drivers):
        """Test Windows authentication works with auto-detection."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                use_windows_auth=True,
                # No driver specified, should auto-detect
            )

            assert config.odbc_driver == "ODBC Driver 18 for SQL Server"
            assert config.use_windows_auth is True

    def test_connection_string_includes_detected_driver(self, mock_available_drivers):
        """Test that connection string includes the auto-detected driver."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
            )

            conn_str = config.build_connection_string()
            assert "DRIVER={ODBC Driver 18 for SQL Server}" in conn_str

    def test_explicit_none_triggers_auto_detect(self, mock_available_drivers):
        """Test that explicitly passing None for driver triggers auto-detection."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
                odbc_driver=None,  # Explicitly None
            )

            assert config.odbc_driver == "ODBC Driver 18 for SQL Server"


class TestBackwardCompatibility:
    """Tests to ensure backward compatibility with existing code."""

    def test_old_code_with_explicit_driver_still_works(self, mock_available_drivers):
        """Test that old code specifying a driver explicitly still works."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # Old code that explicitly specifies driver 18
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
                odbc_driver="ODBC Driver 18 for SQL Server",
            )

            assert config.odbc_driver == "ODBC Driver 18 for SQL Server"

    def test_old_code_without_driver_gets_auto_detect(self, mock_available_drivers):
        """Test that old code without driver parameter gets auto-detection."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_available_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            # Old code without odbc_driver parameter
            config = SQLServerConnectionConfig(
                database="testdb",
                host="localhost",
                username="sa",
                password="password",
            )

            # Should get auto-detected driver
            assert config.odbc_driver == "ODBC Driver 18 for SQL Server"
