"""
Tests for self-observability counters (P2-7).

Covers:
    - fast_classify increments classification_fast_total
    - fast_classify miss increments classification_fast_miss_total
    - Export success/failure increments export counters
    - Dedup suppression increments dedup_suppressed_total
"""

from __future__ import annotations

from unittest.mock import patch, MagicMock

import pytest

from risicare_core.observability import (
    classification_fast_total,
    classification_fast_miss_total,
    classification_llm_total,
    export_success_total,
    export_failure_total,
    dedup_suppressed_total,
)


class TestClassificationCounters:
    """fast_classify should increment observability counters."""

    def setup_method(self):
        """Reset counters before each test."""
        classification_fast_total.reset()
        classification_fast_miss_total.reset()
        classification_llm_total.reset()

    def test_fast_classify_increments_counter(self):
        """Every fast_classify call should increment classification_fast_total."""
        from risicare_diagnosis.stages.classifier import fast_classify

        before = sum(classification_fast_total.get_all().values())
        fast_classify("Error", "timeout occurred")
        after = sum(classification_fast_total.get_all().values())
        assert after == before + 1

    def test_fast_classify_miss_increments_counter(self):
        """fast_classify returning None should increment classification_fast_miss_total."""
        from risicare_diagnosis.stages.classifier import fast_classify

        before = sum(classification_fast_miss_total.get_all().values())
        result = fast_classify("Error", "some completely unique gibberish xyz123abc")
        after = sum(classification_fast_miss_total.get_all().values())
        assert result is None
        assert after == before + 1

    def test_fast_classify_hit_does_not_increment_miss(self):
        """fast_classify returning a result should NOT increment miss counter."""
        from risicare_diagnosis.stages.classifier import fast_classify

        before = sum(classification_fast_miss_total.get_all().values())
        result = fast_classify("Error", "timeout occurred")
        after = sum(classification_fast_miss_total.get_all().values())
        assert result is not None
        assert after == before


class TestExportCounters:
    """Export success/failure should increment counters."""

    def setup_method(self):
        """Reset counters before each test."""
        export_success_total.reset()
        export_failure_total.reset()

    def test_http_export_success_increments_counter(self):
        """HttpExporter success should increment export_success_total."""
        from risicare.exporters.http import HttpExporter
        from risicare.exporters.base import ExportResult
        from datetime import datetime, timezone

        exporter = HttpExporter(endpoint="http://test.invalid:1")
        span = MagicMock()
        span.to_dict.return_value = {"trace_id": "a" * 32, "span_id": "b" * 16}

        with patch.object(exporter, "_send_request", return_value=ExportResult.SUCCESS):
            result = exporter.export([span])

        assert result == ExportResult.SUCCESS
        total = export_success_total.get({"exporter": "http"})
        assert total >= 1

    def test_http_export_failure_increments_counter(self):
        """HttpExporter failure should increment export_failure_total."""
        from risicare.exporters.http import HttpExporter
        from risicare.exporters.base import ExportResult

        exporter = HttpExporter(endpoint="http://test.invalid:1", max_retries=1)
        span = MagicMock()
        span.to_dict.return_value = {"trace_id": "a" * 32, "span_id": "b" * 16}

        with patch.object(exporter, "_send_request", return_value=ExportResult.FAILURE):
            result = exporter.export([span])

        assert result == ExportResult.FAILURE
        total = export_failure_total.get({"exporter": "http"})
        assert total >= 1

    def test_otlp_export_success_increments_counter(self):
        """OTLPExporter success should increment export_success_total."""
        from risicare.exporters.otlp import OTLPExporter
        from risicare.exporters.base import ExportResult

        exporter = OTLPExporter(endpoint="http://test.invalid:1/v1/traces")

        with patch.object(exporter, "_build_otlp_payload", return_value={"resourceSpans": []}):
            with patch.object(exporter, "_send", return_value=True):
                result = exporter.export([MagicMock()])

        assert result == ExportResult.SUCCESS
        total = export_success_total.get({"exporter": "otlp"})
        assert total >= 1

    def test_otlp_export_failure_increments_counter(self):
        """OTLPExporter failure should increment export_failure_total."""
        from risicare.exporters.otlp import OTLPExporter
        from risicare.exporters.base import ExportResult

        exporter = OTLPExporter(
            endpoint="http://test.invalid:1/v1/traces",
            max_retries=1,
        )

        with patch.object(exporter, "_build_otlp_payload", return_value={"resourceSpans": []}):
            with patch.object(exporter, "_send", return_value=False):
                result = exporter.export([MagicMock()])

        assert result == ExportResult.FAILURE
        total = export_failure_total.get({"exporter": "otlp"})
        assert total >= 1


class TestDedupCounter:
    """Dedup suppression should increment counter."""

    def setup_method(self):
        """Reset counter before each test."""
        dedup_suppressed_total.reset()

    def test_suppression_increments_counter(self):
        """is_provider_suppressed() returning True should increment dedup counter."""
        from risicare.integrations._dedup import (
            is_provider_suppressed,
            suppress_provider_instrumentation,
        )

        before = sum(dedup_suppressed_total.get_all().values())
        with suppress_provider_instrumentation():
            assert is_provider_suppressed() is True
        after = sum(dedup_suppressed_total.get_all().values())
        assert after == before + 1

    def test_no_suppression_does_not_increment(self):
        """is_provider_suppressed() returning False should not increment counter."""
        from risicare.integrations._dedup import is_provider_suppressed

        before = sum(dedup_suppressed_total.get_all().values())
        assert is_provider_suppressed() is False
        after = sum(dedup_suppressed_total.get_all().values())
        assert after == before
