import os
import logging
from typing import Optional, Dict
from ..interfaces import MetricProvider
from ..utils import convert_tags, get_logger

class DatadogProvider(MetricProvider):
    def __init__(
        self,
        api_key: str = None,
        app_key: str = None,
        host_name: str = None,
        logger: Optional[logging.Logger] = None
    ):
        self.logger = logger or get_logger(__name__)

        # Try importing datadog library, handle if not installed
        try:
            from datadog import initialize, statsd, api
            self.statsd = statsd
            self.api = api
        except ImportError:
            self.logger.warning("Warning: datadog library not found. Metrics will be dropped.")
            self.statsd = None
            self.api = None
            initialize = None

        # api_key is mandatory for initialize(); skip if not supplied or library missing
        options = {}
        if api_key:
            options['api_key'] = api_key
        if app_key:
            options['app_key'] = app_key
        if host_name:
            options['hostname'] = host_name
            options['hostname_from_config'] = False

        if 'api_key' in options and initialize is not None:
            initialize(**options)

    def send(self, *args, **kwargs):
        """
        Unified send method for Datadog.
        Expects 'metric_type' in kwargs to determine the type of metric/event.
        Expects 'mode' in kwargs to determine submission channel: 'dogstatsd' (default) or 'api'.
        """
        if not self.statsd: return

        metric_type = kwargs.pop('metric_type', None)
        mode = kwargs.pop('mode', 'dogstatsd')
        self.logger.info({"event": "datadog send called", "metric_type": metric_type, "mode": mode})

        if metric_type == 'count':
            self._count(*args, mode=mode, **kwargs)
        elif metric_type == 'increment':
            self._increment(*args, mode=mode, **kwargs)
        elif metric_type == 'decrement':
            self._decrement(*args, mode=mode, **kwargs)
        elif metric_type == 'gauge':
            self._gauge(*args, mode=mode, **kwargs)
        elif metric_type == 'histogram':
            # AWS Lambda Datadog Extension does not support histograms, use distributions instead
            if os.getenv('AWS_EXECUTION_ENV'):
                self._distribution(*args, mode=mode, **kwargs)
            else:
                self._histogram(*args, mode=mode, **kwargs)
        elif metric_type == 'event':
            self._log_event(*args, mode=mode, **kwargs)
        elif metric_type == 'rate':
            self._rate(*args, mode=mode, **kwargs)
        elif metric_type == 'distribution':
            self._distribution(*args, mode=mode, **kwargs)
        elif metric_type == 'set':
            self._set(*args, mode=mode, **kwargs)

        else:
             # Fallback/Inference
            if 'event_name' in kwargs and 'message' in kwargs:
                 self._log_event(*args, mode=mode, **kwargs)
            else:
                 self._gauge(*args, mode=mode, **kwargs)

    def _log_event(self, event_name: str, message: str, tags: Optional[Dict[str, str]] = None, event_type: str = "info", mode: str = 'dogstatsd'):
        """
        EVENT
        Send an event.

        Best For:
        - Logging significant, discrete occurrences that are not regular metrics.
        - Correlating events (deployments, errors) with metric spikes.

        Examples:
        - "Deployment Started": New version of order service deployed.
        - "Payment Gateway Down": Critical alert from payment microservice.
        - "Cache Flushed": Manual admin action.

        DogStatsD:
        - dog.event(title, text, tags=tags, alert_type=alert_type)

        API:
        - api.Event.create(title=title, text=text, tags=tags, alert_type=alert_type)
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api' and self.api:
             self.api.Event.create(title=event_name, text=message, tags=tag_list, alert_type=event_type)
        else:
            self.statsd.event(title=event_name, message=message, tags=tag_list, alert_type=event_type)

    def _count(self, metric: str, value: int = 1, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        COUNT
        The COUNT metric submission.

        Definition:
        The COUNT metric submission type represents the total number of event occurrences in one time interval.
        A COUNT can be used to track the total number of connections made to a database or the total number of requests to an endpoint.
        This number of events can accumulate or decrease over time—it is not monotonically increasing.

        Best For:
        - Tracking the total number of things happening.
        - Counting errors, requests, or completed operations.

        Examples:
        - Number of orders received from OMS: `rx.orders.count`
        - Number of prescriptions rejected: `rx.prescription.rejected`
        - Number of order validation errors: `rx.api.errors`

        DogStatsD:
        - dog.count(metric, value, tags=tags)
        - dog.increment(metric, value=1, tags=tags) (if value is 1)

        API:
        - api.Metric.send(metric=metric, points=value, tags=tags, type='count')
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api' and self.api:
            self.api.Metric.send(metric=metric, points=value, tags=tag_list, type='count')
        else:
            if value == 1:
                self.statsd.increment(metric, tags=tag_list)
                self.statsd.decrement(metric, tags=tag_list)
            else:
                self.statsd.count(metric, value=value, tags=tag_list)

    def _increment(self, metric: str, value: int = 1, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        INCREMENT
        The INCREMENT metric submission.

        Definition:
        The INCREMENT metric submission type represents the total number of event occurrences in one time interval.

        Best For:
        - Tracking the total number of things happening.
        - Counting errors, requests, or completed operations.

        Examples:
        - Number of orders received from OMS: `rx.orders.count`
        - Number of prescriptions rejected: `rx.prescription.rejected`
        - Number of order validation errors: `rx.api.errors`

        DogStatsD:
        - dog.increment(metric, value=1, tags=tags) (if value is 1)

        API:
        - self.logger.warning("'increment' is a DogStatsD specific type and cannot be submitted via API. Ignoring.")
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api':
            self.logger.warning("'increment' is a DogStatsD specific type and cannot be submitted via API. Ignoring.")
        else:
            self.statsd.increment(metric, value=value, tags=tag_list)

    def _decrement(self, metric: str, value: int = 1, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        DECREMENT
        The DECREMENT metric submission.

        Definition:
        The INCREMENT metric submission type represents the total number of event occurrences in one time interval.

        Best For:
        - Tracking the total number of things happening.
        - Counting errors, requests, or completed operations.

        Examples:
        - Number of orders received from OMS: `rx.orders.count`
        - Number of prescriptions rejected: `rx.prescription.rejected`
        - Number of order validation errors: `rx.api.errors`

        DogStatsD:
        - dog.decrement(metric, value=1, tags=tags) (if value is 1)

        API:
        - self.logger.warning("'decrement' is a DogStatsD specific type and cannot be submitted via API. Ignoring.")
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api':
            self.logger.warning("'decrement' is a DogStatsD specific type and cannot be submitted via API. Ignoring.")
        else:
            self.statsd.decrement(metric, value=value, tags=tag_list)

    def _gauge(self, metric: str, value: float, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        GAUGE
        The GAUGE metric submission.

        Definition:
        The GAUGE metric submission type represents a snapshot of events in one time interval.
        This representative snapshot value is the last value submitted to the Agent during a time interval.
        A GAUGE can be used to take a measure of something reporting continuously—like the available disk space or memory used.

        Best For:
        - Tracking a value that goes up and down over time.
        - Snapshots of current state.

        Examples:
        - Current number of prescriptions in the prescription queue: `rx.prescription.queue`
        - Active threads in the order processing microservice: `rx.system.threads`
        - Memory usage of the diet formulation service: `rx.system.memory`

        DogStatsD:
        - dog.gauge(metric, value, tags=tags)

        API:
        - api.Metric.send(metric=metric, points=value, tags=tag_list, type='gauge')
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api' and self.api:
            self.api.Metric.send(metric=metric, points=value, tags=tag_list, type='gauge')
        else:
            self.statsd.gauge(metric, value=value, tags=tag_list)

    def _histogram(self, metric: str, value: float, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        HISTOGRAM
        The HISTOGRAM metric submission.

        Definition:
        The HISTOGRAM metric submission type represents the statistical distribution of a set of values calculated Agent-side in one time interval.
        The Agent aggregates the values that are sent in a defined time interval and produces different metrics (min, max, avg, etc.) which represent the set of values.

        Best For:
        - Calculating statistical distributions (min, max, avg, 95th percentile) of a value.
        - Performance timing and latency.

        Examples:
        - Latency of the vet validation microservice: `rx.vet.validation.latency`
        - Size of the diet formulation payload: `rx.diet.payload.size`
        - Time taken to process a payment: `rx.payment.duration`

        DogStatsD:
        - dog.histogram(metric, value, tags=tags)

        API:
        - Not directly supported as a simple point submission type usually (historically).
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api':
            self.logger.warning("'histogram' is best sent via DogStatsD. Dropping or falling back.")
        else:
            self.statsd.histogram(metric, value=value, tags=tag_list)

    def _distribution(self, metric: str, value: float, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        DISTRIBUTION
        The DISTRIBUTION metric submission.

        Definition:
        The DISTRIBUTION metric submission type represents the global statistical distribution of a set of values calculated across your entire distributed infrastructure in one time interval.
        A DISTRIBUTION can be used to instrument logical objects, like services, independently from the underlying hosts. Aggregations occur on the server-side.

        Best For:
        - Global latency measurement across all microservices/regions.
        - Aggregating percentiles accurately across distributed systems.

        Examples:
        - Global end-to-end order processing latency: `rx.order.global_latency`
        - Distributed database query duration: `rx.db.query.duration`

        DogStatsD:
        - dog.distribution(metric, value, tags=tags)
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api':
            self.logger.warning("'distribution' is a DogStatsD specific type and cannot be submitted via API. Ignoring.")
        else:
            self.statsd.distribution(metric, value=value, tags=tag_list)

    def _set(self, metric: str, value: float, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        SET
        The SET metric submission.

        Definition:
        The SET metric submission type counts the number of unique elements in a group.

        Best For:
        - Counting unique elements in a group.

        Examples:
        - Count unique vets approving prescriptions today: `rx.vets.active`
        - Unique customers placing orders: `rx.customers.unique`
        - Unique microservice instances handling requests: `rx.instances.active`

        DogStatsD:
        - dog.set(metric, value, tags=tags)
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api':
            self.logger.warning("'set' is a DogStatsD specific type and cannot be submitted via API. Ignoring.")
        else:
            self.statsd.set(metric, value=value, tags=tag_list)

    def _rate(self, metric: str, value: float, tags: Optional[Dict[str, str]] = None, mode: str = 'dogstatsd'):
        """
        RATE
        The RATE metric submission.

        Definition:
        The RATE metric submission type represents the total number of event occurrences per second in one time interval.
        A RATE can be used to track how often something is happening—like the frequency of connections made to a database or the flow of requests made to an endpoint.

        Best For:
        - Measuring the frequency of events per second.
        - High-throughput flow monitoring.

        Examples:
        - Order creation requests per second: `rx.orders.rate`
        - Diet consultation API hits per second: `rx.diet.api.rate`

        DogStatsD:
        - Not explicitly supported as a distinct call in standard DogStatsD (often calculated server side from counts).

        API:
        - api.Metric.send(metric=metric, points=value, tags=tags, type='rate')
        """
        tag_list = convert_tags(tags) or []
        if mode == 'api' and self.api:
            self.api.Metric.send(metric=metric, points=value, tags=tag_list, type='rate')
        else:
            self.logger.warning("'rate' type is primarily for API submission. Sending as count (calculated rate) via DogStatsD.")
            self.statsd.count(metric, value=value, tags=tag_list)