"""Provider setup functions for OpenTelemetry.

This module provides functions to initialize MeterProvider, LoggerProvider,
and TracerProvider with proper configuration and exporters.

Prometheus Metrics Exported
----------------------------

When prometheus_enabled=True, the following metric types are exposed on the
configured HTTP endpoint (default: http://127.0.0.1:9464/metrics):

**Model Prediction Metrics:**
- model_predictions_total (counter) - Total number of predictions made
- model_latency_seconds (histogram) - Prediction latency distribution
- model_errors_total (counter) - Total prediction errors
- model_accuracy_ratio (gauge) - Model accuracy metrics

**LLM/GenAI Metrics (when using LLM integrations):**
- llm_tokens_total (counter) - Token usage (input/output)
- llm_latency_seconds (histogram) - LLM API call latency
- llm_cost_total (counter) - Cumulative LLM API costs
- llm_errors_total (counter) - LLM API errors

**Resource Attributes (as labels):**
All metrics include resource attributes as Prometheus labels:
- service_name - Service/model name
- model_id - Model identifier
- model_version - Model version
- model_category - "internal" or "vendor"
- model_type - "regression", "classification", "generative", etc.
- team_name - Team responsible for the model

**Configuration:**
Environment Variables:
- MCA_PROMETHEUS_ENABLED (default: false) - Enable Prometheus exporter
- MCA_PROMETHEUS_PORT (default: 9464) - HTTP server port
- MCA_PROMETHEUS_HOST (default: 127.0.0.1) - Bind address

Security Note: Default binding to 127.0.0.1 (localhost) prevents network exposure.
For Kubernetes deployments, explicitly set MCA_PROMETHEUS_HOST=0.0.0.0 and secure
the endpoint with NetworkPolicies or a service mesh.

**Port Conflict Behavior:**
If the configured port is already in use, the exporter will log a warning and
gracefully degrade to OTLP-only mode. No exception is raised.
"""

import logging
import warnings
import threading
from typing import Dict, Optional, Any, Callable, Tuple, TYPE_CHECKING

if TYPE_CHECKING:
    from ..config.settings import LoggingConfig
    from ..exporters.gcp_trace_exporter import GCPCloudTraceExporter
from wsgiref.simple_server import make_server, WSGIServer

from opentelemetry import metrics, trace
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry._logs import set_logger_provider, get_logger_provider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

# Import buffered exporters for retry logic
from ..buffering.exporters import (
    BufferedOTLPMetricExporter,
    BufferedOTLPLogExporter,
    BufferedOTLPSpanExporter,
)
from ..buffering.retry import RetryPolicy


class ProviderAlreadySetWarning(UserWarning):
    """Warning when attempting to set a provider that's already configured."""


def create_resource_attributes(
    service_name: str,
    model_id: Optional[str] = None,
    model_version: str = "0.3.0",
    team_name: Optional[str] = None,
    model_category: str = "internal",
    model_type: str = "regression",
    extra_resource: Optional[Dict[str, str]] = None,
    **kwargs,
) -> Resource:
    """Create OpenTelemetry Resource with model metadata.

    Uses the two-field taxonomy (model_category + model_type) to provide
    complete model classification as resource attributes.

    Args:
        service_name: Name of the service
        model_id: Unique model identifier
        model_version: Model version string
        team_name: Team responsible for the model
        model_category: High-level category ("internal" or "vendor")
        model_type: Specific type ("regression", "time-series", "classification", "generative", "agentic")
        extra_resource: Additional resource attributes from registry
        **kwargs: Additional resource attributes

    Returns:
        Resource instance with model metadata
    """
    # Base attributes (always included)
    attributes = {
        "service.name": service_name,
        "model.version": model_version,
        # Two-field taxonomy (Story 2.4 - AC#8)
        "model.category": model_category,
        "model.type": model_type,
    }

    # Optional attributes
    if model_id:
        attributes["model.id"] = model_id
    if team_name:
        attributes["team.name"] = team_name

    # Merge extra_resource attributes from registry
    if extra_resource:
        attributes.update(extra_resource)

    # Add any additional attributes
    attributes.update(kwargs)

    return Resource.create(attributes)


