"""Baud rate auto-detection for Quectel LC29H GNSS modules.

Probes a serial port at candidate baud rates by sending $PQTMVERNO
and checking for a valid NMEA response. Returns detected parameters
(port, baud, model, firmware) or None.
"""

import asyncio
import logging
import sys
from dataclasses import dataclass

from .commands import pqtm_version
from .parser import parse_pqtm_verno
from .protocol import parse_response, validate_checksum
from .transport import SerialTransport

logger = logging.getLogger(__name__)

# Baud rates to probe, ordered by likelihood in rtkbase deployments.
# 115200 is standard for configured receivers; 9600 is factory default.
CANDIDATE_BAUDS = [115200, 9600, 57600, 38400, 19200]


@dataclass
class DetectionResult:
    """Result from a successful receiver detection."""

    port: str
    baud: int
    model: str
    firmware: str


async def _probe_baud(port: str, baud: int, timeout: float = 2.0) -> DetectionResult | None:
    """Try to detect an LC29H module at a specific baud rate.

    Sends PQTMVERNO and waits for a response with a valid checksum.
    Returns DetectionResult if an LC29H is found, else None.
    """
    transport = SerialTransport(port, baud)
    try:
        await transport.connect()
    except Exception:
        logger.debug("Cannot open %s at %d", port, baud)
        return None

    try:
        line = await transport.query(pqtm_version(), "$PQTMVERNO", timeout=timeout)
        if not line:
            return None

        if not validate_checksum(line):
            return None

        _, fields = parse_response(line)
        info = parse_pqtm_verno(fields)

        if not info.model or not info.model.startswith("LC29H"):
            return None

        return DetectionResult(
            port=port,
            baud=baud,
            model=info.model,
            firmware=info.firmware_version,
        )
    except (ValueError, TimeoutError, OSError):
        return None
    finally:
        await transport.disconnect()


async def detect_receiver(
    port: str,
    bauds: list[int] | None = None,
    timeout: float = 2.0,
) -> DetectionResult | None:
    """Auto-detect an LC29H module by probing candidate baud rates.

    Args:
        port: Serial port path (e.g. /dev/ttyUSB2).
        bauds: Baud rates to try. Defaults to CANDIDATE_BAUDS.
        timeout: Per-probe timeout in seconds.

    Returns:
        DetectionResult if an LC29H is found, else None.
    """
    for baud in (bauds or CANDIDATE_BAUDS):
        logger.debug("Probing %s at %d baud", port, baud)
        result = await _probe_baud(port, baud, timeout)
        if result:
            logger.info("Detected %s on %s at %d baud", result.model, port, baud)
            return result
    return None


def detect_receiver_sync(
    port: str,
    bauds: list[int] | None = None,
    timeout: float = 2.0,
) -> DetectionResult | None:
    """Synchronous wrapper around detect_receiver() for subprocess use."""
    return asyncio.run(detect_receiver(port, bauds, timeout))


if __name__ == "__main__":
    # Quick CLI for testing: python -m lc29h.detect /dev/ttyUSB2
    port_arg = sys.argv[1] if len(sys.argv) > 1 else "/dev/ttyS0"
    result = detect_receiver_sync(port_arg)
    if result:
        print(f"{result.port} {result.baud} {result.model} {result.firmware}")
    else:
        print(f"No LC29H detected on {port_arg}", file=sys.stderr)
        sys.exit(1)
