#!/usr/bin/env python3

# We can't import ei-scanner, so let's go via this route. The proper
# handling would be to have ei-scanner be the entry point for a ei_scanner
# module but.. well, one day we'll do that maybe.
import pytest

try:
    from eiscanner import parse, scanner, Protocol
except ImportError:
    print("Run tests from within the build directory")
    pytest.skip(allow_module_level=True)

from pathlib import Path

# set to the protocol file by meson
protofile = "@PROTOFILE@"


@pytest.fixture
def protocol_xml() -> Path:
    return Path(protofile)


@pytest.fixture
def protocol(protocol_xml: Path, component: str) -> Protocol:
    print(f"protocol for component {component}")
    return parse(protocol_xml, component)


@pytest.mark.skipif(
    protofile.startswith("@"),
    reason="Protocol XML file path invalid, run tests in the build dir",
)
class TestScanner:
    @pytest.mark.parametrize("component", ("eis", "ei", "brei"))
    def test_ei_names(self, component: str, protocol: Protocol):
        for interface in protocol.interfaces:
            assert interface.name.startswith(component)
            assert not interface.plainname.startswith(component)
            assert not interface.plainname.startswith("ei_")

        # just some manual checks
        assert "handshake" in [i.plainname for i in protocol.interfaces]
        assert "connection" in [i.plainname for i in protocol.interfaces]
        assert "button" in [i.plainname for i in protocol.interfaces]

    @pytest.mark.parametrize("component", ("ei",))
    def test_interface_arg(self, protocol: Protocol):
        intf = next((i for i in protocol.interfaces if i.name == "ei_device"))
        event = next((e for e in intf.events if e.name == "interface"))

        obj, interface_name, version = event.arguments
        assert obj.interface_arg == interface_name
        assert obj.interface_arg_for is None
        assert interface_name.interface_arg_for == obj
        assert interface_name.interface_arg is None
        assert version.interface_arg is None
        assert version.interface_arg_for is None

    def iterate_args(self, protocol: Protocol):
        for interface in protocol.interfaces:
            for request in interface.requests:
                for arg in request.arguments:
                    yield interface, request, arg
            for event in interface.events:
                for arg in event.arguments:
                    yield interface, event, arg

    @pytest.mark.parametrize("component", ("ei",))
    def test_versione_arg(self, protocol: Protocol):
        for interface, message, arg in self.iterate_args(protocol):
            if arg.protocol_type == "new_id":
                if f"{interface.plainname}.{message.name}" not in [
                    "connection.sync",
                ]:
                    assert (
                        arg.version_arg is not None
                    ), f"{interface.name}.{message.name}::{arg.name}"
                    assert (
                        arg.version_arg.name == "version"
                    ), f"{interface.name}.{message.name}::{arg.name}"
            elif arg.name == "version":
                if f"{interface.plainname}.{message.name}" not in [
                    "handshake.handshake_version",
                    "handshake.interface_version",
                ]:
                    assert (
                        arg.version_arg_for is not None
                    ), f"{interface.name}.{message.name}::{arg.name}"
                    assert (
                        arg.version_arg_for.name != "version"
                    ), f"{interface.name}.{message.name}::{arg.name}"
            else:
                assert (
                    arg.version_arg is None
                ), f"{interface.name}.{message.name}::{arg.name}"
                assert (
                    arg.version_arg_for is None
                ), f"{interface.name}.{message.name}::{arg.name}"

    @pytest.mark.parametrize("method", ("yamlfile", "jsonfile", "string"))
    def test_cli_extra_data(self, tmp_path, method):
        result_path = tmp_path / "result"
        tmpl_path = tmp_path / "template"
        with open(tmpl_path, "w") as template:
            template.write(">{{extra.foo}}<")

        if method == "yamlfile":
            extra_path = tmp_path / "extra_data.yml"
            with open(extra_path, "w") as extra_data:
                extra_data.write("foo: 'yes'")
            extra_data_arg = f"--jinja-extra-data-file={extra_path}"
        elif method == "jsonfile":
            extra_path = tmp_path / "extra_data.json"
            with open(extra_path, "w") as extra_data:
                extra_data.write('{"foo": "yes"}')
            extra_data_arg = f"--jinja-extra-data-file={extra_path}"
        elif method == "string":
            extra_data = '{"foo": "yes"}'
            extra_data_arg = f"--jinja-extra-data={extra_data}"
        else:
            pytest.fail(f"Unsupported method {method}")

        try:
            scanner(
                [
                    f"--output={result_path}",
                    extra_data_arg,
                    protofile,
                    str(tmpl_path),
                ]
            )
        except SystemExit as e:
            pytest.fail(reason=f"Unexpected system exit code {e}")

        assert result_path.exists()
        with open(result_path) as fd:
            result = fd.read()
            assert result == ">yes<"
