import json

from typeguard import typechecked
from typing import Any, Dict, Union
from wallboard.utils.constants import ALERT_LABELS_TO_THRESHOLD_LABEL, DEFAULT_ALERT_THRESHOLD_LABEL


@typechecked
class AlertConfig:
    """Model class for a singular alert config for a product.

    Enforces attribute schemas (types/allowed values) and defaults.
    Implements to_dict() and from_dict() to allow easy conversion to and from dictionaries.
    """

    def __init__(
        self,
        label: str,
        threshold: Union[int, float, str] = "default",
        enabled: bool = True,
    ):
        self.label = label
        self.threshold = threshold
        self.enabled = enabled

    @property
    def label(self) -> str:
        return self.__label

    @label.setter
    def label(self, label: str):
        assert label in ALERT_LABELS_TO_THRESHOLD_LABEL, f"Unrecognized alert label: {label}"
        self.__label = label
        if self.__label:
            self.__threshold_label = ALERT_LABELS_TO_THRESHOLD_LABEL[self.__label]
        else:
            self.__threshold_label = DEFAULT_ALERT_THRESHOLD_LABEL

    @property
    def threshold(self) -> Union[int, float, str]:
        return self.__threshold

    @threshold.setter
    def threshold(self, threshold: Union[int, float, str]):
        if isinstance(threshold, str):
            assert (
                threshold == "default"
            ), f"Alert threshold must be an int, float, or the string 'default'. Got: {threshold}"
        self.__threshold = threshold

    @property
    def enabled(self) -> bool:
        return self.__enabled

    @enabled.setter
    def enabled(self, enabled: bool):
        self.__enabled = enabled

    def from_dict(self, cfg: Dict[str, Any]):
        """Load this object's attributes from a dictionary

        Parameters
        ----------
        cfg : Dict[str, Any]
            The dictionary to load this object's attributes from. Requires structure:

            {
                "exampleAlertThresholdLabel": int|float|'default',
                "enabled": True|False
            }
        """
        assert len(cfg) <= 2, f"Unexpected number of keys in alert config: {cfg}"
        self.enabled = bool(cfg.pop("enabled", True))
        self.threshold = "default" if not cfg else list(cfg.values())[0]

    def to_dict(self, sparse: bool = False) -> Dict[str, Any]:
        """Convert this object to a dictionary

        Parameters
        ----------
        sparse : bool
            (optional) If true, does not include defaulted fields on the returned dictionary.

        Returns
        -------
        alert_dict : Dict[str, Any]
            This object in dictionary form with the structure:

            {
                "exampleAlertThresholdLabel": int|float|'default',
                "enabled": True|False
            }
        """
        d = {}
        if self.threshold != "default" or not sparse:
            d[self.__threshold_label] = self.threshold
        if not self.enabled or not sparse:
            d["enabled"] = self.enabled
        return d

    def __repr__(self) -> str:
        return json.dumps({self.label: self.to_dict(sparse=True)})