def setup_meter_provider(
    resource: Resource,
    endpoint: str = "http://localhost:4318/v1/metrics",
    export_interval_ms: int = 5000,
    use_buffering: bool = False,
    retry_policy: Optional[RetryPolicy] = None,
    persist_queue: bool = False,
    persist_path: Optional[str] = None,
    persist_min_disk_space_mb: float = 10.0,
    prometheus_enabled: bool = False,
    prometheus_port: int = 9464,
    prometheus_host: str = "127.0.0.1",
) -> Tuple[
    MeterProvider,
    Optional["BufferedOTLPMetricExporter"],
    Optional[Any],
    Optional[Callable[[], None]],
]:
    """Set up and configure MeterProvider for metrics collection.

    Args:
        resource: OpenTelemetry Resource with metadata
        endpoint: OTLP HTTP endpoint for metrics
        export_interval_ms: Interval for periodic metric export
        use_buffering: Whether to use buffered exporter with retry logic
        retry_policy: RetryPolicy for retries (used if use_buffering=True)
        persist_queue: Whether to persist queue to disk (default: False)
        persist_path: Path for queue persistence (required if persist_queue=True)
        persist_min_disk_space_mb: Minimum free disk space (MB) for persistence (default: 10.0)
        prometheus_enabled: Whether to enable Prometheus exporter
        prometheus_port: Port for Prometheus HTTP server
        prometheus_host: Host to bind Prometheus HTTP server

    Returns:
        Tuple of (MeterProvider, BufferedExporter or None, PrometheusReader or None, ShutdownCallback or None)

    Note:
        If a MeterProvider is already set globally, a warning is issued and
        the new provider overwrites it. Creating multiple MCAClient instances
        will overwrite the global provider configuration.
    """
    # Check if meter provider already set
    existing_provider = metrics.get_meter_provider()
    if existing_provider is not None and hasattr(existing_provider, "_sdk_config"):
        warnings.warn(
            "MeterProvider is already set globally. Creating multiple MCAClient "
            "instances will overwrite the global provider. Consider reusing a "
            "single MCAClient instance across your application.",
            ProviderAlreadySetWarning,
            stacklevel=3,
        )

    # Create OTLP metric exporter (buffered or standard)
    buffered_exporter = None
    if use_buffering and retry_policy:
        exporter = BufferedOTLPMetricExporter(
            endpoint=endpoint,
            retry_policy=retry_policy,
            persist_queue=persist_queue,
            persist_path=persist_path,
            persist_min_disk_space_mb=persist_min_disk_space_mb,
        )
        exporter.start_background_worker()
        buffered_exporter = exporter
    else:
        exporter = OTLPMetricExporter(endpoint=endpoint)

    # Create OTLP reader (existing code)
    otlp_reader = PeriodicExportingMetricReader(exporter, export_interval_millis=export_interval_ms)

    # Create Prometheus reader if enabled (NEW)
    prometheus_reader, prometheus_shutdown = setup_prometheus_exporter(
        prometheus_enabled=prometheus_enabled,
        prometheus_port=prometheus_port,
        prometheus_host=prometheus_host,
    )

    # Build readers list - always include OTLP, optionally add Prometheus
    readers = [otlp_reader]
    if prometheus_reader:
        readers.append(prometheus_reader)

    # Initialize meter provider with all readers
    provider = MeterProvider(resource=resource, metric_readers=readers)

    # Set global meter provider
    metrics.set_meter_provider(provider)

    return provider, buffered_exporter, prometheus_reader, prometheus_shutdown


