import dataclasses
import enum

import sila
from unitelabs.cdk.sila.common.feature import Feature
from unitelabs.cdk.sila.data_types.custom_data_type import CustomDataType
from unitelabs.cdk.sila.data_types.infer_data_type import infer


class TestCustom:
    def test_should_infer_custom_with_basic_type(self):
        feature = Feature()

        @dataclasses.dataclass
        class TestCustomDataType(CustomDataType):
            """Test Custom Data Type."""

            test_custom_data_type: str

        data_type = infer(TestCustomDataType, feature)

        assert issubclass(data_type, sila.Custom)
        assert data_type.identifier == "TestCustomDataType"
        assert data_type.display_name == "Test Custom Data Type"
        assert data_type.description == "Test Custom Data Type."
        assert data_type.data_type == sila.String

    def test_should_infer_custom_with_complex_type(self):
        feature = Feature()

        @dataclasses.dataclass
        class TestCustomDataType(CustomDataType):
            """
            Test Custom Data Type.

            Attributes:
              Value: A value.
              AnotherValue: Another value.
            """

            value: int
            another_value: float

        data_type = infer(TestCustomDataType, feature)

        assert issubclass(data_type, sila.Custom)
        assert data_type.identifier == "TestCustomDataType"
        assert data_type.display_name == "Test Custom Data Type"
        assert data_type.description == "Test Custom Data Type."
        assert issubclass(data_type.data_type, sila.Structure)
        assert data_type.data_type.elements == {
            "value": sila.Element("Value", "Value", "A value.", sila.Integer),
            "another_value": sila.Element("AnotherValue", "Another Value", "Another value.", sila.Real),
        }


class TestEnumDataType(enum.Enum):
    VALUE_A = enum.auto()
    VALUE_B = enum.auto()
    VALUE_C = enum.auto()


class TestEnum:
    def test_should_infer_enum(self):
        feature = Feature()

        data_type = infer(TestEnumDataType, feature)

        assert issubclass(data_type, sila.Constrained)
        assert data_type.data_type == sila.String
        assert data_type.constraints == [
            sila.Set([sila.String("value a"), sila.String("value b"), sila.String("value c")])
        ]

    def test_should_infer_list_of_enum(self):
        feature = Feature()

        data_type = infer(list[TestEnumDataType], feature)

        assert issubclass(data_type, sila.List)
        assert issubclass(data_type.data_type, sila.Constrained)
        assert data_type.data_type.constraints == [
            sila.Set([sila.String("value a"), sila.String("value b"), sila.String("value c")])
        ]

    def test_should_infer_enum_in_structure(self):
        feature = Feature()

        @dataclasses.dataclass
        class CustomEnumDataType:
            """
            Test Custom Data Type.

            Attributes:
              Value: A value.
            """

            value: TestEnumDataType

        data_type = infer(CustomEnumDataType, feature)

        assert issubclass(data_type, sila.Structure)
        assert issubclass(data_type.elements["value"].data_type, sila.Constrained)
        assert data_type.elements["value"].data_type.constraints[0].values == [
            sila.String("value a"),
            sila.String("value b"),
            sila.String("value c"),
        ]

    def test_should_infer_enum_in_custom(self):
        feature = Feature()

        @dataclasses.dataclass
        class CustomEnumDataType(CustomDataType):
            """
            Test Custom Data Type.

            Attributes:
              Value: A value.
            """

            value: TestEnumDataType

        data_type = infer(CustomEnumDataType, feature)

        assert issubclass(data_type, sila.Custom)
        assert issubclass(data_type.data_type, sila.Structure)
        assert issubclass(data_type.data_type.elements["value"].data_type, sila.Constrained)
        assert data_type.data_type.elements["value"].data_type.constraints[0].values == [
            sila.String("value a"),
            sila.String("value b"),
            sila.String("value c"),
        ]
