"""Unit tests for the tracing module."""

import unittest
from unittest.mock import MagicMock, patch

from opentelemetry.sdk.resources import Resource
from opentelemetry.trace import Tracer

from amqp.service.tracing import (
    MetricsConfig,
    TracingConfig,
    create_resource,
    create_span,
    initialize_telemetry,
    setup_metrics_provider,
    setup_otlp_exporter,
    setup_trace_provider,
)


class TestTracingConfig(unittest.TestCase):
    """Test cases for TracingConfig."""

    def test_default_values(self):
        """Test that default values are set correctly."""
        config = TracingConfig(service_name="test-service", service_version="1.0.0")
        self.assertEqual(config.service_name, "test-service")
        self.assertEqual(config.service_version, "1.0.0")
        self.assertEqual(config.otlp_endpoint, "http://localhost:4317")
        self.assertTrue(config.insecure)
        self.assertFalse(config.debug)
        self.assertIsNone(config.additional_attributes)

    def test_custom_values(self):
        """Test that custom values are set correctly."""
        config = TracingConfig(
            service_name="test-service",
            service_version="1.0.0",
            otlp_endpoint="http://custom:4317",
            insecure=False,
            debug=True,
            additional_attributes={"custom": "attribute"},
        )
        self.assertEqual(config.otlp_endpoint, "http://custom:4317")
        self.assertFalse(config.insecure)
        self.assertTrue(config.debug)
        self.assertEqual(config.additional_attributes, {"custom": "attribute"})


class TestMetricsConfig(unittest.TestCase):
    """Test cases for MetricsConfig."""

    def test_default_values(self):
        """Test that default values are set correctly."""
        config = MetricsConfig()
        self.assertEqual(config.prometheus_port, 9464)
        self.assertEqual(config.prometheus_host, "localhost")
        self.assertEqual(config.export_interval_millis, 30000)
        self.assertIsNone(config.additional_attributes)

    def test_custom_values(self):
        """Test that custom values are set correctly."""
        config = MetricsConfig(
            prometheus_port=8000,
            prometheus_host="0.0.0.0",
            export_interval_millis=15000,
            additional_attributes={"custom": "attribute"},
        )
        self.assertEqual(config.prometheus_port, 8000)
        self.assertEqual(config.prometheus_host, "0.0.0.0")
        self.assertEqual(config.export_interval_millis, 15000)
        self.assertEqual(config.additional_attributes, {"custom": "attribute"})


class TestCreateResource(unittest.TestCase):
    """Test cases for create_resource function."""

    def test_create_resource_basic(self):
        """Test creating a resource with basic configuration."""
        config = TracingConfig(service_name="test-service", service_version="1.0.0")
        resource = create_resource(config)
        self.assertIsInstance(resource, Resource)
        self.assertEqual(resource.attributes.get("service.name"), "test-service")
        self.assertEqual(resource.attributes.get("service.version"), "1.0.0")

    def test_create_resource_with_attributes(self):
        """Test creating a resource with additional attributes."""
        config = TracingConfig(
            service_name="test-service",
            service_version="1.0.0",
            additional_attributes={"custom": "attribute"},
        )
        resource = create_resource(config)
        self.assertEqual(resource.attributes.get("custom"), "attribute")


class TestSetupExporters(unittest.TestCase):
    """Test cases for exporter setup functions."""

    @patch("amqp.service.tracing.OTLPSpanExporter")
    def test_setup_otlp_exporter(self, mock_otlp):
        """Test OTLP exporter setup."""
        config = TracingConfig(
            service_name="test-service",
            service_version="1.0.0",
            otlp_endpoint="http://jaeger:4317",
            insecure=True,
        )
        setup_otlp_exporter(config)
        mock_otlp.assert_called_once_with(endpoint="http://jaeger:4317", insecure=True)

    @patch("amqp.service.tracing.OTLPSpanExporter")
    def test_setup_otlp_exporter_secure(self, mock_otlp):
        """Test OTLP exporter setup with secure connection."""
        config = TracingConfig(
            service_name="test-service",
            service_version="1.0.0",
            otlp_endpoint="https://jaeger:4317",
            insecure=False,
        )
        setup_otlp_exporter(config)
        mock_otlp.assert_called_once_with(endpoint="https://jaeger:4317", insecure=False)