def setup_logger_provider(
    resource: Resource,
    endpoint: str = "http://localhost:4318/v1/logs",
    batch_size: int = 512,
    use_buffering: bool = False,
    retry_policy: Optional[RetryPolicy] = None,
    persist_queue: bool = False,
    persist_path: Optional[str] = None,
    persist_min_disk_space_mb: float = 10.0,
    gcp_config: Optional["LoggingConfig"] = None,
) -> tuple[LoggerProvider, Optional["BufferedOTLPLogExporter"]]:
    """Set up and configure LoggerProvider for structured logging.

    Args:
        resource: OpenTelemetry Resource with metadata
        endpoint: OTLP HTTP endpoint for logs
        batch_size: Maximum batch size for log export
        use_buffering: Whether to use buffered exporter with retry logic
        retry_policy: RetryPolicy for retries (used if use_buffering=True)
        persist_queue: Whether to persist queue to disk (default: False)
        persist_path: Path for queue persistence (required if persist_queue=True)
        persist_min_disk_space_mb: Minimum free disk space (MB) for persistence (default: 10.0)
        gcp_config: Optional LoggingConfig for GCP Cloud Logging integration

    Returns:
        Tuple of (LoggerProvider, BufferedExporter or None)

    Note:
        If a LoggerProvider is already set globally, a warning is issued and
        the new provider overwrites it. Creating multiple MCAClient instances
        will overwrite the global provider configuration.
    """
    # Check if logger provider already set
    try:
        existing_provider = get_logger_provider()
        if existing_provider is not None and hasattr(existing_provider, "_resource"):
            warnings.warn(
                "LoggerProvider is already set globally. Creating multiple MCAClient "
                "instances will overwrite the global provider. Consider reusing a "
                "single MCAClient instance across your application.",
                ProviderAlreadySetWarning,
                stacklevel=3,
            )
    except Exception:
        # get_logger_provider() may not be available in all OpenTelemetry versions
        pass

    # Create OTLP log exporter (buffered or standard)
    buffered_exporter = None
    if use_buffering and retry_policy:
        log_exporter = BufferedOTLPLogExporter(
            endpoint=endpoint,
            retry_policy=retry_policy,
            persist_queue=persist_queue,
            persist_path=persist_path,
            persist_min_disk_space_mb=persist_min_disk_space_mb,
        )
        log_exporter.start_background_worker()
        buffered_exporter = log_exporter
    else:
        log_exporter = OTLPLogExporter(endpoint=endpoint)

    # Create batch log processor with configured batch size
    log_processor = BatchLogRecordProcessor(log_exporter, max_export_batch_size=batch_size)

    # Initialize logger provider
    logger_provider = LoggerProvider(resource=resource)
    logger_provider.add_log_record_processor(log_processor)

    # Add GCP Cloud Logging exporter if enabled (Story 6.2)
    if gcp_config and gcp_config.gcp_logging_enabled:
        if use_buffering and retry_policy:
            from ..exporters.gcp_logging import BufferedCloudLoggingExporter

            gcp_exporter = BufferedCloudLoggingExporter(
                project_id=gcp_config.gcp_project_id,
                log_name=gcp_config.gcp_log_name,
                resource_type=gcp_config.gcp_resource_type,
                credentials=gcp_config.gcp_credentials,
                timeout=gcp_config.gcp_log_timeout,
                retry_policy=retry_policy,
            )
            gcp_exporter.start_background_worker()
        else:
            from ..exporters.gcp_logging import CloudLoggingExporter

            gcp_exporter = CloudLoggingExporter(
                project_id=gcp_config.gcp_project_id,
                log_name=gcp_config.gcp_log_name,
                resource_type=gcp_config.gcp_resource_type,
                credentials=gcp_config.gcp_credentials,
                timeout=gcp_config.gcp_log_timeout,
            )

        gcp_processor = BatchLogRecordProcessor(gcp_exporter, max_export_batch_size=batch_size)
        logger_provider.add_log_record_processor(gcp_processor)

    # Set global logger provider
    set_logger_provider(logger_provider)

    return logger_provider, buffered_exporter


