import unittest

from pylabrobot.plate_reading.tecan.spark20m.spark_packet_parser import (
  SparkPacket,
  SparkParser,
  parse_single_spark_packet,
)


class TestSparkPacket(unittest.TestCase):
  def test_packet_too_short(self) -> None:
    with self.assertRaises(ValueError) as ctx:
      SparkPacket(b"\x81\x00\x00")  # Only 3 bytes, need at least 5
    self.assertIn("too short", str(ctx.exception))

  def test_packet_payload_truncated(self) -> None:
    # Header says payload is 10 bytes, but we only provide 5
    with self.assertRaises(ValueError) as ctx:
      SparkPacket(b"\x81\x00\x00\x0a\x01\x02\x03\x04\x05")
    self.assertIn("shorter than indicated", str(ctx.exception))

  def test_valid_resp_ready_packet(self) -> None:
    # Type 129 (RespReady), seq 0, payload len 0, checksum
    data = bytes([129, 0, 0, 0, 129])  # checksum = XOR of header = 129
    packet = SparkPacket(data)
    self.assertEqual(packet.type, "RespReady")
    self.assertEqual(packet.seq_num, 0)
    self.assertEqual(packet.payload_len, 0)

  def test_valid_resp_message_packet(self) -> None:
    # Type 133 (RespMessage), seq 1, payload with message number and text
    payload = b"\x00\x64format|arg1"  # number=100, format="format", args=["arg1"]
    header = bytes([133, 1, 0, len(payload)])
    checksum = 0
    for b in header + payload:
      checksum ^= b
    data = header + payload + bytes([checksum])
    packet = SparkPacket(data)
    self.assertEqual(packet.type, "RespMessage")
    self.assertEqual(packet.parsed_payload["number"], 100)
    self.assertEqual(packet.parsed_payload["format"], "format")
    self.assertEqual(packet.parsed_payload["args"], ["arg1"])


class TestSparkParser(unittest.TestCase):
  def test_parser_handles_invalid_packets(self) -> None:
    # Mix of valid and invalid packets
    valid_packet = bytes([129, 0, 0, 0, 129])
    invalid_packet = b"\x81\x00"  # Too short

    parser = SparkParser([valid_packet, invalid_packet])

    # Should have parsed the valid one
    self.assertEqual(len(parser.all_packets), 1)
    self.assertEqual(parser.all_packets[0].type, "RespReady")

  def test_parser_empty_input(self) -> None:
    parser = SparkParser([])
    self.assertEqual(len(parser.all_packets), 0)
    self.assertEqual(parser.process_all_sequences(), {})


class TestParseSingleSparkPacket(unittest.TestCase):
  def test_returns_error_dict_on_failure(self) -> None:
    result = parse_single_spark_packet(b"\x00")  # Too short
    self.assertIn("error", result)
    self.assertIn("hex_string", result)

  def test_returns_packet_dict_on_success(self) -> None:
    data = bytes([129, 5, 0, 0, 129 ^ 5])  # RespReady, seq 5
    result = parse_single_spark_packet(data)
    self.assertEqual(result["type"], "RespReady")
    self.assertEqual(result["seq_num"], 5)


if __name__ == "__main__":
  unittest.main()
