"""voice_command_node.py — Voice command interpreter for SaltyBot (Issue #409).

Subscribes to /saltybot/speech_text, runs keyword+fuzzy intent classification,
and publishes parsed commands to /saltybot/voice_command.

ROS2 topics
-----------
Subscribe: /saltybot/speech_text              (std_msgs/String)
Publish:   /saltybot/voice_command            (saltybot_social_msgs/VoiceCommand)

Parameters
----------
min_confidence          float   0.70    Intent confidence threshold for execution
announce_intent         bool    true    Log intent at INFO level
"""

from __future__ import annotations

import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, QoSReliabilityPolicy

from std_msgs.msg import String
from saltybot_social_msgs.msg import VoiceCommand

from .voice_command_parser import parse


class VoiceCommandNode(Node):
    """Voice command interpreter node."""

    def __init__(self) -> None:
        super().__init__("voice_command_node")

        # ── Parameters ──────────────────────────────────────────────────────
        self.declare_parameter("min_confidence", 0.70)
        self.declare_parameter("announce_intent", True)

        self._min_conf: float = self.get_parameter("min_confidence").value
        self._announce: bool = self.get_parameter("announce_intent").value

        # ── Reliable QoS — voice commands must not be dropped ───────────────
        qos = QoSProfile(depth=10)
        qos.reliability = QoSReliabilityPolicy.RELIABLE

        self._cmd_pub = self.create_publisher(
            VoiceCommand,
            "/saltybot/voice_command",
            qos,
        )

        self._speech_sub = self.create_subscription(
            String,
            "/saltybot/speech_text",
            self._on_speech_text,
            qos,
        )

        self.get_logger().info(
            f"voice_command_node ready (min_confidence={self._min_conf})"
        )

    def _on_speech_text(self, msg: String) -> None:
        """Process incoming speech text."""
        text = msg.data.strip()
        if not text:
            return

        parsed = parse(text)

        if self._announce:
            self.get_logger().info(
                f"[VoiceCmd] '{text}' → {parsed.intent} (conf={parsed.confidence:.2f})"
            )

        # Filter by confidence threshold
        if parsed.confidence < self._min_conf and parsed.intent != "fallback":
            self.get_logger().debug(
                f"Confidence {parsed.confidence:.2f} below threshold {self._min_conf}"
            )
            return

        # Publish the command
        cmd_msg = VoiceCommand()
        cmd_msg.header.stamp = self.get_clock().now().to_msg()
        cmd_msg.intent = parsed.intent
        cmd_msg.raw_text = parsed.raw_text
        cmd_msg.speaker_id = "voice_command"
        cmd_msg.confidence = float(parsed.confidence)
        cmd_msg.requires_confirmation = False

        self._cmd_pub.publish(cmd_msg)


def main(args=None) -> None:
    rclpy.init(args=args)
    node = VoiceCommandNode()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == "__main__":
    main()