def setup_tracer_provider(
    resource: Resource,
    endpoint: str = "http://localhost:4318/v1/traces",
    batch_size: int = 512,
    use_buffering: bool = False,
    retry_policy: Optional[RetryPolicy] = None,
    persist_queue: bool = False,
    persist_path: Optional[str] = None,
    persist_min_disk_space_mb: float = 10.0,
) -> tuple[TracerProvider, Optional["BufferedOTLPSpanExporter"]]:
    """Set up and configure TracerProvider for distributed tracing.

    Args:
        resource: OpenTelemetry Resource with metadata
        endpoint: OTLP HTTP endpoint for traces
        batch_size: Maximum batch size for trace export
        use_buffering: Whether to use buffered exporter with retry logic
        retry_policy: RetryPolicy for retries (used if use_buffering=True)
        persist_queue: Whether to persist queue to disk (default: False)
        persist_path: Path for queue persistence (required if persist_queue=True)
        persist_min_disk_space_mb: Minimum free disk space (MB) for persistence (default: 10.0)

    Returns:
        Tuple of (TracerProvider, BufferedExporter or None)

    Note:
        If a TracerProvider is already set globally, a warning is issued and
        the new provider overwrites it. Creating multiple MCAClient instances
        will overwrite the global provider configuration.
    """
    # Check if tracer provider already set
    existing_provider = trace.get_tracer_provider()
    if existing_provider is not None and hasattr(existing_provider, "_resource"):
        warnings.warn(
            "TracerProvider is already set globally. Creating multiple MCAClient "
            "instances will overwrite the global provider. Consider reusing a "
            "single MCAClient instance across your application.",
            ProviderAlreadySetWarning,
            stacklevel=3,
        )

    # Create OTLP trace exporter (buffered or standard)
    buffered_exporter = None
    if use_buffering and retry_policy:
        trace_exporter = BufferedOTLPSpanExporter(
            endpoint=endpoint,
            retry_policy=retry_policy,
            persist_queue=persist_queue,
            persist_path=persist_path,
            persist_min_disk_space_mb=persist_min_disk_space_mb,
        )
        trace_exporter.start_background_worker()
        buffered_exporter = trace_exporter
    else:
        trace_exporter = OTLPSpanExporter(endpoint=endpoint)

    # Create batch span processor with configured batch size
    span_processor = BatchSpanProcessor(trace_exporter, max_export_batch_size=batch_size)

    # Initialize tracer provider
    tracer_provider = TracerProvider(resource=resource)
    tracer_provider.add_span_processor(span_processor)

    # Set global tracer provider
    trace.set_tracer_provider(tracer_provider)

    return tracer_provider, buffered_exporter


def setup_gcp_trace_exporter(
    config,
    resource: Resource,
    batch_size: int = 512,
) -> Optional["GCPCloudTraceExporter"]:
    """Set up GCP Cloud Trace exporter if enabled.

    Args:
        config: MCAConfig instance
        resource: OpenTelemetry Resource
        batch_size: Batch size for span processing

    Returns:
        GCPCloudTraceExporter instance or None if not enabled

    Raises:
        ConfigurationError: If GCP credentials are unavailable or invalid
    """
    if not config.gcp_trace_enabled:
        return None

    from ..core.gcp_auth import get_google_credentials, get_credentials_for_config
    from ..exporters.gcp_trace_exporter import GCPCloudTraceExporter
    from ..utils.exceptions import ConfigurationError

    # Get GCP credentials
    credentials = get_google_credentials(config)
    if not credentials:
        raise ConfigurationError(
            "GCP Cloud Trace enabled but no credentials available. "
            "Configure gcp_credentials_path, gcp_credentials_json, or ensure ADC is available."
        )

    # Determine project ID
    project_id = config.gcp_project_id
    if not project_id:
        # Extract from credentials
        creds = get_credentials_for_config(config)
        if creds:
            project_id = creds.project_id
        else:
            raise ConfigurationError(
                "Could not determine GCP project_id for Cloud Trace. "
                "Set gcp_project_id in config or use credentials with project_id."
            )

    # Create GCP Cloud Trace exporter
    trace_exporter = GCPCloudTraceExporter(
        project_id=project_id, credentials=credentials, resource=resource
    )

    import logging

    logger = logging.getLogger(__name__)
    logger.info("GCP Cloud Trace exporter enabled for project: %s", project_id)

    return trace_exporter


