import collections.abc
import dataclasses
import datetime

from sila.framework.constraints import Set as SiLASet
from sila.framework.data_types import Date, Integer, Real, String, Time, Timestamp


@dataclasses.dataclass
class Set(SiLASet):
    """
    A constraint that enforces that a value is part of a defined set of values.

    Raises:
      ValueError: If the list of allowed values is empty.
    """

    values: collections.abc.Sequence[str | int | float | datetime.date | datetime.time | datetime.datetime]

    def __post_init__(self):
        if not self.values:
            msg = "Expected a non-empty list of values for Set constraint."
            raise ValueError(msg)

        values_type = type(self.values[0])

        if issubclass(values_type, str):
            self.values = [String(value) for value in self.values]
        elif issubclass(values_type, int):
            self.values = [Integer(value) for value in self.values]
        elif issubclass(values_type, float):
            self.values = [Real(value) for value in self.values]
        elif issubclass(values_type, datetime.datetime):
            self.values = [Timestamp.from_datetime(value) for value in self.values]
        elif issubclass(values_type, datetime.date):
            self.values = [Date.from_date(value) for value in self.values]
        elif issubclass(values_type, datetime.time):
            self.values = [Time.from_time(value) for value in self.values]
        else:
            msg = (
                "Expected type of str, int, float, datetime.date, datetime.time, datetime.datetime, "
                f"Instead received type '{values_type}'."
            )
            raise TypeError(msg)
        return super().__post_init__()
