"""Unit tests for debug mode functionality.

Tests cover:
- Debug mode enabled via environment variable
- Debug mode enabled via config
- Debug mode disabled (default)
- Verbose logging during SDK initialization
- Verbose logging during prediction recording
- Performance impact when debug mode is disabled
"""

import pytest
import logging
import os
from unittest.mock import Mock, patch, MagicMock
from mca_sdk import MCAClient
from mca_sdk.config import MCAConfig


class TestDebugModeConfiguration:
    """Test debug mode configuration loading."""

    def test_debug_mode_disabled_by_default(self):
        """Test debug mode is disabled by default."""
        config = MCAConfig(service_name="test-service")
        assert config.debug_mode is False

    def test_debug_mode_enabled_via_config(self):
        """Test debug mode can be enabled via config."""
        config = MCAConfig(service_name="test-service", debug_mode=True)
        assert config.debug_mode is True

    def test_debug_mode_enabled_via_env_var_true(self):
        """Test debug mode enabled via MCA_DEBUG=true."""
        with patch.dict(os.environ, {"MCA_DEBUG": "true", "MCA_SERVICE_NAME": "test-service"}):
            config = MCAConfig.from_env()
            assert config.debug_mode is True

    def test_debug_mode_enabled_via_env_var_1(self):
        """Test debug mode enabled via MCA_DEBUG=1."""
        with patch.dict(os.environ, {"MCA_DEBUG": "1", "MCA_SERVICE_NAME": "test-service"}):
            config = MCAConfig.from_env()
            assert config.debug_mode is True

    def test_debug_mode_enabled_via_env_var_yes(self):
        """Test debug mode enabled via MCA_DEBUG=yes."""
        with patch.dict(os.environ, {"MCA_DEBUG": "yes", "MCA_SERVICE_NAME": "test-service"}):
            config = MCAConfig.from_env()
            assert config.debug_mode is True

    def test_debug_mode_disabled_via_env_var_false(self):
        """Test debug mode disabled via MCA_DEBUG=false."""
        with patch.dict(os.environ, {"MCA_DEBUG": "false", "MCA_SERVICE_NAME": "test-service"}):
            config = MCAConfig.from_env()
            assert config.debug_mode is False


