"""OpenTelemetry instrumentation — no-op when SDK not installed."""

from __future__ import annotations

import contextlib
from collections.abc import Generator
from typing import Any

_OTEL_AVAILABLE = False
try:
    from opentelemetry import metrics, trace  # type: ignore[import-untyped]

    _OTEL_AVAILABLE = True
except ImportError:
    pass


def get_tracer(name: str = "extractor") -> Any:
    if _OTEL_AVAILABLE:
        return trace.get_tracer(name)
    return _NoOpTracer()


def get_meter(name: str = "extractor") -> Any:
    if _OTEL_AVAILABLE:
        return metrics.get_meter(name)
    return _NoOpMeter()


class _NoOpTracer:
    @contextlib.contextmanager
    def start_as_current_span(self, name: str, **kwargs: Any) -> Generator[Any, None, None]:
        yield _NoOpSpan()


class _NoOpSpan:
    def set_attribute(self, key: str, value: Any) -> None:
        pass

    def set_status(self, *args: Any) -> None:
        pass


class _NoOpMeter:
    def create_counter(self, name: str, **kwargs: Any) -> _NoOpCounter:
        return _NoOpCounter()

    def create_histogram(self, name: str, **kwargs: Any) -> _NoOpHistogram:
        return _NoOpHistogram()


class _NoOpCounter:
    def add(self, amount: int | float, attributes: dict | None = None) -> None:
        pass


class _NoOpHistogram:
    def record(self, amount: int | float, attributes: dict | None = None) -> None:
        pass


@contextlib.contextmanager
def trace_stage(name: str, **attrs: Any) -> Generator[Any, None, None]:
    """Context manager that creates an OTEL span if available, else is a no-op."""
    tracer = get_tracer()
    with tracer.start_as_current_span(name) as span:
        for k, v in attrs.items():
            with contextlib.suppress(Exception):
                span.set_attribute(k, str(v))
        yield span


# Module-level meter and counters (created lazily)
_meter: Any = None
_docs_counter: Any = None
_records_counter: Any = None
_errors_counter: Any = None


def _get_counters() -> tuple[Any, Any, Any]:
    global _meter, _docs_counter, _records_counter, _errors_counter
    if _meter is None:
        _meter = get_meter()
        _docs_counter = _meter.create_counter(
            "extractor.documents_processed",
            description="Number of documents processed",
        )
        _records_counter = _meter.create_counter(
            "extractor.records_written",
            description="Number of records written to output",
        )
        _errors_counter = _meter.create_counter(
            "extractor.documents_failed",
            description="Number of documents that failed processing",
        )
    return _docs_counter, _records_counter, _errors_counter


def record_document_processed(n_records: int, format: str = "unknown") -> None:
    docs, records, _ = _get_counters()
    docs.add(1, {"format": format})
    records.add(n_records, {"format": format})


def record_document_failed(format: str = "unknown") -> None:
    _, _, errors = _get_counters()
    errors.add(1, {"format": format})
