"""Integration tests – command execution and response parsing."""

from __future__ import annotations

import pytest

from routeros import typed_map

pytestmark = pytest.mark.integration


class TestSystemCommands:
    def test_identity_print(self, client):
        reply = client.run("/system/identity/print")
        assert reply.done is not None
        assert len(reply.re) == 1
        identity = reply.re[0].map
        assert "name" in identity
        assert isinstance(identity["name"], str)

    def test_resource_print(self, client):
        reply = client.run("/system/resource/print")
        assert len(reply.re) == 1
        res = reply.re[0].map
        assert "uptime" in res
        assert "version" in res
        assert "cpu-load" in res

    def test_resource_typed_values(self, client):
        reply = client.run("/system/resource/print")
        res = reply.re[0]

        row = typed_map(res, {
            "cpu-load":       int,
            "free-memory":    int,
            "total-memory":   int,
            "free-hdd-space": int,
        })
        assert isinstance(row["cpu-load"], int)
        assert 0 <= row["cpu-load"] <= 100
        assert row["total-memory"] > 0
        assert row["free-memory"] <= row["total-memory"]

    def test_clock_print(self, client):
        reply = client.run("/system/clock/print")
        assert len(reply.re) == 1
        clk = reply.re[0].map
        assert "time" in clk
        assert "date" in clk


class TestInterfaceCommands:
    def test_interface_print(self, client):
        reply = client.run("/interface/print")
        assert reply.done is not None
        # RouterOS always has at least one interface.
        assert len(reply.re) >= 1
        for s in reply.re:
            assert "name" in s.map
            assert "type" in s.map

    def test_interface_typed_map(self, client):
        reply = client.run("/interface/print")
        for s in reply.re:
            row = typed_map(s, {
                "name":    str,
                "mtu":     int,
                "running": bool,
                "disabled": bool,
            })
            assert isinstance(row.get("mtu", 0), int)
            assert isinstance(row.get("running", False), bool)

    def test_interface_proplist_filter(self, client):
        """Using =.proplist= should limit returned fields."""
        reply = client.run("/interface/print", "=.proplist=name,type")
        for s in reply.re:
            assert "name" in s.map
            assert "type" in s.map


class TestIPAddressCommands:
    def test_ip_address_print(self, client):
        reply = client.run("/ip/address/print")
        assert reply.done is not None
        # May be empty if no addresses configured.
        for s in reply.re:
            assert "address" in s.map
            assert "interface" in s.map

    def test_ip_route_print(self, client):
        reply = client.run("/ip/route/print")
        assert reply.done is not None

    def test_sentence_map_access(self, client):
        """Sentence.map must be a plain dict with string values."""
        reply = client.run("/interface/print")
        if reply.re:
            s = reply.re[0]
            assert isinstance(s.map, dict)
            for k, v in s.map.items():
                assert isinstance(k, str)
                assert isinstance(v, str)


class TestErrorHandling:
    def test_unknown_command_raises_device_error(self, client):
        from routeros import DeviceError

        with pytest.raises(DeviceError):
            client.run("/this/command/does/not/exist")

    def test_invalid_argument_raises_device_error(self, client):
        """Send a malformed =set= argument to trigger a !trap from the device."""
        from routeros import DeviceError

        # Trying to set a read-only or non-existent property causes a !trap.
        with pytest.raises(DeviceError):
            client.run("/ip/address/set", "=.id=*FFFFFFFF", "=address=INVALID")

    def test_error_message_is_string(self, client):
        from routeros import DeviceError

        try:
            client.run("/no-such/path")
        except DeviceError as e:
            assert isinstance(e.message, str)
            assert len(e.message) > 0


class TestRunArgs:
    def test_run_args_equivalent_to_run(self, client):
        r1 = client.run("/system/identity/print")
        r2 = client.run_args(["/system/identity/print"])
        assert r1.re[0].map["name"] == r2.re[0].map["name"]

    def test_multiple_commands_sequential(self, client):
        for _ in range(5):
            reply = client.run("/system/identity/print")
            assert reply.done is not None
