"""Validate control.command payloads before publishing."""

from __future__ import annotations

from typing import Any

HEAT_MODES = frozenset({"sleep", "maintain", "backflush"})
FILTER_MODES = frozenset({"on", "auto", "off"})
CONTROL_OPS = frozenset(
    {
        "set_heat_mode",
        "set_target_temp",
        "target_temp_increment",
        "target_temp_decrement",
        "set_filter_mode",
    }
)
TEMP_SETUP_OPS = frozenset({"setup_start", "setup_finish", "setup_cancel"})

TARGET_F_MIN = 50.0
TARGET_F_MAX = 110.0


class CommandValidationError(ValueError):
    pass


def _clamp_target(target: float) -> float:
    return max(TARGET_F_MIN, min(TARGET_F_MAX, target))


def validate_control_command(body: dict[str, Any]) -> tuple[str, dict[str, Any]]:
    op = body.get("op")
    if not op or not isinstance(op, str):
        raise CommandValidationError("missing or invalid op")
    if op not in CONTROL_OPS:
        raise CommandValidationError(f"unknown op: {op!r}")

    args = body.get("args")
    if args is not None and not isinstance(args, dict):
        raise CommandValidationError("args must be an object")
    args = dict(args or {})

    if op == "set_heat_mode":
        mode = args.get("mode")
        if mode not in HEAT_MODES:
            raise CommandValidationError(f"invalid heat mode: {mode!r}")
        return op, {"mode": mode}

    if op == "set_filter_mode":
        mode = args.get("mode")
        if mode not in FILTER_MODES:
            raise CommandValidationError(f"invalid filter mode: {mode!r}")
        return op, {"mode": mode}

    if op == "set_target_temp":
        raw = args.get("target_f")
        if raw is None:
            raise CommandValidationError("target_f required")
        return op, {"target_f": _clamp_target(float(raw))}

    if op == "target_temp_increment":
        delta = float(args.get("delta_f", 1))
        return op, {"delta_f": delta}

    if op == "target_temp_decrement":
        delta = float(args.get("delta_f", 1))
        return op, {"delta_f": delta}

    raise CommandValidationError(f"unknown op: {op!r}")


def validate_temp_setup_op(op: str) -> str:
    if op not in TEMP_SETUP_OPS:
        raise CommandValidationError(f"unknown setup op: {op!r}")
    return op