class TestDebugModeLogging:
    """Test debug mode logging functionality."""

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    def test_debug_mode_enables_verbose_logging(self, mock_check_version, mock_setup_providers):
        """Test debug mode enables verbose logging with DEBUG level."""
        # Setup mocks
        mock_setup_providers.return_value = (
            MagicMock(),  # meter_provider
            MagicMock(),  # logger_provider
            MagicMock(),  # tracer_provider
            MagicMock(),  # resource
            MagicMock(),  # metric_exporter
            MagicMock(),  # log_exporter
            MagicMock(),  # span_exporter
            MagicMock(),  # prometheus_reader
            MagicMock(),  # prometheus_shutdown
        )

        # Create client with debug mode enabled
        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=True,
        )

        # Capture log records to verify debug logging
        with (
            patch("opentelemetry.metrics.get_meter"),
            patch("opentelemetry.trace.get_tracer"),
            patch("logging.getLogger") as mock_get_logger,
        ):
            mock_logger = MagicMock()
            mock_get_logger.return_value = mock_logger

            client = MCAClient(config=config)

        # Verify logger level was set to DEBUG
        set_level_calls = [call[0][0] for call in mock_logger.setLevel.call_args_list]
        assert logging.DEBUG in set_level_calls, f"Expected DEBUG level (10) in {set_level_calls}"

        # Verify debug messages were logged during initialization
        debug_calls = [call for call in mock_logger.debug.call_args_list]
        assert len(debug_calls) > 0

        # Verify key initialization messages were logged
        debug_messages = [str(call) for call in debug_calls]
        debug_text = " ".join(debug_messages)

        assert "Debug mode enabled" in debug_text
        assert "MCA SDK version" in debug_text
        assert "Configuration loaded" in debug_text

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_logs_config_details(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug mode logs configuration details during initialization."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            model_type="internal",
            collector_endpoint="http://localhost:4318",
            debug_mode=True,
        )

        with (
            patch("opentelemetry.metrics.get_meter"),
            patch("opentelemetry.trace.get_tracer"),
        ):
            client = MCAClient(config=config)

        # Verify configuration details were logged
        debug_calls = [str(call) for call in mock_logger.debug.call_args_list]
        debug_text = " ".join(debug_calls)

        assert "service_name=test-service" in debug_text
        assert (
            "model_type=regression" in debug_text
        )  # After migration from deprecated model_type='internal'
        assert "collector_endpoint=http://localhost:4318" in debug_text

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_logs_provider_setup(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug mode logs OpenTelemetry provider setup."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=True,
        )

        with (
            patch("opentelemetry.metrics.get_meter"),
            patch("opentelemetry.trace.get_tracer"),
        ):
            client = MCAClient(config=config)

        # Verify provider setup was logged
        debug_calls = [str(call) for call in mock_logger.debug.call_args_list]
        debug_text = " ".join(debug_calls)

        assert (
            "Setting up OpenTelemetry providers" in debug_text
            or "OpenTelemetry providers initialized" in debug_text
        )

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_logs_prediction_recording(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug mode logs prediction recording details."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=True,
        )

        with (
            patch("opentelemetry.metrics.get_meter") as mock_get_meter,
            patch("opentelemetry.trace.get_tracer"),
        ):
            # Mock meter and metrics
            mock_meter = MagicMock()
            mock_counter = MagicMock()
            mock_histogram = MagicMock()
            mock_get_meter.return_value = mock_meter
            mock_meter.create_counter.return_value = mock_counter
            mock_meter.create_histogram.return_value = mock_histogram

            client = MCAClient(config=config)

            # Reset debug call count before recording prediction
            mock_logger.debug.reset_mock()

            # Record a prediction
            client.record_prediction(
                input_data={"feature1": 0.5},
                output={"prediction": 1},
                latency=0.123,
                model_version="1.0.0",
            )

        # Verify prediction recording was logged
        debug_calls = [str(call) for call in mock_logger.debug.call_args_list]
        debug_text = " ".join(debug_calls)

        assert "Recording prediction" in debug_text
        assert "latency=0.123" in debug_text
        assert "Metric recorded" in debug_text or "metric" in debug_text.lower()

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_includes_prediction_id_in_logs(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug mode includes prediction_id for log correlation."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=True,
        )

        with (
            patch("opentelemetry.metrics.get_meter") as mock_get_meter,
            patch("opentelemetry.trace.get_tracer"),
        ):
            mock_meter = MagicMock()
            mock_counter = MagicMock()
            mock_histogram = MagicMock()
            mock_get_meter.return_value = mock_meter
            mock_meter.create_counter.return_value = mock_counter
            mock_meter.create_histogram.return_value = mock_histogram

            client = MCAClient(config=config)
            mock_logger.debug.reset_mock()

            # Record prediction
            client.record_prediction(latency=0.1)

        # Verify prediction_id appears in debug logs
        debug_calls = [str(call) for call in mock_logger.debug.call_args_list]
        debug_text = " ".join(debug_calls)

        # Should see UUID format in logs (e.g., [abc-123-def])
        import re

        uuid_pattern = r"\[[\w-]+\]"
        assert re.search(uuid_pattern, debug_text), "Prediction ID not found in debug logs"

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_disabled_no_verbose_logs(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug mode disabled means no verbose DEBUG logs."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=False,  # Explicitly disabled
        )

        with (
            patch("opentelemetry.metrics.get_meter"),
            patch("opentelemetry.trace.get_tracer"),
        ):
            client = MCAClient(config=config)

        # Verify logger level was NOT set to DEBUG
        # (Should remain at default or not be changed)
        set_level_calls = [
            call for call in mock_logger.setLevel.call_args_list if call[0][0] == logging.DEBUG
        ]
        assert len(set_level_calls) == 0, "DEBUG level should not be set when debug_mode=False"


