"""Tests unitaires pour le module OTel."""

from unittest.mock import MagicMock, patch

from opentelemetry.trace import Tracer
from opentelemetry.metrics import Meter


class TestSetupOtel:
    """Tests pour setup_otel."""

    @patch("cezam_lib.cezam_shared.otel.OTLPMetricExporter")
    @patch("cezam_lib.cezam_shared.otel.OTLPSpanExporter")
    @patch("cezam_lib.cezam_shared.otel.metrics")
    @patch("cezam_lib.cezam_shared.otel.trace")
    def test_setup_otel_returns_tracer_and_meter(
        self,
        mock_trace: MagicMock,
        mock_metrics: MagicMock,
        mock_span_exporter: MagicMock,
        mock_metric_exporter: MagicMock,
    ) -> None:
        """Vérifie que setup_otel retourne un tuple (Tracer, Meter)."""
        from cezam_lib.cezam_shared import setup_otel

        mock_tracer = MagicMock(spec=Tracer)
        mock_meter = MagicMock(spec=Meter)
        mock_trace.get_tracer.return_value = mock_tracer
        mock_metrics.get_meter.return_value = mock_meter

        tracer, meter = setup_otel("test-service")

        assert tracer is mock_tracer
        assert meter is mock_meter

    @patch("cezam_lib.cezam_shared.otel.OTLPMetricExporter")
    @patch("cezam_lib.cezam_shared.otel.OTLPSpanExporter")
    @patch("cezam_lib.cezam_shared.otel.metrics")
    @patch("cezam_lib.cezam_shared.otel.trace")
    def test_setup_otel_uses_service_name(
        self,
        mock_trace: MagicMock,
        mock_metrics: MagicMock,
        mock_span_exporter: MagicMock,
        mock_metric_exporter: MagicMock,
    ) -> None:
        """Vérifie que le service_name est utilisé pour créer tracer et meter."""
        from cezam_lib.cezam_shared import setup_otel

        setup_otel("my-service")

        mock_trace.get_tracer.assert_called_once_with("my-service")
        mock_metrics.get_meter.assert_called_once_with("my-service")

    @patch("cezam_lib.cezam_shared.otel.OTLPMetricExporter")
    @patch("cezam_lib.cezam_shared.otel.OTLPSpanExporter")
    @patch("cezam_lib.cezam_shared.otel.metrics")
    @patch("cezam_lib.cezam_shared.otel.trace")
    def test_setup_otel_uses_custom_endpoint(
        self,
        mock_trace: MagicMock,
        mock_metrics: MagicMock,
        mock_span_exporter: MagicMock,
        mock_metric_exporter: MagicMock,
    ) -> None:
        """Vérifie que l'endpoint personnalisé est utilisé."""
        from cezam_lib.cezam_shared import setup_otel

        setup_otel("test-service", otel_endpoint="otel.example.com:4317")

        mock_span_exporter.assert_called_once_with(
            endpoint="otel.example.com:4317", insecure=True
        )
        mock_metric_exporter.assert_called_once_with(
            endpoint="otel.example.com:4317", insecure=True
        )

    @patch("cezam_lib.cezam_shared.otel.OTLPMetricExporter")
    @patch("cezam_lib.cezam_shared.otel.OTLPSpanExporter")
    @patch("cezam_lib.cezam_shared.otel.metrics")
    @patch("cezam_lib.cezam_shared.otel.trace")
    def test_setup_otel_uses_default_endpoint(
        self,
        mock_trace: MagicMock,
        mock_metrics: MagicMock,
        mock_span_exporter: MagicMock,
        mock_metric_exporter: MagicMock,
    ) -> None:
        """Vérifie que l'endpoint par défaut est localhost:4317."""
        from cezam_lib.cezam_shared import setup_otel

        setup_otel("test-service")

        mock_span_exporter.assert_called_once_with(
            endpoint="localhost:4317", insecure=True
        )


class TestInjectTraceContext:
    """Tests pour inject_trace_context."""

    @patch("cezam_lib.cezam_shared.otel.inject")
    def test_inject_trace_context_modifies_headers(
        self, mock_inject: MagicMock
    ) -> None:
        """Vérifie que inject_trace_context appelle inject avec les headers."""
        from cezam_lib.cezam_shared import inject_trace_context

        headers = {"existing": "header"}
        result = inject_trace_context(headers)

        mock_inject.assert_called_once_with(headers)
        assert result is headers

    @patch("cezam_lib.cezam_shared.otel.inject")
    def test_inject_trace_context_returns_same_dict(
        self, mock_inject: MagicMock
    ) -> None:
        """Vérifie que inject_trace_context retourne le même dictionnaire."""
        from cezam_lib.cezam_shared import inject_trace_context

        headers = {"key": "value"}
        result = inject_trace_context(headers)

        assert result is headers

    @patch("cezam_lib.cezam_shared.otel.inject")
    def test_inject_trace_context_with_empty_headers(
        self, mock_inject: MagicMock
    ) -> None:
        """Vérifie que inject_trace_context fonctionne avec un dict vide."""
        from cezam_lib.cezam_shared import inject_trace_context

        headers: dict = {}
        result = inject_trace_context(headers)

        mock_inject.assert_called_once_with(headers)
        assert result == {}


class TestExtractTraceContext:
    """Tests pour extract_trace_context."""

    @patch("cezam_lib.cezam_shared.otel.otel_context")
    @patch("cezam_lib.cezam_shared.otel.extract")
    def test_extract_trace_context_extracts_and_attaches(
        self, mock_extract: MagicMock, mock_otel_context: MagicMock
    ) -> None:
        """Vérifie que extract_trace_context extrait et attache le contexte."""
        from cezam_lib.cezam_shared import extract_trace_context

        mock_ctx = MagicMock()
        mock_extract.return_value = mock_ctx

        headers = {"traceparent": "00-abc123-def456-01"}
        extract_trace_context(headers)

        mock_extract.assert_called_once_with(headers)
        mock_otel_context.attach.assert_called_once_with(mock_ctx)

    @patch("cezam_lib.cezam_shared.otel.otel_context")
    @patch("cezam_lib.cezam_shared.otel.extract")
    def test_extract_trace_context_with_empty_headers(
        self, mock_extract: MagicMock, mock_otel_context: MagicMock
    ) -> None:
        """Vérifie que extract_trace_context fonctionne avec un dict vide."""
        from cezam_lib.cezam_shared import extract_trace_context

        mock_ctx = MagicMock()
        mock_extract.return_value = mock_ctx

        headers: dict = {}
        extract_trace_context(headers)

        mock_extract.assert_called_once_with(headers)
        mock_otel_context.attach.assert_called_once_with(mock_ctx)
