"""Tests for ODBC driver utility functions."""

from unittest.mock import MagicMock, patch

import pytest

from shared.sqlserver.odbc_driver_utils import (
    get_all_odbc_drivers,
    get_best_sqlserver_driver,
    get_sqlserver_drivers,
    is_driver_available,
    validate_sqlserver_driver,
)


@pytest.fixture
def mock_pyodbc_drivers():
    """Mock pyodbc.drivers() with a realistic set of drivers."""
    return [
        "ODBC Driver 18 for SQL Server",
        "ODBC Driver 17 for SQL Server",
        "PostgreSQL Unicode",
        "MySQL ODBC 8.0 Driver",
        "SQLite3 ODBC Driver",
    ]


@pytest.fixture
def mock_pyodbc_sqlserver_only():
    """Mock pyodbc.drivers() with only SQL Server drivers."""
    return [
        "ODBC Driver 18 for SQL Server",
        "ODBC Driver 17 for SQL Server",
        "ODBC Driver 13 for SQL Server",
        "SQL Server Native Client 11.0",
    ]


@pytest.fixture
def mock_pyodbc_no_drivers():
    """Mock pyodbc.drivers() with no drivers."""
    return []


class TestGetAllOdbcDrivers:
    """Tests for get_all_odbc_drivers function."""

    def test_get_all_odbc_drivers_success(self, mock_pyodbc_drivers):
        """Test getting all ODBC drivers successfully."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            result = get_all_odbc_drivers()

            assert result == mock_pyodbc_drivers
            mock_pyodbc.drivers.assert_called_once()

    def test_get_all_odbc_drivers_import_error(self):
        """Test handling of ImportError when pyodbc is not available."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_get_pyodbc.side_effect = ImportError("pyodbc not found")

            result = get_all_odbc_drivers()

            assert result == []

    def test_get_all_odbc_drivers_exception(self):
        """Test handling of general exceptions."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.side_effect = RuntimeError("ODBC error")
            mock_get_pyodbc.return_value = mock_pyodbc

            result = get_all_odbc_drivers()

            assert result == []


class TestGetSqlserverDrivers:
    """Tests for get_sqlserver_drivers function."""

    def test_get_sqlserver_drivers_mixed(self, mock_pyodbc_drivers):
        """Test filtering SQL Server drivers from mixed driver list."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            result = get_sqlserver_drivers()

            assert len(result) == 2
            assert "ODBC Driver 18 for SQL Server" in result
            assert "ODBC Driver 17 for SQL Server" in result
            assert "PostgreSQL Unicode" not in result

    def test_get_sqlserver_drivers_only_sqlserver(self, mock_pyodbc_sqlserver_only):
        """Test getting drivers when only SQL Server drivers exist."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_sqlserver_only
            mock_get_pyodbc.return_value = mock_pyodbc

            result = get_sqlserver_drivers()

            assert len(result) == 4
            assert result == mock_pyodbc_sqlserver_only

    def test_get_sqlserver_drivers_none_found(self, mock_pyodbc_no_drivers):
        """Test when no SQL Server drivers are found."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_no_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            result = get_sqlserver_drivers()

            assert result == []

    def test_get_sqlserver_drivers_no_sqlserver(self):
        """Test when drivers exist but none are SQL Server."""
        drivers = ["PostgreSQL Unicode", "MySQL ODBC 8.0 Driver"]
        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

            result = get_sqlserver_drivers()

            assert result == []


