"""rtkbase-compatible CLI adapter for LC29H GNSS modules.

Provides a `lc29h rtkbase` subcommand group that exposes the detection
and configuration interface rtkbase's install.sh expects. All output is
machine-parseable: plain text to stdout, progress/errors to stderr,
exit codes for success (0) and failure (1).

This mirrors how rtkbase integrates with Unicore (unicore_tool.py),
Septentrio (sept_tool.py), and U-Blox (ubxtool).
"""

import asyncio
import sys

import click

from .commands import (
    pqtm_cfg_msg_rate,
    pqtm_cfg_nmea_msg,
    pqtm_cfg_port,
    pqtm_restore_params,
    pqtm_save_params,
    pqtm_version,
)
from .detect import detect_receiver
from .parser import parse_pqtm_verno
from .protocol import parse_response, validate_checksum
from .transport import SerialTransport

# RTCM3 messages to enable for base station operation.
# Same set as Unicore UM980/UM982 config in rtkbase.
RTCM3_MESSAGES = [
    "RTCM3-1005",  # Station coordinates (ARP)
    "RTCM3-1019",  # GPS ephemeris
    "RTCM3-1020",  # GLONASS ephemeris
    "RTCM3-1042",  # BeiDou ephemeris
    "RTCM3-1045",  # Galileo F/NAV ephemeris
    "RTCM3-1046",  # Galileo I/NAV ephemeris
    "RTCM3-1077",  # GPS MSM7 observations
    "RTCM3-1087",  # GLONASS MSM7 observations
    "RTCM3-1097",  # Galileo MSM7 observations
    "RTCM3-1127",  # BeiDou MSM7 observations
]


def _run(coro):
    """Run an async coroutine from sync Click context."""
    return asyncio.run(coro)


def _err(msg: str):
    """Print message to stderr."""
    click.echo(msg, err=True)


@click.group(name="rtkbase")
@click.option("--port", default=None, envvar="LC29H_PORT", help="Serial port path.")
@click.option(
    "--baud", "--baudrate", default=None, type=int,
    envvar="LC29H_BAUD", help="Serial baud rate.",
)
@click.pass_context
def rtkbase_group(ctx, port: str | None, baud: int | None):
    """rtkbase integration commands for GNSS base station setup."""
    # Override parent context if options given directly to rtkbase subgroup
    if port is not None:
        ctx.obj["port"] = port
    if baud is not None:
        ctx.obj["baud"] = baud


@rtkbase_group.command()
@click.pass_context
def detect(ctx):
    """Detect LC29H module on port.

    Output format: /dev/PORT - quectel - BAUD - FIRMWARE - MODEL
    """
    port = ctx.obj["port"]
    baud = ctx.obj["baud"]

    async def _detect():
        result = await detect_receiver(port, bauds=[baud] if baud != 115200 else None)
        if result:
            click.echo(
                f"{result.port} - quectel - {result.baud} - "
                f"{result.firmware} - {result.model}"
            )
            return True
        return False

    if not _run(_detect()):
        sys.exit(1)


@rtkbase_group.command(name="get-model")
@click.pass_context
def get_model(ctx):
    """Print the receiver model to stdout (e.g. LC29HBS)."""
    port = ctx.obj["port"]
    baud = ctx.obj["baud"]

    async def _get_model():
        transport = SerialTransport(port, baud)
        await transport.connect()
        try:
            line = await transport.query(pqtm_version(), "$PQTMVERNO", timeout=3.0)
            if not line or not validate_checksum(line):
                return None
            _, fields = parse_response(line)
            info = parse_pqtm_verno(fields)
            return info.model if info.model else None
        finally:
            await transport.disconnect()

    model = _run(_get_model())
    if model:
        click.echo(model)
    else:
        sys.exit(1)


@rtkbase_group.command(name="get-firmware")
@click.pass_context
def get_firmware(ctx):
    """Print the firmware version to stdout."""
    port = ctx.obj["port"]
    baud = ctx.obj["baud"]

    async def _get_firmware():
        transport = SerialTransport(port, baud)
        await transport.connect()
        try:
            line = await transport.query(pqtm_version(), "$PQTMVERNO", timeout=3.0)
            if not line or not validate_checksum(line):
                return None
            _, fields = parse_response(line)
            info = parse_pqtm_verno(fields)
            return info.firmware_version if info.firmware_version else None
        finally:
            await transport.disconnect()

    firmware = _run(_get_firmware())
    if firmware:
        click.echo(firmware)
    else:
        sys.exit(1)


@rtkbase_group.command()
@click.pass_context
def reset(ctx):
    """Restore factory default configuration."""
    port = ctx.obj["port"]
    baud = ctx.obj["baud"]

    async def _reset():
        transport = SerialTransport(port, baud)
        await transport.connect()
        try:
            await transport.write(pqtm_restore_params())
            _err("Factory defaults restored")
            return True
        finally:
            await transport.disconnect()

    if not _run(_reset()):
        sys.exit(1)


@rtkbase_group.command()
@click.pass_context
def configure(ctx):
    """Configure LC29H-BS as RTCM3 base station.

    Applies full base station configuration:
    1. Set UART1 to 115200 baud with NMEA+RTCM3 output
    2. Enable RTCM3 messages (1005, 1019-1127)
    3. Enable GGA output for position reporting
    4. Persist configuration to flash
    """
    port = ctx.obj["port"]
    baud = ctx.obj["baud"]

    async def _configure():
        transport = SerialTransport(port, baud)
        await transport.connect()
        try:
            # Step 1: Configure UART1 for RTCM3 + NMEA output at 115200
            _err("Configuring UART1: 115200 baud, NMEA+RTCM3 output...")
            await transport.write(
                pqtm_cfg_port(
                    port="UART1",
                    baud=115200,
                    input_protos="NMEA|RTCM3",
                    output_protos="NMEA|RTCM3",
                )
            )
            await asyncio.sleep(0.2)

            # Step 2: Enable RTCM3 messages at 1Hz
            for msg in RTCM3_MESSAGES:
                _err(f"Enabling {msg}...")
                await transport.write(pqtm_cfg_msg_rate(msg, rate=1, port=1))
                await asyncio.sleep(0.1)

            # Step 3: Enable GGA output on UART1 (needed for base position)
            _err("Enabling GGA output...")
            await transport.write(pqtm_cfg_nmea_msg("GGA", uart1=1, uart2=0))
            await asyncio.sleep(0.1)

            # Step 4: Save to flash
            _err("Saving configuration to flash...")
            await transport.write(pqtm_save_params())
            await asyncio.sleep(0.5)

            _err("LC29H-BS base station configuration complete")
            return True
        finally:
            await transport.disconnect()

    if not _run(_configure()):
        sys.exit(1)