class TestDebugModePerformance:
    """Test debug mode performance characteristics."""

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_logs_not_called_when_disabled(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug logging calls are skipped when debug mode is disabled."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=False,
        )

        with (
            patch("opentelemetry.metrics.get_meter") as mock_get_meter,
            patch("opentelemetry.trace.get_tracer"),
        ):
            mock_meter = MagicMock()
            mock_counter = MagicMock()
            mock_histogram = MagicMock()
            mock_get_meter.return_value = mock_meter
            mock_meter.create_counter.return_value = mock_counter
            mock_meter.create_histogram.return_value = mock_histogram

            client = MCAClient(config=config)

            # Clear any initialization debug calls
            mock_logger.debug.reset_mock()

            # Record prediction with debug mode OFF
            client.record_prediction(latency=0.1)

        # When debug mode is off, debug() should not be called
        # (Implementation may still call it, but logger won't output at DEBUG level)
        # The key is that setLevel(DEBUG) was never called
        set_level_debug_calls = [
            call for call in mock_logger.setLevel.call_args_list if call[0][0] == logging.DEBUG
        ]
        assert len(set_level_debug_calls) == 0


class TestDebugModeIntegration:
    """Integration tests for debug mode with other SDK features."""

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.RegistryClient")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_logs_registry_initialization(
        self,
        mock_get_logger,
        mock_registry_client,
        mock_check_version,
        mock_setup_providers,
    ):
        """Test debug mode logs registry client initialization."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        # Mock registry client
        mock_registry_instance = MagicMock()
        mock_registry_config = MagicMock()
        mock_registry_config.model_id = "test-model"
        mock_registry_config.thresholds = {"accuracy": 0.9}
        mock_registry_instance.fetch_model_config.return_value = mock_registry_config
        mock_registry_client.return_value = mock_registry_instance

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            registry_url="http://localhost:8080",
            debug_mode=True,
        )

        with (
            patch("opentelemetry.metrics.get_meter"),
            patch("opentelemetry.trace.get_tracer"),
        ):
            client = MCAClient(config=config)

        # Verify registry initialization was logged
        debug_calls = [str(call) for call in mock_logger.debug.call_args_list]
        debug_text = " ".join(debug_calls)

        assert "registry" in debug_text.lower()

    @patch("mca_sdk.core.client.setup_all_providers")
    @patch("mca_sdk.core.client.check_version")
    @patch("mca_sdk.core.client.logging.getLogger")
    def test_debug_mode_logs_attribute_validation(
        self, mock_get_logger, mock_check_version, mock_setup_providers
    ):
        """Test debug mode logs attribute validation details."""
        mock_logger = MagicMock()
        mock_get_logger.return_value = mock_logger
        mock_setup_providers.return_value = (
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
            MagicMock(),
        )

        config = MCAConfig(
            service_name="test-service",
            model_id="test-model",
            team_name="test-team",
            debug_mode=True,
        )

        with (
            patch("opentelemetry.metrics.get_meter") as mock_get_meter,
            patch("opentelemetry.trace.get_tracer"),
        ):
            mock_meter = MagicMock()
            mock_counter = MagicMock()
            mock_histogram = MagicMock()
            mock_get_meter.return_value = mock_meter
            mock_meter.create_counter.return_value = mock_counter
            mock_meter.create_histogram.return_value = mock_histogram

            client = MCAClient(config=config)
            mock_logger.debug.reset_mock()

            # Record prediction with attributes
            client.record_prediction(latency=0.1, model_version="1.0.0", dataset="test")

        # Verify attribute validation was logged
        debug_calls = [str(call) for call in mock_logger.debug.call_args_list]
        debug_text = " ".join(debug_calls)

        assert "Attribute validation" in debug_text or "attributes" in debug_text.lower()
