"""
Native OpenTelemetry TracerProvider for AgentBill
P3-A: Replaces manual OTLP JSON construction with native OTEL SDK

[LEGACY_COMPAT:P3-A] This module coexists with tracer.py (manual OTLP).
When otel_mode="native", this module is used.
When otel_mode="legacy", tracer.py (manual) is used.
Target removal of legacy path: after P3-A validated in production + 1 minor release.
"""

import logging
from typing import Optional, Dict, Any

logger = logging.getLogger("agentbill")


def _check_otel_available() -> bool:
    """Check if OpenTelemetry SDK packages are installed."""
    try:
        import opentelemetry.sdk.trace  # noqa: F401
        import opentelemetry.exporter.otlp.proto.http.trace_exporter  # noqa: F401
        return True
    except ImportError:
        return False


OTEL_AVAILABLE = _check_otel_available()


class AgentBillSpanProcessor:
    """
    Custom span processor that wraps BatchSpanProcessor with AgentBill
    identity injection (customer_id, agent_id).

    This is the P3-D hook point — for now it's a thin wrapper.
    P3-D will add PII redaction and enrichment here.
    """

    def __init__(self, exporter, customer_id: Optional[str] = None,
                 agent_id: Optional[str] = None, **kwargs):
        if not OTEL_AVAILABLE:
            raise ImportError(
                "opentelemetry-sdk is required for native OTEL mode. "
                "Install with: pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http"
            )
        from opentelemetry.sdk.trace.export import BatchSpanProcessor
        self._processor = BatchSpanProcessor(exporter, **kwargs)
        self._customer_id = customer_id
        self._agent_id = agent_id

    def on_start(self, span, parent_context=None):
        return self._processor.on_start(span, parent_context)

    def on_end(self, span):
        return self._processor.on_end(span)

    def shutdown(self):
        return self._processor.shutdown()

    def force_flush(self, timeout_millis=5000):
        return self._processor.force_flush(timeout_millis)


class AgentBillTracerProvider:
    """
    Wraps native OTEL TracerProvider with AgentBill-specific configuration.

    This provider:
    1. Creates a Resource with service.name, customer.id, agent.id
    2. Configures OTLPSpanExporter pointing to /otel-collector
    3. Uses BatchSpanProcessor for efficient batching
    4. Exposes a tracer via get_tracer()
    """

    def __init__(self, config: Dict[str, Any]):
        if not OTEL_AVAILABLE:
            raise ImportError(
                "opentelemetry-sdk and opentelemetry-exporter-otlp-proto-http are required "
                "for native OTEL mode. Install with:\n"
                "  pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http"
            )

        from opentelemetry import trace
        from opentelemetry.sdk.trace import TracerProvider
        from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
        from opentelemetry.sdk.resources import Resource

        self._config = config
        api_key = config["api_key"]
        base_url = config.get("base_url", "https://api.agentbill.io")
        customer_id = config.get("customer_id")
        agent_id = config.get("agent_id")

        # Build resource attributes
        resource_attrs: Dict[str, str] = {
            "service.name": "agentbill-python-sdk",
            "service.version": self._get_version(),
        }
        if customer_id:
            resource_attrs["customer.id"] = customer_id
        if agent_id:
            resource_attrs["agent.id"] = agent_id

        # Add external IDs if present
        external_customer_id = config.get("external_customer_id") or config.get("customer_external_id")
        if external_customer_id:
            resource_attrs["customer.external_id"] = external_customer_id

        resource = Resource.create(resource_attrs)

        # Configure native OTLP exporter.
        # [PHASE3-R3-SDK-REQUESTID-NATIVE] Wrap so per-batch X-Request-Id +
        # Contract-Version: 1 are emitted (TELEMETRY_CONTRACT v1.0.0 §3.2).
        from .instrumented_exporter import wrap_otlp_exporter
        self._exporter = wrap_otlp_exporter(OTLPSpanExporter(
            endpoint=f"{base_url}/functions/v1/otel-collector",
            headers={"X-API-Key": api_key},
        ))

        # Configure batch processor with sensible defaults
        max_queue_size = config.get("max_queue_size", 2048)
        max_export_batch_size = config.get("max_export_batch_size", 512)
        export_interval_ms = config.get("export_interval_ms", 1000)

        self._processor = AgentBillSpanProcessor(
            self._exporter,
            customer_id=customer_id,
            agent_id=agent_id,
            max_queue_size=max_queue_size,
            max_export_batch_size=max_export_batch_size,
            schedule_delay_millis=export_interval_ms,
        )

        # Create provider
        self._provider = TracerProvider(resource=resource)
        self._provider.add_span_processor(self._processor)

        # Register as global provider only if explicitly requested
        if config.get("set_global_provider", False):
            trace.set_tracer_provider(self._provider)

        logger.debug("AgentBillTracerProvider initialized (native OTEL mode)")

    @staticmethod
    def _get_version() -> str:
        """Get the SDK version."""
        try:
            from . import __version__
            return __version__
        except ImportError:
            return "11.0.0"

    def get_tracer(self, name: str = "agentbill"):
        """Get a native OTEL Tracer instance."""
        return self._provider.get_tracer(name, self._get_version())

    def shutdown(self):
        """Shutdown the provider and flush remaining spans."""
        self._provider.shutdown()

    def force_flush(self, timeout_millis: int = 5000) -> bool:
        """Force flush all pending spans."""
        return self._provider.force_flush(timeout_millis)


class NativeSpanWrapper:
    """
    P3-A: Wraps native OTEL Span to match the legacy Span interface.

    This ensures backward compatibility — callers using span.trace_id,
    span.set_attributes(), span.end() continue to work unchanged.

    [LEGACY_COMPAT:P3-A] This wrapper exists to bridge native OTEL Span and
    the legacy Span class interface. When legacy is fully removed, callers
    should migrate to using native OTEL Span directly or this wrapper
    becomes the canonical Span type.
    """

    def __init__(self, otel_span, trace_id: str, span_id: str, parent_span_id: Optional[str] = None):
        self._otel_span = otel_span
        self.trace_id = trace_id
        self.span_id = span_id
        self.parent_span_id = parent_span_id
        self.name = otel_span.name if hasattr(otel_span, 'name') else ""
        self.start_time = otel_span.start_time if hasattr(otel_span, 'start_time') else None
        self.end_time = None
        self.status = {"code": 0}

    def set_attributes(self, attributes: Dict[str, Any]):
        """Set multiple attributes on the span."""
        for k, v in attributes.items():
            self._otel_span.set_attribute(k, v)

    def set_status(self, code: int, message: str = ""):
        """Set span status (0=OK, 1=ERROR)."""
        from opentelemetry.trace import StatusCode
        if code == 0:
            self._otel_span.set_status(StatusCode.OK, message)
        else:
            self._otel_span.set_status(StatusCode.ERROR, message)
        self.status = {"code": code, "message": message}

    def end(self):
        """End the span."""
        self._otel_span.end()
        self.end_time = self._otel_span.end_time if hasattr(self._otel_span, 'end_time') else None

    @property
    def attributes(self) -> Dict[str, Any]:
        """Read-only access to span attributes for backward compatibility."""
        if hasattr(self._otel_span, 'attributes') and self._otel_span.attributes:
            return dict(self._otel_span.attributes)
        return {}


def create_tracer_provider(config: Dict[str, Any]) -> AgentBillTracerProvider:
    """Factory function to create the native OTEL tracer provider."""
    return AgentBillTracerProvider(config)
