"""Protocol helper tests."""

import pytest

from pymyarm.constants import Instruction
from pymyarm.protocol import (
    build_instruction_frame,
    build_position_calibrate_frame,
    build_read_frame,
    build_sync_position_frame,
    build_sync_read_frame,
    bytes_to_int16,
    calculate_checksum,
    int16_to_bytes,
    parse_response_frame,
)


def test_calculate_checksum():
    data = [0x01, 0x02, 0x01]
    assert calculate_checksum(data) == 0xFB


def test_build_instruction_frame_ping():
    frame = build_instruction_frame(1, Instruction.PING)
    assert frame == bytes([0xFF, 0xFF, 0x01, 0x02, 0x01, 0xFB])


def test_build_read_frame():
    frame = build_read_frame(1, 0x38, 2)
    assert frame[:2] == bytes([0xFF, 0xFF])
    assert frame[2] == 1
    assert frame[4] == Instruction.READ_DATA


def test_build_sync_read_frame_broadcast():
    frame = build_sync_read_frame(0x38, 2, [1, 2, 3])
    assert frame[2] == 0xFE
    assert frame[4] == Instruction.SYNC_READ


def test_parse_response_frame_ok():
    payload = [1, 4, 0, 0, 8]
    chk = calculate_checksum(payload)
    frame = bytes([0xFF, 0xFF] + payload + [chk])
    parsed = parse_response_frame(frame)
    assert parsed == (1, 0, [0, 8])


def test_int16_bytes_roundtrip():
    value = 2048
    b = int16_to_bytes(value)
    assert b == [0x00, 0x08]
    assert bytes_to_int16(b[0], b[1]) == value


def test_build_position_calibrate_frame_2048():
    frame = build_position_calibrate_frame(1, 2048)
    # [0xFF, 0xFF, 0x01, 0x04, 0x0B, 0x00, 0x08, checksum]
    assert frame[0:2] == bytes([0xFF, 0xFF])
    assert frame[2] == 1  # servo_id
    assert frame[3] == 4  # length = 2 params + 2
    assert frame[4] == Instruction.POSITION_CALIBRATE  # 0x0B
    assert frame[5] == 0x00  # 2048 low byte
    assert frame[6] == 0x08  # 2048 high byte
    expected_chk = calculate_checksum(list(frame[2:7]))
    assert frame[7] == expected_chk


def test_build_position_calibrate_frame_3072():
    frame = build_position_calibrate_frame(1, 3072)
    assert frame[4] == Instruction.POSITION_CALIBRATE
    assert frame[5] == 0x00  # 3072 low byte
    assert frame[6] == 0x0C  # 3072 high byte
    expected_chk = calculate_checksum(list(frame[2:7]))
    assert frame[7] == expected_chk


def test_build_sync_position_frame_rejects_negative_speed():
    with pytest.raises(ValueError, match="non-negative"):
        build_sync_position_frame([(1, 2048)], speed=-1, acceleration=0)


def test_build_sync_position_frame_rejects_negative_position():
    with pytest.raises(ValueError, match="non-negative"):
        build_sync_position_frame([(1, -10)], speed=300, acceleration=0)
