"""Service for PX4 shell connection management."""

import asyncio
from typing import Optional

from mavsdk import System


class ShellConnection:
    """Manage a persistent MAVSDK shell connection with queued output."""

    def __init__(self, port: str, baudrate: int) -> None:
        self.port = port
        self.baudrate = baudrate
        self.drone: Optional[System] = None
        self.output_queue: "asyncio.Queue[str]" = asyncio.Queue()
        self.connected = False

    async def connect(self) -> None:
        """Establish connection to PX4 and start shell monitoring."""

        self.drone = System()
        serial = f"serial://{self.port}:{self.baudrate}"
        await self.drone.connect(system_address=serial)

        async for state in self.drone.core.connection_state():
            if state.is_connected:
                self.connected = True
                break

        asyncio.create_task(self._observe_shell())

    async def _observe_shell(self) -> None:
        """Monitor shell output and place data into the queue."""

        if not self.drone:
            return

        try:
            async for output in self.drone.shell.receive():
                await self.output_queue.put(output)
        except Exception as exc:  # pragma: no cover - streaming loop
            await self.output_queue.put(f"Error receiving shell output: {exc}")

    async def send_command(self, command: str) -> None:
        """Send a command to the PX4 shell."""

        if not self.connected or not self.drone:
            raise RuntimeError("Not connected to PX4")
        await self.drone.shell.send(command)

    async def receive_output(self) -> Optional[str]:
        """Receive shell output (non-blocking)."""

        try:
            return await asyncio.wait_for(self.output_queue.get(), timeout=0.1)
        except asyncio.TimeoutError:
            return None

    async def disconnect(self) -> None:
        """Close the connection if required."""

        self.connected = False
        # MAVSDK handles cleanup on garbage collection; nothing else required here.

