import importlib.util
import sys
import unittest
from pathlib import Path
from unittest.mock import patch


def load_package():
    package_dir = Path(__file__).resolve().parents[1] / "kocoafab-robodog"
    init_py = package_dir / "__init__.py"
    spec = importlib.util.spec_from_file_location(
        "kocoafab_robodog",
        init_py,
        submodule_search_locations=[str(package_dir)],
    )
    module = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = module
    spec.loader.exec_module(module)
    robodog_module = sys.modules["kocoafab_robodog.robodog"]
    return module, robodog_module


def make_manual_response(command):
    packet = bytearray(0x20)
    packet[4] = 0x20
    packet[6] = 0x02
    packet[7] = command
    return packet


class AiCommandSyncTest(unittest.TestCase):
    def test_ai_mode_waits_for_a_new_response_when_threads_are_running(self):
        package, robodog_module = load_package()
        dog = package.RoboDog()
        stale_packet = make_manual_response(0x12)
        fresh_packet = make_manual_response(0x11)
        dog.rcvPacket[: len(stale_packet[:24])] = stale_packet[:24]
        dog._rx_sequence = 1
        dog.isTXThreadRun = True
        dog.isRXThreadRun = True
        sleep_calls = []

        def fake_sleep(_duration):
            sleep_calls.append(_duration)
            dog.rcvPacket[: len(fresh_packet[:24])] = fresh_packet[:24]
            dog._rx_sequence = 2

        with patch.object(robodog_module, "sleep", fake_sleep):
            result = dog.ai_mode(package.FACE_RECOGNITION, timeout=0.05)

        self.assertEqual(0x11, result)
        self.assertGreaterEqual(len(sleep_calls), 1)

    def test_face_tracking_waits_for_a_new_response_when_threads_are_running(self):
        package, robodog_module = load_package()
        dog = package.RoboDog()
        stale_packet = make_manual_response(0x12)
        fresh_packet = make_manual_response(0x12)
        dog.rcvPacket[: len(stale_packet[:24])] = stale_packet[:24]
        dog._rx_sequence = 3
        dog.isTXThreadRun = True
        dog.isRXThreadRun = True
        sleep_calls = []

        def fake_sleep(_duration):
            sleep_calls.append(_duration)
            dog.rcvPacket[: len(fresh_packet[:24])] = fresh_packet[:24]
            dog._rx_sequence = 4

        with patch.object(robodog_module, "sleep", fake_sleep):
            result = dog.face_tracking(package.CLASS1, timeout=0.05)

        self.assertEqual(0x12, result)
        self.assertGreaterEqual(len(sleep_calls), 1)

    def test_ai_mode_keeps_closed_dog_snapshot_behavior(self):
        package, _robodog_module = load_package()
        dog = package.RoboDog()
        packet = make_manual_response(0x13)
        dog.rcvPacket[: len(packet[:24])] = packet[:24]

        self.assertEqual(0x13, dog.ai_mode(3, timeout=0.01))


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