class TestGetBestSqlserverDriver:
    """Tests for get_best_sqlserver_driver function."""

    def test_get_best_driver_prefers_18(self):
        """Test that driver 18 is preferred when available."""
        drivers = [
            "ODBC Driver 17 for SQL Server",
            "ODBC Driver 18 for SQL Server",
            "ODBC Driver 13 for SQL Server",
        ]
        with patch("shared.sqlserver.odbc_driver_utils.get_sqlserver_drivers") as mock_get_drivers:
            mock_get_drivers.return_value = drivers

            result = get_best_sqlserver_driver()

            assert result == "ODBC Driver 18 for SQL Server"

    def test_get_best_driver_falls_back_to_17(self):
        """Test fallback to driver 17 when 18 is not available."""
        drivers = [
            "ODBC Driver 17 for SQL Server",
            "ODBC Driver 13 for SQL Server",
        ]
        with patch("shared.sqlserver.odbc_driver_utils.get_sqlserver_drivers") as mock_get_drivers:
            mock_get_drivers.return_value = drivers

            result = get_best_sqlserver_driver()

            assert result == "ODBC Driver 17 for SQL Server"

    def test_get_best_driver_native_client(self):
        """Test selection of native client when ODBC drivers not available."""
        drivers = [
            "SQL Server Native Client 10.0",
            "SQL Server Native Client 11.0",
        ]
        with patch("shared.sqlserver.odbc_driver_utils.get_sqlserver_drivers") as mock_get_drivers:
            mock_get_drivers.return_value = drivers

            result = get_best_sqlserver_driver()

            assert result == "SQL Server Native Client 11.0"

    def test_get_best_driver_unknown_driver(self):
        """Test handling of unknown SQL Server driver."""
        drivers = ["Custom SQL Server Driver"]
        with patch("shared.sqlserver.odbc_driver_utils.get_sqlserver_drivers") as mock_get_drivers:
            mock_get_drivers.return_value = drivers

            result = get_best_sqlserver_driver()

            assert result == "Custom SQL Server Driver"

    def test_get_best_driver_no_drivers(self):
        """Test when no SQL Server drivers are available."""
        with patch("shared.sqlserver.odbc_driver_utils.get_sqlserver_drivers") as mock_get_drivers:
            mock_get_drivers.return_value = []

            result = get_best_sqlserver_driver()

            assert result is None


class TestIsDriverAvailable:
    """Tests for is_driver_available function."""

    def test_is_driver_available_found(self, mock_pyodbc_drivers):
        """Test checking for a driver that exists."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            result = is_driver_available("ODBC Driver 18 for SQL Server")

            assert result is True

    def test_is_driver_available_not_found(self, mock_pyodbc_drivers):
        """Test checking for a driver that doesn't exist."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            result = is_driver_available("ODBC Driver 99 for SQL Server")

            assert result is False

    def test_is_driver_available_case_sensitive(self, mock_pyodbc_drivers):
        """Test that driver name check is case-sensitive."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            result = is_driver_available("odbc driver 18 for sql server")

            assert result is False


class TestValidateSqlserverDriver:
    """Tests for validate_sqlserver_driver function."""

    def test_validate_driver_valid(self, mock_pyodbc_drivers):
        """Test validation of a valid driver."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            is_valid, message = validate_sqlserver_driver("ODBC Driver 18 for SQL Server")

            assert is_valid is True
            assert "available" in message.lower()

    def test_validate_driver_empty_name(self):
        """Test validation with empty driver name."""
        is_valid, message = validate_sqlserver_driver("")

        assert is_valid is False
        assert "empty" in message.lower()

    def test_validate_driver_whitespace_only(self):
        """Test validation with whitespace-only driver name."""
        is_valid, message = validate_sqlserver_driver("   ")

        assert is_valid is False
        assert "empty" in message.lower()

    def test_validate_driver_not_found_with_suggestions(self, mock_pyodbc_drivers):
        """Test validation when driver not found but suggestions available."""
        with patch("shared.odbc.driver_utils._get_pyodbc") as mock_get_pyodbc:
            mock_pyodbc = MagicMock()
            mock_pyodbc.drivers.return_value = mock_pyodbc_drivers
            mock_get_pyodbc.return_value = mock_pyodbc

            is_valid, message = validate_sqlserver_driver("ODBC Driver 99 for SQL Server")

            assert is_valid is False
            assert "not found" in message.lower()
            assert "ODBC Driver 18 for SQL Server" in message
            assert "ODBC Driver 17 for SQL Server" in message

    def test_validate_driver_no_drivers_available(self):
        """Test validation when no ODBC drivers are installed."""
        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

            is_valid, message = validate_sqlserver_driver("ODBC Driver 18 for SQL Server")

            assert is_valid is False
            assert "no odbc drivers found" in message.lower()

    def test_validate_driver_no_sqlserver_drivers(self):
        """Test validation when no SQL Server drivers are installed."""
        drivers = ["PostgreSQL Unicode", "MySQL ODBC 8.0 Driver"]
        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

            is_valid, message = validate_sqlserver_driver("ODBC Driver 18 for SQL Server")

            assert is_valid is False
            assert "not found" in message.lower()
            assert "installed" in message.lower()
