import os
from types import SimpleNamespace

import pytest

from matterlab_relays import JYdaqRelay


PORT = "/dev/ttyUSB0" if os.name == "posix" else "COM1"


@pytest.fixture
def relay_fixture(mock_serial):
    return JYdaqRelay(com_port=PORT, address=1, channel=1)


def test_initialization_defaults(mock_serial, relay_fixture):
    assert isinstance(relay_fixture, JYdaqRelay)
    assert relay_fixture.address == 1
    assert relay_fixture.channel == 1
    assert relay_fixture.device._connection is mock_serial


def test_constructor_rejects_invalid_channel(mock_serial):
    with pytest.raises(AssertionError, match="JYDAO relay channel must be in \\[0,7\\]!"):
        JYdaqRelay(com_port=PORT, address=1, channel=8)


def test_add_rtu_master_builds_master(relay_fixture, mocker):
    master = mocker.MagicMock()
    rtu_master_spy = mocker.patch("matterlab_relays.jydao_relay.RtuMaster", return_value=master)
    relay_fixture.rtu_master_factory = rtu_master_spy

    relay_fixture._add_rtu_master()

    rtu_master_spy.assert_called_once_with(relay_fixture.device)
    master.set_timeout.assert_called_once_with(0.5)
    master.set_verbose.assert_called_once_with(True)


def test_set_relay_executes_single_coil_write(relay_fixture):
    relay_fixture.relay_rtu_master = SimpleNamespace(execute=lambda *args, **kwargs: (args, kwargs))
    calls = []
    relay_fixture.relay_rtu_master = SimpleNamespace(execute=lambda *args, **kwargs: calls.append((args, kwargs)))

    relay_fixture.set_relay(channel=1, open_channel=True)

    assert calls == [((1,), {"function_code": 5, "starting_address": 1, "output_value": 1})]


def test_set_multiple_relays_executes_bulk_write(relay_fixture):
    calls = []
    relay_fixture.relay_rtu_master = SimpleNamespace(execute=lambda *args, **kwargs: calls.append((args, kwargs)))

    relay_fixture.set_multiple_relays([True, False, 1])

    assert calls == [((1,), {"function_code": 15, "starting_address": 0, "output_value": [1, 0, 1]})]


def test_query_multiple_relays_returns_boolean_list(relay_fixture):
    relay_fixture.relay_rtu_master = SimpleNamespace(execute=lambda *args, **kwargs: (1, 0, 1, 0, 1, 0, 1, 0))

    assert relay_fixture.query_multiple_relays() == [True, False, True, False, True, False, True, False]


def test_query_relay_and_on_property(relay_fixture, mocker):
    query_multiple_spy = mocker.patch.object(relay_fixture, "query_multiple_relays", return_value=[False, True, False, False, False, False, False, False])
    set_spy = mocker.patch.object(relay_fixture, "set_relay")

    assert relay_fixture.query_relay(1) is True
    assert relay_fixture.on is True
    relay_fixture.on = False

    assert query_multiple_spy.call_count == 2
    set_spy.assert_called_once_with(channel=1, open_channel=False)
