# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.


from typing import Any, Optional, Set, Type


from .._uniffi import CommonMetricData
from .._uniffi import LabeledBoolean
from .._uniffi import LabeledCounter
from .._uniffi import LabeledString
from ..testing import ErrorType


class LabeledMetricBase:
    """
    This implements the developer-facing API for labeled metrics.

    Instances of this class type are automatically generated by
    `glean.load_metrics`, allowing developers to record values that were
    previously registered in the metrics.yaml file.

    Unlike most metric types, LabeledMetricType does not have its own
    corresponding storage, but records metrics for the underlying metric type T
    in the storage for that type. The only difference is that labeled metrics
    are stored with the special key `$category.$name/$label`. The collect
    method knows how to pull these special values back out of the individual
    metric storage and rearrange them correctly in the ping.
    """

    # The following class attribute must be overridden by classes
    # inheriting from LabeledMetricBase:

    # The FFI function to instantiate the labeled metric type
    _ctor: Type

    def __init__(
        self,
        common_metric_data: CommonMetricData,
        labels: Optional[Set[str]] = None,
    ):
        self._inner = self._ctor(common_metric_data, labels)

    def __getitem__(self, item: str) -> Any:
        """
        Get the specific metric for a given label.

        If a set of acceptable labels were specified in the metrics.yaml file,
        and the given label is not in the set, it will be recorded under the
        special `__other__`.

        If a set of acceptable labels was not specified in the metrics.yaml
        file, only the first 16 unique labels will be used. After that, any
        additional labels will be recorded under the special `__other__` label.

        Labels must be snake_case and less than 30 characters. If an invalid
        label is used, the metric will be recorded in the special `__other__`
        label.
        """
        return self._inner.get(item)

    def test_get_num_recorded_errors(self, error_type: ErrorType) -> int:
        """
        Returns the number of errors recorded for the given metric.

        Args:
            error_type (ErrorType): The type of error recorded.
            ping_name (str): (default: first value in send_in_pings) The name
                of the ping to retrieve the metric for.

        Returns:
            num_errors (int): The number of errors recorded for the metric for
                the given error type.
        """
        return self._inner.test_get_num_recorded_errors(error_type)


class LabeledBooleanMetricType(LabeledMetricBase):
    _ctor = LabeledBoolean


class LabeledCounterMetricType(LabeledMetricBase):
    _ctor = LabeledCounter


class LabeledStringMetricType(LabeledMetricBase):
    _ctor = LabeledString


__all__ = [
    "LabeledBooleanMetricType",
    "LabeledCounterMetricType",
    "LabeledStringMetricType",
]
