"""Safe OTLP exporter that suppresses errors gracefully."""

import logging
from collections.abc import Sequence

from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult

logger = logging.getLogger(__name__)


class SafeOTLPSpanExporter(SpanExporter):
    """Wrapper around an OTLP exporter that catches and logs errors.

    Useful when tracing should be best-effort and not affect the main
    application if the trace endpoint is unreachable.
    """

    def __init__(self, wrapped_exporter: SpanExporter, suppress_errors: bool = True):
        self.wrapped_exporter = wrapped_exporter
        self.suppress_errors = suppress_errors
        self._error_count = 0
        self._max_logged_errors = 3

    def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
        try:
            return self.wrapped_exporter.export(spans)
        except Exception as e:
            if not self.suppress_errors:
                raise

            self._error_count += 1

            if self._error_count <= self._max_logged_errors:
                logger.warning(
                    f"Failed to export {len(spans)} spans to Coalex "
                    f"(error #{self._error_count}): {type(e).__name__}: {e}"
                )
                if self._error_count == self._max_logged_errors:
                    logger.warning(
                        f"Suppressing further span export errors (total so far: {self._error_count}). "
                        "Tracing will continue but spans may be lost if the endpoint is unreachable."
                    )

            return SpanExportResult.SUCCESS

    def shutdown(self) -> None:
        try:
            self.wrapped_exporter.shutdown()
        except Exception as e:
            if not self.suppress_errors:
                raise
            logger.debug(f"Error during exporter shutdown: {e}")

    def force_flush(self, timeout_millis: int = 30000) -> bool:
        try:
            return self.wrapped_exporter.force_flush(timeout_millis)
        except Exception as e:
            if not self.suppress_errors:
                raise
            logger.debug(f"Error during force flush: {e}")
            return False
