"""Unit tests for commands.py's command-code table and resolution logic.

Unlike test_codegen.py/test_real_scripts.py, these aren't byte-for-byte
regression tests against real RoborunPlus output (most of the ~300 manual-
sourced codes have never been individually bytecode-confirmed -- see
commands.py's docstring). These tests just pin down the resolution logic
itself: the 10 bytecode-confirmed values, and the verb-specific handling
for the handful of aliases that mean different things depending on which
device-I/O function they're passed to.
"""

import pytest

from microbasic_compiler.commands import UnknownCommandError, resolve_command_code

# The 10 values independently confirmed by decoding real RoborunPlus .hex
# output (batches 4 and 6) -- these must never drift.
BYTECODE_CONFIRMED = {
    "_VAR": 6,
    "_ALIM": 42,
    "_MS": 16,
    "_GO": 0,
    "_ESTOP": 14,
    "_AI": 16,
    "_BATAMPS": 12,
    "_MOTAMPS": 0,
    "_MOTPWR": 2,
    "_V": 13,
}


@pytest.mark.parametrize("name,code", sorted(BYTECODE_CONFIRMED.items()))
def test_bytecode_confirmed_codes(name: str, code: int):
    assert resolve_command_code(name) == code


@pytest.mark.parametrize(
    "name,verb,code",
    [
        ("_BRUN", "setcommand", 0x0C),
        ("_BRUN", "setconfig", 0x48),
        ("_CSS", "setcommand", 0x6C),
        ("_CSS", "getvalue", 0x6E),
    ],
)
def test_verb_specific_codes(name: str, verb: str, code: int):
    assert resolve_command_code(name, verb=verb) == code


@pytest.mark.parametrize("name", ["_BRUN", "_CSS"])
def test_verb_specific_names_not_in_flat_table(name: str):
    # _BRUN/_CSS only mean something once you know the verb -- neither is
    # in the flat COMMAND_CODES table, so resolving without a matching verb
    # (or with no verb at all) must raise rather than silently guess.
    with pytest.raises(UnknownCommandError):
        resolve_command_code(name)


def test_unknown_command_raises():
    with pytest.raises(UnknownCommandError):
        resolve_command_code("_NOT_A_REAL_COMMAND")


def test_case_and_underscore_insensitive():
    assert resolve_command_code("_var") == resolve_command_code("_VAR") == resolve_command_code("VAR")
