"""
Tests for the connection tester utility.

This module tests the ConnectionTester class and related functionality.
"""

import unittest
from unittest.mock import MagicMock, patch

from data_exchange_agent.utils.connection_tester import (
    ConnectionTestResult,
    ConnectionTester,
    TestResult,
    print_test_results,
)


class TestTestResult(unittest.TestCase):
    """Test suite for the TestResult dataclass."""

    def test_success_result(self):
        """Test creating a successful test result."""
        result = TestResult(
            name="test_conn",
            status=ConnectionTestResult.SUCCESS,
            message="Connection OK",
        )
        self.assertTrue(result.is_success)
        self.assertEqual(result.name, "test_conn")
        self.assertIsNone(result.error_details)

    def test_failure_result(self):
        """Test creating a failed test result."""
        result = TestResult(
            name="test_conn",
            status=ConnectionTestResult.FAILURE,
            message="Connection failed",
            error_details="Error traceback here",
        )
        self.assertFalse(result.is_success)
        self.assertEqual(result.error_details, "Error traceback here")

    def test_skipped_result(self):
        """Test creating a skipped test result."""
        result = TestResult(
            name="test_conn",
            status=ConnectionTestResult.SKIPPED,
            message="Not a testable connection",
        )
        self.assertFalse(result.is_success)


class TestConnectionTester(unittest.TestCase):
    """Test suite for the ConnectionTester class."""

    def setUp(self):
        """Set up test fixtures."""
        self.mock_config = MagicMock()

    def test_init(self):
        """Test ConnectionTester initialization."""
        tester = ConnectionTester(self.mock_config)
        self.assertEqual(tester._config, self.mock_config)

    def test_test_source_connections_empty(self):
        """Test when no source connections are configured."""
        self.mock_config.get.return_value = {}
        tester = ConnectionTester(self.mock_config)

        results = tester.test_source_connections()

        self.assertEqual(results, [])

    @patch.dict("sys.modules", {"pyodbc": MagicMock()})
    def test_test_odbc_connection_success(self):
        """Test successful ODBC connection test."""
        import sys

        # Setup mock
        mock_pyodbc = sys.modules["pyodbc"]
        mock_conn = MagicMock()
        mock_cursor = MagicMock()
        mock_conn.cursor.return_value = mock_cursor
        mock_pyodbc.connect.return_value = mock_conn

        mock_conn_config = MagicMock()
        mock_conn_config.connection_string = "DRIVER={...};SERVER=localhost"

        self.mock_config.get.return_value = {"sqlserver": mock_conn_config}
        tester = ConnectionTester(self.mock_config)

        results = tester.test_source_connections()

        self.assertEqual(len(results), 1)
        self.assertEqual(results[0].status, ConnectionTestResult.SUCCESS)
        self.assertEqual(results[0].name, "sqlserver")

    @patch.dict("sys.modules", {"pyodbc": MagicMock()})
    def test_test_odbc_connection_failure(self):
        """Test failed ODBC connection test."""
        import sys

        mock_pyodbc = sys.modules["pyodbc"]
        mock_pyodbc.connect.side_effect = Exception("Connection refused")

        mock_conn_config = MagicMock()
        mock_conn_config.connection_string = "DRIVER={...};SERVER=localhost"

        self.mock_config.get.return_value = {"sqlserver": mock_conn_config}
        tester = ConnectionTester(self.mock_config)

        results = tester.test_source_connections()

        self.assertEqual(len(results), 1)
        self.assertEqual(results[0].status, ConnectionTestResult.FAILURE)
        self.assertIn("Connection refused", results[0].message)

    def test_test_target_connections_empty(self):
        """Test when no target connections are configured."""
        self.mock_config.get.return_value = {}
        tester = ConnectionTester(self.mock_config)

        results = tester.test_target_connections()

        self.assertEqual(results, [])

    def test_test_target_connections_non_snowflake_skipped(self):
        """Test that non-Snowflake connections are skipped."""
        # Non-Snowflake config (e.g., S3)
        mock_s3_config = MagicMock()
        mock_s3_config.__class__.__name__ = "S3Config"

        self.mock_config.get.return_value = {"s3": mock_s3_config}
        tester = ConnectionTester(self.mock_config)

        results = tester.test_target_connections()

        # Skipped results are not added to the list
        self.assertEqual(results, [])

    def test_test_snowflake_connection_name_success(self):
        """Test successful Snowflake named connection test."""
        from data_exchange_agent.config.sections.connections.cloud_storages.snowflake import (
            SnowflakeConnectionNameConfig,
        )

        # Create a mock module structure for snowflake.connector
        mock_snowflake = MagicMock()
        mock_conn = MagicMock()
        mock_cursor = MagicMock()
        mock_conn.cursor.return_value = mock_cursor
        mock_snowflake.connector.connect.return_value = mock_conn

        mock_conn_config = MagicMock(spec=SnowflakeConnectionNameConfig)
        mock_conn_config.connection_name = "my_connection"

        self.mock_config.get.return_value = {"snowflake": mock_conn_config}
        tester = ConnectionTester(self.mock_config)

        with patch.dict(
            "sys.modules",
            {
                "snowflake": mock_snowflake,
                "snowflake.connector": mock_snowflake.connector,
            },
        ):
            results = tester.test_target_connections()

        self.assertEqual(len(results), 1)
        self.assertEqual(results[0].status, ConnectionTestResult.SUCCESS)

    def test_test_all_connections(self):
        """Test testing all connections at once."""
        self.mock_config.get.return_value = {}
        tester = ConnectionTester(self.mock_config)

        results, all_passed = tester.test_all_connections()

        self.assertEqual(results, [])
        self.assertTrue(all_passed)


class TestPrintTestResults(unittest.TestCase):
    """Test suite for the print_test_results function."""

    @patch("builtins.print")
    def test_print_success_results(self, mock_print):
        """Test printing successful results."""
        results = [
            TestResult(
                name="conn1",
                status=ConnectionTestResult.SUCCESS,
                message="Connection OK",
            )
        ]

        print_test_results(results, all_passed=True)

        # Verify print was called with success message
        calls = [str(call) for call in mock_print.call_args_list]
        self.assertTrue(any("conn1" in call for call in calls))

    @patch("builtins.print")
    def test_print_failure_results(self, mock_print):
        """Test printing failed results."""
        results = [
            TestResult(
                name="conn1",
                status=ConnectionTestResult.FAILURE,
                message="Connection failed",
                error_details="Error details here",
            )
        ]

        print_test_results(results, all_passed=False)

        # Verify print was called with failure message
        calls = [str(call) for call in mock_print.call_args_list]
        self.assertTrue(any("conn1" in call for call in calls))


if __name__ == "__main__":
    unittest.main()
