#!/usr/bin/env python3
"""
tts_personality_node.py — TTS personality engine with context-aware greetings (Issue #494)

Provides personality-driven text-to-speech with:
  - Context-aware greetings (time of day, person names, emotion state)
  - Priority queue management (safety > social > idle chatter)
  - Rate/pitch modulation based on emotion
  - Integration with emotion engine (Issue #429)
  - Configurable personality parameters

Subscribes
──────────
  /saltybot/tts_request      std_msgs/String  — natural language text
  /saltybot/emotion_state    std_msgs/String  — emotion ("happy", "sad", "angry", "neutral")
  /saltybot/person_detected  std_msgs/String  — known person name

Publishers
──────────
  /saltybot/tts_command      std_msgs/String  — formatted TTS command with personality
  /saltybot/personality_state  std_msgs/String — current personality state

Parameters
──────────
  personality_name           str    "Luna"        — robot's name/personality
  enable_context_aware       bool   True          — use time/person/emotion context
  speed_happy                float  1.1           — faster speech when happy
  speed_sad                  float  0.9           — slower speech when sad
  pitch_happy                float  1.15          — higher pitch when happy
  pitch_sad                  float  0.85          — lower pitch when sad
  pitch_neutral              float  1.0
  greetings_morning          list   []            — greetings for 5am-12pm
  greetings_afternoon        list   []            — greetings for 12pm-5pm
  greetings_evening          list   []            — greetings for 5pm-9pm
  greetings_night            list   []            — greetings for 9pm-5am
  known_people               dict   {}            — person names for personalization
"""

from __future__ import annotations

import queue
import threading
import time
from datetime import datetime
from typing import Optional, Dict, List
import random

import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile
from std_msgs.msg import String


class PersonalityContext:
    """Context for personality-aware responses."""
    def __init__(self):
        self.current_emotion = "neutral"
        self.last_person = None
        self.emotion_timestamp = time.time()
        self.person_timestamp = time.time()
        self.interaction_count = 0

    def update_emotion(self, emotion: str) -> None:
        """Update current emotion state."""
        if emotion != self.current_emotion:
            self.current_emotion = emotion
            self.emotion_timestamp = time.time()

    def update_person(self, person_name: str) -> None:
        """Update last detected person."""
        if person_name != self.last_person:
            self.last_person = person_name
            self.person_timestamp = time.time()

    def get_time_period(self) -> str:
        """Return time period: morning, afternoon, evening, or night."""
        hour = datetime.now().hour
        if 5 <= hour < 12:
            return "morning"
        elif 12 <= hour < 17:
            return "afternoon"
        elif 17 <= hour < 21:
            return "evening"
        else:
            return "night"


