"""Signal measurement tools — RSSI, DVB config, LNA, and AZ sweep."""

from fastmcp import Context

from mcbirdcage.state import BirdcageState


def _require_device(state: BirdcageState):
    if state.device is None:
        raise RuntimeError("No device connected. Use the 'connect' tool first.")


def register(mcp):
    """Register signal tools on the FastMCP instance."""

    @mcp.tool()
    async def get_rssi(ctx: Context, iterations: int = 10) -> dict:
        """Read averaged RSSI signal strength from the BCM4515 DVB tuner.

        Returns {"reads": int, "average": int, "current": int}.
        Noise floor is approximately 500 ADC counts.

        Args:
            iterations: Number of samples to average (default 10).
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        return state.device.get_rssi(iterations)

    @mcp.tool()
    async def get_adc_rssi(ctx: Context) -> dict:
        """Read single-shot ADC RSSI value (raw, unbounded).

        Returns the raw ADC count from the hardware ADC subsystem,
        bypassing the DVB tuner's averaging. Useful for quick signal checks.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        raw = state.device.get_adc_rssi()
        return {"raw": raw}

    @mcp.tool()
    async def get_lock_status(ctx: Context) -> dict:
        """Check DVB signal lock status (single-shot).

        Returns the raw firmware response string containing lock state,
        RSSI, and glitch count.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        raw = state.device.get_lock_status()
        return {"raw": raw}

    @mcp.tool()
    async def enable_lna(ctx: Context) -> dict:
        """Enable the LNA by setting LNB voltage to 13V (V-pol).

        Shortcut for set_lnb_voltage(mode='odu'). Use set_lnb_voltage
        to switch back to 18V (H-pol) when done.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        state.device.enable_lna()
        return {"status": "lna_enabled", "voltage": "13V", "polarity": "V"}

    @mcp.tool()
    async def set_lnb_voltage(ctx: Context, mode: str) -> dict:
        """Set LNB DC voltage and polarization.

        Controls the LNB bias voltage on the coax feed line. This
        determines both LNA power state and receive polarization:

        - 'odu' = 13V (V-pol, LNA enabled) — for radio astronomy / ham
        - 'stb' = 18V (H-pol, boot default) — standard consumer mode

        V-pol and H-pol see different transponders. The PEAK submenu's
        'rssits' command alternates between them for comparison.

        Args:
            mode: 'odu' for 13V/V-pol or 'stb' for 18V/H-pol.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        raw = state.device.set_lnb_voltage(mode)
        voltage = "13V" if mode.lower() == "odu" else "18V"
        polarity = "V" if mode.lower() == "odu" else "H"
        return {
            "status": "set",
            "mode": mode.lower(),
            "voltage": voltage,
            "polarity": polarity,
            "raw": raw,
        }

    @mcp.tool()
    async def get_dvb_config(ctx: Context) -> dict:
        """Read BCM4515 DVB tuner hardware and firmware version.

        Returns chip ID, revision, firmware version, and strap config.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        raw = state.device.get_dvb_config()
        return {"raw": raw}

    @mcp.tool()
    async def get_channel_params(ctx: Context) -> dict:
        """Read current DVB channel parameters.

        Returns frequency, symbol rate, modulation, LNB polarity,
        tone settings, and scan configuration.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        raw = state.device.get_channel_params()
        return {"raw": raw}

    @mcp.tool()
    async def az_sweep(
        ctx: Context,
        start_az: float,
        span: float,
        step_cdeg: int = 100,
        num_xponders: int = 1,
        timeout: float = 120.0,
    ) -> dict:
        """Execute a firmware-accelerated azimuth sweep (azscanwxp).

        Moves to start_az, then sweeps span degrees while measuring
        RSSI/Lock/SNR at each step. The firmware handles motor movement
        and measurement atomically — no per-point serial round-trips.

        Returns a list of measurement points, each containing:
        az (degrees), rssi (ADC counts), lock (0/1), snr (dB).

        SAFETY: Requires homed motors. Do NOT run on uncalibrated axes.

        Args:
            start_az: Starting azimuth in degrees.
            span: Total sweep width in degrees.
            step_cdeg: Step size in centidegrees (100 = 1.00 deg).
            num_xponders: Transponders to cycle per position.
            timeout: Max wait time for the sweep to complete.
        """
        state: BirdcageState = ctx.request_context.lifespan_context
        _require_device(state)
        points = state.device.az_sweep_firmware(
            start_az, span, step_cdeg, num_xponders, timeout
        )
        return {"points": points, "count": len(points)}