def setup_prometheus_exporter(
    prometheus_enabled: bool,
    prometheus_port: int = 9464,
    prometheus_host: str = "127.0.0.1",
) -> Tuple[Optional[Any], Optional[Callable[[], None]]]:
    """Set up Prometheus MetricReader with HTTP server for scraping.

    Creates a PrometheusMetricReader that exposes metrics on an HTTP endpoint
    for Prometheus to scrape. The HTTP server runs in a daemon thread and
    can be stopped via the returned callback.

    Architecture:
        This function integrates OpenTelemetry's PrometheusMetricReader with
        prometheus_client's WSGI app. The PrometheusMetricReader collects OTel
        metrics and writes them to the default prometheus_client REGISTRY, which
        the WSGI app reads to serve /metrics endpoint.

    Port Conflict Strategy:
        If the port is already in use (OSError), the function logs a warning and
        returns (None, None). This allows graceful degradation to OTLP-only export.
        The SDK will continue to operate normally without Prometheus metrics.
        This is intentional fail-soft behavior - no retries, no random ports.

    Lifecycle:
        - HTTP server starts immediately in a daemon thread
        - Server can be stopped by calling the returned shutdown callback
        - Daemon thread ensures process exit isn't blocked by the server
        - For batch jobs, consider using OTLP push instead of Prometheus pull

    Args:
        prometheus_enabled: Whether to enable Prometheus exporter
        prometheus_port: Port for Prometheus HTTP server (default: 9464)
        prometheus_host: Host to bind HTTP server (default: 127.0.0.1)

    Returns:
        Tuple of (PrometheusMetricReader or None, ShutdownCallback or None)
        Both are None if disabled or if initialization fails.

    Raises:
        No exceptions raised. All errors are caught and logged, returning (None, None).
    """
    if not prometheus_enabled:
        return None, None

    try:
        from opentelemetry.exporter.prometheus import PrometheusMetricReader
        from prometheus_client import make_wsgi_app
    except ImportError:
        logging.error(
            "opentelemetry-exporter-prometheus not installed. "
            "Cannot enable Prometheus exporter. "
            "Install with pip install mca-sdk[prometheus]"
        )
        return None, None

    try:
        # Create WSGI app for Prometheus metrics
        app = make_wsgi_app()

        # Create HTTP server
        # Allow reuse address to minimize "Address already in use" issues on quick restarts
        WSGIServer.allow_reuse_address = True
        httpd = make_server(prometheus_host, prometheus_port, app)

        # Start server in a daemon thread
        thread = threading.Thread(target=httpd.serve_forever)
        thread.daemon = True
        thread.start()

        # Define shutdown callback
        def shutdown_server():
            logging.debug("Shutting down Prometheus HTTP server...")
            httpd.shutdown()
            httpd.server_close()
            thread.join(timeout=2.0)
            logging.debug("Prometheus HTTP server stopped.")

        # Create PrometheusMetricReader
        # No need to pass registry explicitly, it uses default registry which make_wsgi_app uses
        reader = PrometheusMetricReader()

        logging.info(
            f"Prometheus metrics exposed on http://{prometheus_host}:{prometheus_port}/metrics"
        )
        return reader, shutdown_server

    except OSError as e:
        # Finding 8 fix: Fail-fast for port conflicts (production safety)
        if "Address already in use" in str(e):
            from ..utils.exceptions import ConfigurationError

            raise ConfigurationError(
                f"Prometheus port {prometheus_port} already in use. "
                f"Another service may be bound to this port. "
                "Change MCA_PROMETHEUS_PORT or disable with MCA_PROMETHEUS_ENABLED=false"
            ) from e
        else:
            # Other OS errors - log and continue
            logging.error(f"Failed to start Prometheus exporter: {e}")
            return None, None
    except Exception as e:
        # Other errors (import issues, etc.) - log and continue
        logging.error(f"Unexpected error initializing Prometheus exporter: {e}")
        return None, None