class TtsPersonalityNode(Node):
    """TTS personality engine with context-aware responses."""

    def __init__(self):
        super().__init__("tts_personality")

        # Personality parameters
        self.declare_parameter("personality_name", "Luna")
        self.declare_parameter("enable_context_aware", True)

        # Emotion-based rate/pitch modulation
        self.declare_parameter("speed_happy", 1.1)
        self.declare_parameter("speed_sad", 0.9)
        self.declare_parameter("speed_neutral", 1.0)
        self.declare_parameter("pitch_happy", 1.15)
        self.declare_parameter("pitch_sad", 0.85)
        self.declare_parameter("pitch_neutral", 1.0)

        # Greeting templates (by time of day)
        self.declare_parameter("greetings_morning", [
            "Good morning! I hope you slept well.",
            "Rise and shine! Ready for a great day?",
            "Morning! Let's get moving.",
        ])
        self.declare_parameter("greetings_afternoon", [
            "Afternoon! How's it going?",
            "Hey there! Hope your day is great so far.",
            "Afternoon check-in! Everything okay?",
        ])
        self.declare_parameter("greetings_evening", [
            "Good evening! Time to wind down?",
            "Evening! Getting ready for tomorrow?",
            "Hi! How was your day?",
        ])
        self.declare_parameter("greetings_night", [
            "Burning the midnight oil?",
            "Still awake? Sweet dreams coming soon?",
            "Night owl mode activated!",
        ])

        # Known people for personalization
        self.declare_parameter("known_people", {})

        # Load parameters
        self._personality_name = self.get_parameter("personality_name").value
        self._context_aware = self.get_parameter("enable_context_aware").value
        self._speed_happy = self.get_parameter("speed_happy").value
        self._speed_sad = self.get_parameter("speed_sad").value
        self._speed_neutral = self.get_parameter("speed_neutral").value
        self._pitch_happy = self.get_parameter("pitch_happy").value
        self._pitch_sad = self.get_parameter("pitch_sad").value
        self._pitch_neutral = self.get_parameter("pitch_neutral").value
        self._greetings_morning = self.get_parameter("greetings_morning").value
        self._greetings_afternoon = self.get_parameter("greetings_afternoon").value
        self._greetings_evening = self.get_parameter("greetings_evening").value
        self._greetings_night = self.get_parameter("greetings_night").value
        self._known_people = self.get_parameter("known_people").value

        # Personality context
        self._context = PersonalityContext()
        self._context_lock = threading.Lock()

        # Priority levels for queue
        self.PRIORITY_SAFETY = 3      # Emergency/safety messages
        self.PRIORITY_SOCIAL = 2      # Social interactions, greetings
        self.PRIORITY_IDLE = 1        # Idle chatter, commentary
        self.PRIORITY_NORMAL = 0      # Default

        # Queue for TTS requests with priority
        self._tts_queue = queue.PriorityQueue(maxsize=16)
        self._state = "idle"
        self._state_lock = threading.Lock()

        # QoS profile
        qos = QoSProfile(depth=5)

        # Subscribers
        self.create_subscription(
            String, "/saltybot/tts_request",
            self._on_tts_request, qos)

        self.create_subscription(
            String, "/saltybot/emotion_state",
            self._on_emotion_update, qos)

        self.create_subscription(
            String, "/saltybot/person_detected",
            self._on_person_detected, qos)

        # Publishers
        self._tts_command_pub = self.create_publisher(
            String, "/saltybot/tts_command", qos)

        self._personality_state_pub = self.create_publisher(
            String, "/saltybot/personality_state", qos)

        # Worker thread for processing queue
        self._worker_thread = threading.Thread(
            target=self._worker_loop, daemon=True)
        self._worker_thread.start()

        self.get_logger().info(
            f"TTS Personality Engine '{self._personality_name}' started. "
            f"Context-aware: {self._context_aware}")

    def _on_tts_request(self, msg: String) -> None:
        """Handle incoming TTS request."""
        try:
            priority = self.PRIORITY_NORMAL
            text = msg.data

            # Determine priority based on keywords
            if any(w in text.lower() for w in ["safety", "stop", "danger", "emergency"]):
                priority = self.PRIORITY_SAFETY
            elif any(w in text.lower() for w in ["hello", "hey", "hi", "greeting"]):
                priority = self.PRIORITY_SOCIAL

            # Add to queue with timestamp for FIFO within priority
            item = (priority, time.time(), text)
            self._tts_queue.put(item, block=False)

        except queue.Full:
            self.get_logger().warn("TTS queue full, dropping request")

    def _on_emotion_update(self, msg: String) -> None:
        """Handle emotion state updates."""
        with self._context_lock:
            self._context.update_emotion(msg.data)
        self.get_logger().debug(f"Emotion updated: {msg.data}")

    def _on_person_detected(self, msg: String) -> None:
        """Handle person detection events."""
        with self._context_lock:
            self._context.update_person(msg.data)
        self.get_logger().debug(f"Person detected: {msg.data}")

    def _apply_personality(self, text: str) -> str:
        """
        Apply personality modifications to text.
        Returns: modified text with personality context.
        """
        with self._context_lock:
            emotion = self._context.current_emotion
            person = self._context.last_person
            time_period = self._context.get_time_period()

        # Don't modify safety-critical messages
        if any(w in text.lower() for w in ["stop", "danger", "emergency"]):
            return text

        # Add personalization based on context
        modified_text = text
        if self._context_aware:
            # Add person name if recently detected
            if person and (time.time() - self._context.person_timestamp < 30):
                modified_text = f"{person}, {modified_text}"

        return modified_text

    def _get_emotion_modulation(self) -> tuple[float, float]:
        """
        Get speed and pitch multipliers based on emotion.
        Returns: (speed_multiplier, pitch_multiplier)
        """
        with self._context_lock:
            emotion = self._context.current_emotion

        if emotion == "happy":
            return (self._speed_happy, self._pitch_happy)
        elif emotion == "sad":
            return (self._speed_sad, self._pitch_sad)
        else:  # neutral
            return (self._speed_neutral, self._pitch_neutral)

    def _format_tts_command(self, text: str) -> str:
        """
        Format text with personality parameters for TTS service.
        Returns: JSON-formatted TTS command with rate/pitch modulation.
        """
        speed, pitch = self._get_emotion_modulation()

        # Format as JSON-compatible string for tts_service
        # tts_service will parse: {"text": "...", "speed": 1.1, "pitch": 1.15}
        import json
        command = {
            "text": text,
            "speed": float(speed),
            "pitch": float(pitch),
            "personality": self._personality_name,
            "emotion": self._context.current_emotion,
        }
        return json.dumps(command)

    def _update_state(self, new_state: str) -> None:
        """Update and publish personality state."""
        with self._state_lock:
            if new_state != self._state:
                self._state = new_state
                state_msg = String(data=f"{new_state}:{self._personality_name}:{self._context.current_emotion}")
                self._personality_state_pub.publish(state_msg)

    def _worker_loop(self) -> None:
        """Worker thread loop to process TTS queue."""
        while rclpy.ok():
            try:
                # Get next item from queue (blocking, 1s timeout)
                priority, timestamp, text = self._tts_queue.get(timeout=1.0)

                self._update_state("processing")

                # Apply personality modifications
                personalized_text = self._apply_personality(text)

                # Format with emotion modulation
                tts_command = self._format_tts_command(personalized_text)

                # Publish to TTS service
                cmd_msg = String(data=tts_command)
                self._tts_command_pub.publish(cmd_msg)

                self.get_logger().info(
                    f"[{priority}] Speaking: {personalized_text[:60]}...")

                # Small delay to avoid queue hammering
                time.sleep(0.1)
                self._update_state("idle")

            except queue.Empty:
                self._update_state("idle")
            except Exception as e:
                self.get_logger().error(f"Worker error: {e}")

    def _generate_greeting(self) -> str:
        """Generate context-aware greeting."""
        with self._context_lock:
            time_period = self._context.get_time_period()

        greetings_map = {
            "morning": self._greetings_morning,
            "afternoon": self._greetings_afternoon,
            "evening": self._greetings_evening,
            "night": self._greetings_night,
        }

        greetings = greetings_map.get(time_period, self._greetings_afternoon)
        if greetings:
            return random.choice(greetings)
        return "Hello!"


def main(args=None):
    """Main entry point."""
    rclpy.init(args=args)
    node = TtsPersonalityNode()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == "__main__":
    main()