class TestSetupTraceProvider(unittest.TestCase):
    """Test cases for trace provider setup."""

    @patch("amqp.service.tracing.TracerProvider")
    @patch("amqp.service.tracing.BatchSpanProcessor")
    @patch("amqp.service.tracing.setup_otlp_exporter")
    def test_setup_trace_provider_basic(self, mock_otlp_exporter, mock_processor, mock_provider):
        """Test basic trace provider setup."""
        config = TracingConfig(service_name="test-service", service_version="1.0.0")
        resource = create_resource(config)

        mock_provider_instance = MagicMock()
        mock_provider.return_value = mock_provider_instance

        setup_trace_provider(config, resource)

        mock_provider.assert_called_once_with(resource=resource)
        mock_otlp_exporter.assert_called_once()
        self.assertEqual(mock_provider_instance.add_span_processor.call_count, 1)

    @patch("amqp.service.tracing.TracerProvider")
    @patch("amqp.service.tracing.BatchSpanProcessor")
    @patch("amqp.service.tracing.setup_otlp_exporter")
    @patch("amqp.service.tracing.ConsoleSpanExporter")
    def test_setup_trace_provider_with_debug(
        self, mock_console_exporter, mock_otlp_exporter, mock_processor, mock_provider
    ):
        """Test trace provider setup with debug mode."""
        config = TracingConfig(service_name="test-service", service_version="1.0.0", debug=True)
        resource = create_resource(config)

        mock_provider_instance = MagicMock()
        mock_provider.return_value = mock_provider_instance

        setup_trace_provider(config, resource)

        self.assertEqual(mock_provider_instance.add_span_processor.call_count, 2)
        mock_console_exporter.assert_called_once()


class TestSetupMetricsProvider(unittest.TestCase):
    """Test cases for metrics provider setup."""

    @patch("amqp.service.tracing.start_http_server")
    @patch("amqp.service.tracing.PrometheusMetricReader")
    @patch("amqp.service.tracing.PeriodicExportingMetricReader")
    @patch("amqp.service.tracing.MeterProvider")
    def test_setup_metrics_provider_success(
        self, mock_meter_provider, mock_periodic_reader, mock_prometheus_reader, mock_start_server
    ):
        """Test successful metrics provider setup."""
        config = MetricsConfig(prometheus_port=9464, prometheus_host="localhost")
        resource = Resource({})

        setup_metrics_provider(config, resource)

        mock_start_server.assert_called_once_with(port=9464, addr="localhost")
        mock_prometheus_reader.assert_called_once()
        mock_periodic_reader.assert_called_once()
        mock_meter_provider.assert_called_once()

    @patch("amqp.service.tracing.start_http_server")
    def test_setup_metrics_provider_failure(self, mock_start_server):
        """Test metrics provider setup with failure."""
        mock_start_server.side_effect = Exception("Failed to start server")

        config = MetricsConfig()
        resource = Resource({})

        provider = setup_metrics_provider(config, resource)
        self.assertIsNotNone(provider)  # Should return no-op provider


class TestInitializeTelemetry(unittest.TestCase):
    """Test cases for initialize_telemetry function."""

    @patch("amqp.service.tracing.setup_trace_provider")
    @patch("amqp.service.tracing.setup_metrics_provider")
    @patch("amqp.service.tracing.trace")
    @patch("amqp.service.tracing.metrics")
    def test_initialize_telemetry_success(
        self, mock_metrics, mock_trace, mock_setup_metrics, mock_setup_trace
    ):
        """Test successful telemetry initialization."""
        config = TracingConfig(service_name="test-service", service_version="1.0.0")
        metrics_config = MetricsConfig()

        mock_tracer = MagicMock(spec=Tracer)
        mock_trace.get_tracer.return_value = mock_tracer

        tracer = initialize_telemetry(config, metrics_config)

        self.assertEqual(tracer, mock_tracer)
        mock_setup_trace.assert_called_once()
        mock_setup_metrics.assert_called_once()

    @patch("amqp.service.tracing.setup_trace_provider")
    def test_initialize_telemetry_failure(self, mock_setup_trace):
        """Test telemetry initialization with failure."""
        mock_setup_trace.side_effect = Exception("Setup failed")

        tracer = initialize_telemetry()

        self.assertIsNotNone(tracer)  # Should return no-op tracer


class TestCreateSpan(unittest.TestCase):
    """Test cases for create_span function."""

    def test_create_span_basic(self):
        """Test basic span creation."""
        mock_tracer = MagicMock(spec=Tracer)
        create_span(mock_tracer, "test-span")
        mock_tracer.start_as_current_span.assert_called_once_with("test-span", attributes={})

    def test_create_span_with_attributes(self):
        """Test span creation with attributes."""
        mock_tracer = MagicMock(spec=Tracer)
        attributes = {"custom": "attribute"}
        create_span(mock_tracer, "test-span", attributes)
        mock_tracer.start_as_current_span.assert_called_once_with(
            "test-span", attributes=attributes
        )


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