def setup_all_providers(
    service_name: str,
    model_id: Optional[str] = None,
    model_version: str = "0.3.0",
    team_name: Optional[str] = None,
    model_category: str = "internal",
    model_type: str = "regression",
    collector_endpoint: str = "http://localhost:4318",
    metric_export_interval_ms: int = 5000,
    log_batch_size: int = 100,
    trace_batch_size: int = 100,
    extra_resource: Optional[Dict[str, str]] = None,
    use_buffering: bool = False,
    retry_policy: Optional[RetryPolicy] = None,
    persist_queue: bool = False,
    persist_path: Optional[str] = None,
    persist_min_disk_space_mb: float = 10.0,
    gcp_logging_enabled: bool = False,
    gcp_project_id: Optional[str] = None,
    gcp_log_name: Optional[str] = None,
    gcp_resource_type: str = "global",
    gcp_credentials=None,
    gcp_log_timeout: float = 60.0,
    prometheus_enabled: bool = False,
    prometheus_port: int = 9464,
    prometheus_host: str = "127.0.0.1",
    **resource_kwargs,
) -> tuple[
    MeterProvider,
    LoggerProvider,
    TracerProvider,
    Resource,
    Optional["BufferedOTLPMetricExporter"],
    Optional["BufferedOTLPLogExporter"],
    Optional["BufferedOTLPSpanExporter"],
    Optional[Any],
    Optional[Callable[[], None]],
]:
    """Set up all OpenTelemetry providers with consistent configuration.

    This is a convenience function that sets up metrics, logs, and traces
    with a single call. Uses two-field taxonomy for model classification.

    Args:
        service_name: Name of the service
        model_id: Unique model identifier
        model_version: Model version string
        team_name: Team responsible for the model
        model_category: High-level category ("internal" or "vendor")
        model_type: Specific type ("regression", "time-series", "classification", "generative", "agentic")
        collector_endpoint: Base OTLP collector endpoint (without /v1/metrics suffix)
        metric_export_interval_ms: Interval for metric export in milliseconds
        log_batch_size: Batch size for log export
        trace_batch_size: Batch size for trace export
        extra_resource: Additional resource attributes from registry
        use_buffering: Whether to use buffered exporters with retry logic
        retry_policy: RetryPolicy for retries (used if use_buffering=True)
        persist_queue: Whether to persist queue to disk (default: False)
        persist_path: Path for queue persistence (required if persist_queue=True)
        persist_min_disk_space_mb: Minimum free disk space (MB) for persistence (default: 10.0)
        prometheus_enabled: Whether to enable Prometheus exporter
        prometheus_port: Port for Prometheus HTTP server
        prometheus_host: Host to bind Prometheus HTTP server
        **resource_kwargs: Additional resource attributes

    Returns:
        Tuple of (MeterProvider, LoggerProvider, TracerProvider, Resource,
                  BufferedMetricExporter, BufferedLogExporter, BufferedSpanExporter,
                  PrometheusMetricReader, PrometheusShutdownCallback)
        The buffered exporters are None if buffering is disabled.
        PrometheusMetricReader is None if Prometheus is disabled.
    """
    # Create resource with model metadata
    resource = create_resource_attributes(
        service_name=service_name,
        model_id=model_id,
        model_version=model_version,
        team_name=team_name,
        model_category=model_category,
        model_type=model_type,
        extra_resource=extra_resource,
        **resource_kwargs,
    )

    # Setup providers (with optional buffering and persistence)
    meter_provider, metric_exporter, prometheus_reader, prometheus_shutdown = setup_meter_provider(
        resource=resource,
        endpoint=f"{collector_endpoint}/v1/metrics",
        export_interval_ms=metric_export_interval_ms,
        use_buffering=use_buffering,
        retry_policy=retry_policy,
        persist_queue=persist_queue,
        persist_path=persist_path,
        persist_min_disk_space_mb=persist_min_disk_space_mb,
        prometheus_enabled=prometheus_enabled,
        prometheus_port=prometheus_port,
        prometheus_host=prometheus_host,
    )

    # Create GCP logging config if enabled
    from ..config.settings import LoggingConfig

    gcp_config = None
    if gcp_logging_enabled:
        gcp_config = LoggingConfig(
            gcp_logging_enabled=gcp_logging_enabled,
            gcp_project_id=gcp_project_id,
            gcp_log_name=gcp_log_name,
            gcp_resource_type=gcp_resource_type,
            gcp_credentials=gcp_credentials,
            gcp_log_timeout=gcp_log_timeout,
        )

    logger_provider, log_exporter = setup_logger_provider(
        resource=resource,
        endpoint=f"{collector_endpoint}/v1/logs",
        batch_size=log_batch_size,
        use_buffering=use_buffering,
        retry_policy=retry_policy,
        persist_queue=persist_queue,
        persist_path=persist_path,
        persist_min_disk_space_mb=persist_min_disk_space_mb,
        gcp_config=gcp_config,
    )

    tracer_provider, span_exporter = setup_tracer_provider(
        resource=resource,
        endpoint=f"{collector_endpoint}/v1/traces",
        batch_size=trace_batch_size,
        use_buffering=use_buffering,
        retry_policy=retry_policy,
        persist_queue=persist_queue,
        persist_path=persist_path,
        persist_min_disk_space_mb=persist_min_disk_space_mb,
    )

    return (
        meter_provider,
        logger_provider,
        tracer_provider,
        resource,
        metric_exporter,
        log_exporter,
        span_exporter,
        prometheus_reader,
        prometheus_shutdown,
    )
