from acl_engine.parser import ACLParseError, VariableRef, parse_acl


def test_parse_acl_with_metadata_and_assignment() -> None:
    commands = parse_acl(
        '@reason="debug"\nfile.read(path="main.py") -> code\nutil.echo(value=code) -> echoed\n'
    )

    assert len(commands) == 2
    assert commands[0].action == "file.read"
    assert commands[0].params == {"path": "main.py"}
    assert commands[0].assign == "code"
    assert commands[0].meta == {"reason": "debug"}
    assert commands[1].assign == "echoed"
    assert isinstance(commands[1].params["value"], VariableRef)


def test_parse_acl_handles_lists_numbers_and_spacing() -> None:
    commands = parse_acl('system.run( command = "python" , args = ["app.py", 1, false] ) -> result')

    assert commands[0].params["command"] == "python"
    assert commands[0].params["args"] == ["app.py", 1, False]
    assert commands[0].assign == "result"


def test_parse_acl_rejects_invalid_syntax() -> None:
    try:
        parse_acl('file.read(path="main.py" -> code')
    except ACLParseError as error:
        assert error.line_number == 1
    else:  # pragma: no cover
        raise AssertionError("expected ACLParseError")


def test_parse_acl_supports_objects_and_selectors() -> None:
    commands = parse_acl(
        'util.echo(value={message="hello", nested={count=1}, item=items[0], out=result.stdout}) -> payload'
    )

    payload = commands[0].params["value"]
    assert payload["message"] == "hello"
    assert payload["nested"] == {"count": 1}
    assert isinstance(payload["item"], VariableRef)
    assert payload["item"].selectors == (0,)
    assert isinstance(payload["out"], VariableRef)
    assert payload["out"].selectors == ("stdout",)


def test_parse_acl_rejects_uppercase_action_names() -> None:
    try:
        parse_acl('File.read(path="main.py")')
    except ACLParseError as error:
        assert error.line_number == 1
    else:  # pragma: no cover
        raise AssertionError("expected ACLParseError")


def test_parse_acl_rejects_multiple_dot_action_names() -> None:
    try:
        parse_acl('file.read.extra(path="main.py")')
    except ACLParseError as error:
        assert error.line_number == 1
    else:  # pragma: no cover
        raise AssertionError("expected ACLParseError")
