from typing import Any

from pytest import LogCaptureFixture

import pydase.units as u
from pydase import DataService


def test_class_list_attribute(caplog: LogCaptureFixture) -> None:
    class ServiceClass(DataService):
        attr = [0, 1]

    service_instance = ServiceClass()

    service_instance.attr[0] = 1337
    assert "ServiceClass.attr[0] changed to 1337" in caplog.text
    caplog.clear()


def test_instance_list_attribute(caplog: LogCaptureFixture) -> None:
    class SubClass(DataService):
        name = "SubClass"

    class ServiceClass(DataService):
        def __init__(self) -> None:
            self.attr: list[Any] = [0, SubClass()]
            super().__init__()

    service_instance = ServiceClass()

    service_instance.attr[0] = "Hello"
    assert "ServiceClass.attr[0] changed to Hello" in caplog.text
    caplog.clear()

    service_instance.attr[1] = SubClass()
    assert f"ServiceClass.attr[1] changed to {service_instance.attr[1]}" in caplog.text
    caplog.clear()


def test_reused_instance_list_attribute(caplog: LogCaptureFixture) -> None:
    some_list = [0, 1, 2]

    class ServiceClass(DataService):
        def __init__(self) -> None:
            self.attr = some_list
            self.attr_2 = some_list
            self.attr_3 = [0, 1, 2]
            super().__init__()

    service_instance = ServiceClass()

    service_instance.attr[0] = 20
    assert service_instance.attr == service_instance.attr_2
    assert service_instance.attr != service_instance.attr_3

    assert "ServiceClass.attr[0] changed to 20" in caplog.text
    assert "ServiceClass.attr_2[0] changed to 20" in caplog.text


def test_nested_reused_instance_list_attribute(caplog: LogCaptureFixture) -> None:
    some_list = [0, 1, 2]

    class SubClass(DataService):
        attr_list = some_list

        def __init__(self) -> None:
            self.attr_list_2 = some_list
            super().__init__()

    class ServiceClass(DataService):
        def __init__(self) -> None:
            self.attr = some_list
            self.subclass = SubClass()
            super().__init__()

    service_instance = ServiceClass()

    service_instance.attr[0] = 20

    assert service_instance.attr == service_instance.subclass.attr_list

    assert "ServiceClass.attr[0] changed to 20" in caplog.text
    assert "ServiceClass.subclass.attr_list[0] changed to 20" in caplog.text
    assert "ServiceClass.subclass.attr_list_2[0] changed to 20" in caplog.text


def test_protected_list_attribute(caplog: LogCaptureFixture) -> None:
    """Changing protected lists should not emit notifications for the lists themselves,
    but still for all properties depending on them.
    """

    class ServiceClass(DataService):
        _attr = [0, 1]

        @property
        def list_dependend_property(self) -> int:
            return self._attr[0]

    service_instance = ServiceClass()

    service_instance._attr[0] = 1337
    assert "ServiceClass.list_dependend_property changed to 1337" in caplog.text


def test_converting_int_to_float_entries(caplog: LogCaptureFixture) -> None:
    class ServiceClass(DataService):
        float_list = [0.0]

    service_instance = ServiceClass()
    service_instance.float_list[0] = 1

    assert isinstance(service_instance.float_list[0], float)
    assert "ServiceClass.float_list[0] changed to 1.0" in caplog.text


def test_converting_number_to_quantity_entries(caplog: LogCaptureFixture) -> None:
    class ServiceClass(DataService):
        quantity_list: list[u.Quantity] = [1 * u.units.A]

    service_instance = ServiceClass()
    service_instance.quantity_list[0] = 4  # type: ignore

    assert isinstance(service_instance.quantity_list[0], u.Quantity)
    assert "ServiceClass.quantity_list[0] changed to 4.0 A" in caplog.text
    caplog.clear()

    service_instance.quantity_list[0] = 3.1 * u.units.mA
    assert isinstance(service_instance.quantity_list[0], u.Quantity)
    assert "ServiceClass.quantity_list[0] changed to 3.1 mA" in caplog.text
