from dataclasses import dataclass, field
from datetime import date
from typing import Any, Optional, Type

from django.db.models import Aggregate, FloatField, Sum


@dataclass
class MetricField:
    """
    A DTO Class to represent the metric subfield computed by metric backend for a given Metric Key.

    list_display_kwargs: Contains keyword argument to be inserted in a dp.Field
    """

    key: str
    label: str
    list_display_kwargs: dict[str, Any] = field(default_factory=dict)
    aggregate: Type[Aggregate] | None = Sum
    serializer_kwargs: dict[str, Any] = field(default_factory=dict)
    field_type: type = FloatField
    decorators: list[dict[str, str]] = field(
        default_factory=list
    )  # Set into a wbcore decorator accordingly (e.g. to show the metric into a specific currency)
    help_text: str | None = None  # define if the field needs to show a particular help text


@dataclass
class MetricKey:
    """
    a DTO class to represent the metric generated by a metric backend. It contains general information regarding the type of field the metric stored (e.g. Is percent? precision? etc..)
    """

    key: str
    label: str
    subfields: list[MetricField]
    extra_subfields: list[MetricField] = field(default_factory=list)
    additional_prefixes: list[str] = field(
        default_factory=list
    )  # Define the optional category/groups stored in the metric json (e.g if a variable "x" is stored as well as "past_x", a prefix would be "past")
    subfields_map: dict[str, MetricField] = field(init=False)
    subfields_filter_map: dict[str, str] = field(init=False)

    def __post_init__(self):
        subfields_map = dict()
        subfields_filter_map = dict()
        for subfield in self.subfields:
            subfields_map[f"{self.key}_{subfield.key}"] = subfield
            subfields_filter_map[f"{self.key}_{subfield.key}"] = subfield.key
            for prefix in self.additional_prefixes:
                subfields_map[f"{self.key}_{prefix}_{subfield.key}"] = subfield
                subfields_filter_map[f"{self.key}_{prefix}_{subfield.key}"] = f"{prefix}_{subfield.key}"
        object.__setattr__(self, "subfields_map", subfields_map)
        object.__setattr__(self, "subfields_filter_map", subfields_filter_map)

    def __hash__(self):
        return hash(self.key)

    def get_fields(self, with_prefixed_key: bool = False):
        """
        Returns a generator of key identifier and metric label containing in that metric

        Args:
            with_prefixed_key: Default to False. If True yield also the prefixed fields

        Returns:
            a generator of tuple of strings
        """
        for subfield in self.subfields:
            yield f"{self.key}_{subfield.key}", f"{self.label} {subfield.label}"
            if with_prefixed_key:
                for prefix in self.additional_prefixes:
                    yield f"{self.key}_{prefix}_{subfield.key}", f"{self.label} {subfield.label} ({prefix.title()})"


@dataclass
class Metric:
    """
    A DTO class to hold the metrics values computed by a metric backend. The object will be parsed by an orchestrator into a InstrumentMetric object
    """

    basket_id: int
    basket_content_type_id: int
    key: str
    metrics: dict
    date: Optional[date] = None
    instrument_id: int | None = None
    dependency_metrics: list["Metric"] = field(default_factory=list)
