"""Service for uploading files to PX4 SD card via FTP."""

import json
from typing import AsyncGenerator, Optional

from mavsdk import System

try:
    from grpc import RpcError
except ImportError:
    # Fallback if grpc is not directly available (shouldn't happen with mavsdk)
    RpcError = Exception


async def upload_to_sdcard(
    file_path: str,
    port: str,
    baudrate: int,
    remote_path: str = "/fs/microsd/etc",
) -> AsyncGenerator[str, None]:
    """Upload a file to the PX4 SD card via MAVSDK FTP and stream progress."""

    drone: Optional[System] = None
    try:
        drone = System()
        serial = f"serial://{port}:{baudrate}"

        yield "data: {}\n\n".format(
            json.dumps({"type": "status", "message": f"Connecting to {port}..."})
        )
        await drone.connect(system_address=serial)

        # Wait for connection to be established
        async for state in drone.core.connection_state():
            if state.is_connected:
                break

        yield "data: {}\n\n".format(
            json.dumps({"type": "status", "message": "Connected to device"})
        )

        yield "data: {}\n\n".format(
            json.dumps(
                {
                    "type": "status",
                    "message": f"Listing directory {remote_path}...",
                }
            )
        )
        try:
            directory_list = await drone.ftp.list_directory(remote_path)
            yield "data: {}\n\n".format(
                json.dumps(
                    {
                        "type": "status",
                        "message": f"Directory listing: {directory_list}",
                    }
                )
            )
        except Exception as exc:  # pragma: no cover
            yield "data: {}\n\n".format(
                json.dumps(
                    {
                        "type": "warning",
                        "message": f"Could not list directory: {exc}",
                    }
                )
            )

        yield "data: {}\n\n".format(
            json.dumps(
                {
                    "type": "status",
                    "message": f"Uploading {file_path} to {remote_path}...",
                }
            )
        )
        async for progress_data in drone.ftp.upload(file_path, remote_path):
            yield "data: {}\n\n".format(
                json.dumps({"type": "progress", "data": str(progress_data)})
            )

        yield "data: {}\n\n".format(
            json.dumps({"type": "success", "message": "File uploaded successfully"})
        )

    except (RpcError, Exception) as exc:  # pragma: no cover - streaming generator
        error_msg = str(exc)
        if isinstance(exc, RpcError):
            if "Connection reset by peer" in error_msg or "UNAVAILABLE" in error_msg:
                error_msg = (
                    "Connection to PX4 device was lost. "
                    "This may occur if the device disconnected, rebooted, or the connection timed out."
                )
        yield "data: {}\n\n".format(
            json.dumps(
                {
                    "type": "error",
                    "message": f"SD card upload failed: {error_msg}",
                }
            )
        )
        raise
    finally:
        # Cleanup: Ensure the System object is properly released
        if drone is not None:
            drone = None

