import dataclasses
import typing

from sila import Any, Binary, Boolean, Date, Element, Integer, List, Real, String, Structure, Time, Timestamp, datetime
from unitelabs.cdk.sila.data_types.convert_data_type import to_sila


class TestToSila:
    def test_should_convert_string(self):
        value = "Hello, World!"
        responses = {"string_value": Element("StringValue", "String Value", data_type=String)}

        response = to_sila(value, responses)

        assert response == {"string_value": "Hello, World!"}

    def test_should_convert_binary(self):
        value = b"Hello, World!"
        responses = {"binary_value": Element("BinaryValue", "Binary Value", data_type=Binary)}

        response = to_sila(value, responses)

        assert response == {"binary_value": b"Hello, World!"}

    def test_should_convert_boolean(self):
        value = True
        responses = {"boolean_value": Element("BooleanValue", "Boolean Value", data_type=Boolean)}

        response = to_sila(value, responses)

        assert response == {"boolean_value": True}

    def test_should_convert_integer(self):
        value = 42
        responses = {"integer_value": Element("IntegerValue", "Integer Value", data_type=Integer)}

        response = to_sila(value, responses)

        assert response == {"integer_value": 42}

    def test_should_convert_real(self):
        value = 3.141592653589793
        responses = {"real_value": Element("RealValue", "Real Value", data_type=Real)}

        response = to_sila(value, responses)

        assert response == {"real_value": 3.141592653589793}

    def test_should_convert_date(self):
        value = datetime.date(
            year=2022, month=8, day=5, tzinfo=datetime.timezone(datetime.timedelta(hours=12, minutes=34))
        )
        responses = {"date_value": Element("DateValue", "Date Value", data_type=Date)}

        response = to_sila(value, responses)

        assert response == {
            "date_value": datetime.date(
                year=2022, month=8, day=5, tzinfo=datetime.timezone(datetime.timedelta(hours=12, minutes=34))
            )
        }

    def test_should_convert_time(self):
        value = datetime.time(
            hour=12,
            minute=34,
            second=56,
            microsecond=789000,
            tzinfo=datetime.timezone(datetime.timedelta(hours=12, minutes=34)),
        )
        responses = {"time_value": Element("TimeValue", "Time Value", data_type=Time)}

        response = to_sila(value, responses)

        assert response == {
            "time_value": datetime.time(
                hour=12,
                minute=34,
                second=56,
                microsecond=789000,
                tzinfo=datetime.timezone(datetime.timedelta(hours=12, minutes=34)),
            )
        }

    def test_should_convert_timestamp(self):
        value = datetime.datetime(
            year=2022,
            month=8,
            day=5,
            hour=12,
            minute=34,
            second=56,
            microsecond=789000,
            tzinfo=datetime.timezone(datetime.timedelta(hours=12, minutes=34)),
        )
        responses = {"timestamp_value": Element("TimestampValue", "Timestamp Value", data_type=Timestamp)}

        response = to_sila(value, responses)

        assert response == {
            "timestamp_value": datetime.datetime(
                year=2022,
                month=8,
                day=5,
                hour=12,
                minute=34,
                second=56,
                microsecond=789000,
                tzinfo=datetime.timezone(datetime.timedelta(hours=12, minutes=34)),
            )
        }

    def test_should_convert_any(self):
        value = "Hello, World!"
        responses = {"any_value": Element("AnyValue", "Any Value", data_type=Any)}

        response = to_sila(value, responses)

        assert response == {"any_value": "Hello, World!"}

    def test_should_convert_list(self):
        value = ["SiLA 2", "is", "awesome!"]
        responses = {"list_value": Element("ListValue", "List Value", data_type=List)}

        response = to_sila(value, responses)

        assert response == {"list_value": ["SiLA 2", "is", "awesome!"]}

    def test_should_convert_tuple_to_structure(self):
        value = ("Hello, World!", 42)
        responses = {
            "string_value": Element("StringValue", "String Value", data_type=String),
            "integer_value": Element("IntegerValue", "Integer Value", data_type=Integer),
        }

        response = to_sila(value, responses)

        assert response == {"string_value": "Hello, World!", "integer_value": 42}

    def test_should_convert_dict_to_structure(self):
        value = {"string_value": "Hello, World!", "integer_value": 42}
        responses = {
            "string_value": Element("StringValue", "String Value", data_type=String),
            "integer_value": Element("IntegerValue", "Integer Value", data_type=Integer),
        }

        response = to_sila(value, responses)

        assert response == {"string_value": "Hello, World!", "integer_value": 42}

    def test_should_convert_dict_to_nested_structure(self):
        value = {"string_value": "Hello, World!", "integer_value": 42}
        responses = {
            "structure_value": Element(
                "StructureValue",
                "Structure Value",
                data_type=Structure.create(
                    {
                        "string_value": Element("StringValue", "String Value", data_type=String),
                        "integer_value": Element("IntegerValue", "Integer Value", data_type=Integer),
                    }
                ),
            )
        }

        response = to_sila(value, responses)

        assert response == {"structure_value": {"string_value": "Hello, World!", "integer_value": 42}}

    def test_should_convert_typed_dict_to_nested_structure(self):
        class TypedDict(typing.TypedDict):
            string_value: str
            integer_value: int

        value = TypedDict(string_value="Hello, World!", integer_value=42)
        responses = {
            "structure_value": Element(
                "StructureValue",
                "Structure Value",
                data_type=Structure.create(
                    {
                        "string_value": Element("StringValue", "String Value", data_type=String),
                        "integer_value": Element("IntegerValue", "Integer Value", data_type=Integer),
                    }
                ),
            )
        }

        response = to_sila(value, responses)

        assert response == {"structure_value": {"string_value": "Hello, World!", "integer_value": 42}}

    def test_should_convert_class_to_nested_structure(self):
        @dataclasses.dataclass
        class Dataclass:
            string_value: str
            integer_value: int

        value = Dataclass(string_value="Hello, World!", integer_value=42)
        responses = {
            "structure_value": Element(
                "StructureValue",
                "Structure Value",
                data_type=Structure.create(
                    {
                        "string_value": Element("StringValue", "String Value", data_type=String),
                        "integer_value": Element("IntegerValue", "Integer Value", data_type=Integer),
                    }
                ),
            )
        }

        response = to_sila(value, responses)

        assert response == {"structure_value": Dataclass(string_value="Hello, World!", integer_value=42)}
