import asyncio
from typing import AsyncGenerator
from contextlib import asynccontextmanager

from ..commands import RoverCommands, LedMode
from ..exceptions import RoverAckError
from ..generated import RCChannelsCommand, CommandAck, TelemetryData, BatteryData
from ..communicators import AsyncGRPCCommunicator
from ..logging import get_logger


class AsyncRoverClient:
    """
    Асинхронный клиент для управления ровером
    """

    def __init__(self, host: str = "localhost", port: int = 5656, logging: bool = False):
        self._communicator = AsyncGRPCCommunicator(host, port)
        self._is_rc_streaming: bool = False
        self._rc_stream_task = None
        self._rc_stream_interval: float = 0.05
        self._logger = get_logger()
        self.rc_channels = RCChannelsCommand(
            channel1=1500,
            channel2=1500,
            channel3=1500,
            channel4=1500
        )

    async def connect(self, inf_trying: bool = False) -> bool:
        """
        Подключение к роботу
        """
        self._logger.info("connecting to rover...")
        try:
            result = await self._communicator.connect(inf_trying)

            if result:
                self._logger.info("rover connected successfully!")
            else:
                self._logger.error("rover connection failed!")
            return result
        except ConnectionError as e:
            self._logger.error("Connection error during connect:", e=e)
            return False


    async def disconnect(self):
        """
        Отключение от робота
        """
        self._logger.info("disconnecting from rover...")
        await self._communicator.disconnect()
        self._logger.info("rover disconnected!")

    @property
    def is_connected(self) -> bool:
        """
        Статус подключения робота
        """
        return self._communicator.is_connected

    @property
    def is_rc_streaming(self) -> bool:
        """
        Статус потоковой передачи RC-команд
        """
        return self._is_rc_streaming

    async def set_velocity(self, linear: float, angular: float) -> CommandAck:
        """
        Установка линейной и угловой скорости робота

        :param linear: Линейная скорость
        :type linear: float
        :param angular: Угловая скорость
        :type angular: float
        :return: Результат выполнения команды
        :rtype: CommandResult
        """
        try:
            return await self._communicator.send_command(
                RoverCommands.SET_VELOCITY,
                linear=linear,
                angular=angular
            )
        except ConnectionError as e:
            self._logger.error(f"Connection error in set_velocity: {e}")
            return CommandAck(success=False, message=str(e))

    async def led_control(self, r: int, g: int, b: int):
        try:
            return await self._communicator.send_command(
                RoverCommands.LED_CONTROL,
                r=r,
                g=g,
                b=b
            )
        except ConnectionError as e:
            self._logger.error(f"Connection error in led_control: {e}")
            return CommandAck(success=False, message=str(e))

    async def led_custom(self, mode:LedMode, color1: list, color2: list):
        try:
            return await self._communicator.send_command(
                RoverCommands.LED_CUSTOM,
                mode=mode,
                color1=color1,
                color2=color2
            )
        except ConnectionError as e:
            self._logger.error(f"Connection error in led_custom: {e}")
            return CommandAck(success=False, message=str(e))

    async def set_differential_speed(self, left: float, right: float) -> CommandAck:
        """
        Дифференциальное управление скоростями левого и правого колес.

        :param left: Скорость левого колеса в м/с.
                     Положительные значения - вперед,
                     отрицательные - назад
        :type left: float
        :param right: Скорость правого колеса в м/с.
                      Положительные значения - вперед,
                      отрицательные - назад
        :type right: float
        :return: Результат выполнения команды
        :rtype: CommandResult
        """
        try:
            return await self._communicator.send_command(
                RoverCommands.SET_DIFF_SPEED,
                left=left,
                right=right
            )
        except ConnectionError as e:
            self._logger.error(f"Connection error in set_differential_speed: {e}")
            return CommandAck(success=False, message=str(e))

    async def get_telemetry(self) -> TelemetryData:
        """
        Получение телеметрии робота

        :return: Данные телеметрии робота
        :rtype: Telemetry
        """
        try:
            return await self._communicator.get_telemetry()
        except ConnectionError as e:
            self._logger.error(f"Connection error in get_telemetry: {e}")
            return TelemetryData()

    async def get_battery_status(self) -> BatteryData:
        """
        Получение заряда батареи

        :return: Заряд батареи в процентах
        :rtype: int
        """
        try:
            return await self._communicator.get_battery_status()
        except ConnectionError as e:
            self._logger.error(f"Connection error in get_battery_status: {e}")
            return BatteryData()

    async def moo(self):
        """
        Воспроизведение звука "мычания"
        """
        try:
            await self._communicator.send_command(RoverCommands.MOO)
        except ConnectionError as e:
            self._logger.error(f"Connection error in moo: {e}")

    async def beep(self):
        """
        Воспроизведение звукового сигнала
        """
        try:
            await self._communicator.send_command(RoverCommands.BEEP)
        except ConnectionError as e:
            self._logger.error(f"Connection error in beep: {e}")

    async def goto(self, x: float, y: float, yaw: float | None=None):
        """
        Перемещение робота в заданную точку.

        :param x: Координата X целевой точки
        :type x: float
        :param y: Координата Y целевой точки
        :type y: float
        :param yaw: Угол поворота в целевой точке (радианы)
        :type yaw: float | None
        """
        try:
            await self._communicator.goto(x=x, y=y, yaw=yaw)
        except ConnectionError as e:
            self._logger.error(f"Connection error in goto: {e}")
            pass

    async def stop(self) -> CommandAck:
        """
        Остановка
        """
        try:
            return await self._communicator.send_command(RoverCommands.STOP)
        except ConnectionError as e:
            self._logger.error(f"Connection error in stop: {e}")
            return CommandAck(success=False, message=str(e))

    async def emergency_stop(self) -> CommandAck:
        """
        Аварийная остановка
        """
        try:
            return await self._communicator.send_command(RoverCommands.EMERGENCY_STOP)
        except ConnectionError as e:
            self._logger.error(f"Connection error in emergency_stop: {e}")
            return CommandAck(success=False, message=str(e))

    async def stream_telemetry(self) -> AsyncGenerator[TelemetryData, None]:
        """
        Потоковое получение телеметрии

        Генератор, возвращающий данные телеметрии в реальном времени.

        :return: Асинхронный генератор данных телеметрии
        :rtype: AsyncGenerator[TelemetryData, None]

        :Example:
            .. code-block:: python

                async for telemetry in rover.stream_telemetry():
                    print(f"Position: {telemetry.position.x}, {telemetry.position.y}")
        """
        empty_telemetry = TelemetryData()
        try:
            async for data in self._communicator.stream_telemetry():
                yield data
        except ConnectionError as e:
            self._logger.error(f"Connection error in stream_telemetry: {e}")
            yield empty_telemetry

    @asynccontextmanager
    async def rc_stream_context(self):
        """
        Контекстный менеджер для потоковой передачи RC-каналов.

        Автоматически запускает и останавливает RC-поток, обеспечивая
        безопасное управление роботом.

        Example:
            .. code-block:: python
                async with rover.rc_stream_context() as rc_control:
                    # Устанавливаем каналы управления
                    rc_control.channel1 = 2000  # Вперед
                    rc_control.channel2 = 1000  # Поворот влево
                    await asyncio.sleep(2.0)

                    # Меняем команду
                    rc_control.channel1 = 1500  # Стоп
        """
        self._logger.info("entering rc_stream_context")
        try:
            await self.start_rc_stream()

            yield self.rc_channels

        finally:
            self._logger.debug("exiting rc_stream_context")
            await self.stop_rc_stream()

    async def start_rc_stream(self):
        """
        Запустить потоковую передачу RC-команд.

        Используется для непрерывного управления ровером через RC-каналы.

        :raises RuntimeError: При ошибках запуска потока
        """
        if self._is_rc_streaming:
            return

        self._is_rc_streaming = True
        self._rc_stream_task = asyncio.create_task(self._rc_stream_loop())

    async def stop_rc_stream(self):
        """
        Остановка потока RC команд
        """
        self._reset_rc_channels()
        self._is_rc_streaming = False
        if self._rc_stream_task:
            self._rc_stream_task.cancel()
            try:
                await self._rc_stream_task
            except asyncio.CancelledError:
                pass
            finally:
                self._rc_stream_task = None

    def _reset_rc_channels(self):
        """
        Сбрасывает RC каналы в нейтральное положение
        """
        self.rc_channels.channel1 = 1500
        self.rc_channels.channel2 = 1500
        self.rc_channels.channel3 = 1500
        self.rc_channels.channel4 = 1500

    async def _rc_stream_loop(self):
        """
        Цикл потоковой передачи RC-команд
        """
        while self._is_rc_streaming:
            while self._is_rc_streaming and not self._communicator.is_connected:
                await asyncio.sleep(0.5)

            if not self._is_rc_streaming:
                break

            try:
                async def generate_commands():
                    while self._is_rc_streaming and self._communicator.is_connected:
                        yield self.rc_channels
                        await asyncio.sleep(self._rc_stream_interval)

                async for ack in self._communicator.rc_stream(generate_commands()):
                    if not ack.success:
                        #self._logger.warning(f"RC command failed: {ack.message}")
                        pass

            except ConnectionError:
                await asyncio.sleep(1.0)
            except Exception as e:
                self._logger.error(f"RC stream error: {e}")
                await asyncio.sleep(1.0)

    async def __aenter__(self):
        await self.connect()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.disconnect()