//! Emote Tool - LED expressions and sound effects
//!
//! Control LED matrix/strips for robot "expressions" and play sounds.
//! Makes the robot more engaging for kids!

use crate::config::RobotConfig;
use crate::traits::{Tool, ToolResult};
use anyhow::Result;
use async_trait::async_trait;
use serde_json::{Value, json};
use std::path::PathBuf;

/// Predefined LED expressions
#[derive(Debug, Clone, Copy)]
pub enum Expression {
    Happy,     // :)
    Sad,       // :(
    Surprised, // :O
    Thinking,  // :?
    Sleepy,    // -_-
    Excited,   // ^_^
    Love,      // <3 <3
    Angry,     // >:(
    Confused,  // @_@
    Wink,      // ;)
}

impl Expression {
    fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "happy" | "smile" => Some(Self::Happy),
            "sad" | "frown" => Some(Self::Sad),
            "surprised" | "wow" => Some(Self::Surprised),
            "thinking" | "hmm" => Some(Self::Thinking),
            "sleepy" | "tired" => Some(Self::Sleepy),
            "excited" | "yay" => Some(Self::Excited),
            "love" | "heart" => Some(Self::Love),
            "angry" | "mad" => Some(Self::Angry),
            "confused" | "huh" => Some(Self::Confused),
            "wink" => Some(Self::Wink),
            _ => None,
        }
    }

    /// Get LED matrix pattern (8x8 example)
    /// Returns array of 64 RGB values
    fn pattern(&self) -> Vec<(u8, u8, u8)> {
        let black = (0, 0, 0);
        let white = (255, 255, 255);
        let yellow = (255, 255, 0);
        let red = (255, 0, 0);
        let blue = (0, 100, 255);
        let pink = (255, 100, 150);

        // 8x8 patterns (simplified representations)
        match self {
            Self::Happy => {
                // Simple smiley
                vec![
                    black, black, yellow, yellow, yellow, yellow, black, black, black, yellow,
                    black, black, black, black, yellow, black, yellow, black, white, black, black,
                    white, black, yellow, yellow, black, black, black, black, black, black, yellow,
                    yellow, black, white, black, black, white, black, yellow, yellow, black, black,
                    white, white, black, black, yellow, black, yellow, black, black, black, black,
                    yellow, black, black, black, yellow, yellow, yellow, yellow, black, black,
                ]
            }
            Self::Sad => {
                vec![
                    black, black, blue, blue, blue, blue, black, black, black, blue, black, black,
                    black, black, blue, black, blue, black, white, black, black, white, black,
                    blue, blue, black, black, black, black, black, black, blue, blue, black, black,
                    white, white, black, black, blue, blue, black, white, black, black, white,
                    black, blue, black, blue, black, black, black, black, blue, black, black,
                    black, blue, blue, blue, blue, black, black,
                ]
            }
            Self::Excited => {
                vec![
                    yellow, yellow, yellow, yellow, yellow, yellow, yellow, yellow, yellow, black,
                    black, yellow, yellow, black, black, yellow, yellow, black, white, yellow,
                    yellow, white, black, yellow, yellow, yellow, yellow, yellow, yellow, yellow,
                    yellow, yellow, yellow, black, black, black, black, black, black, yellow,
                    yellow, black, white, white, white, white, black, yellow, yellow, black, black,
                    black, black, black, black, yellow, yellow, yellow, yellow, yellow, yellow,
                    yellow, yellow, yellow,
                ]
            }
            Self::Love => {
                vec![
                    black, pink, pink, black, black, pink, pink, black, pink, pink, pink, pink,
                    pink, pink, pink, pink, pink, pink, pink, pink, pink, pink, pink, pink, pink,
                    pink, pink, pink, pink, pink, pink, pink, black, pink, pink, pink, pink, pink,
                    pink, black, black, black, pink, pink, pink, pink, black, black, black, black,
                    black, pink, pink, black, black, black, black, black, black, black, black,
                    black, black, black,
                ]
            }
            Self::Angry => {
                vec![
                    red, red, black, black, black, black, red, red, black, red, red, black, black,
                    red, red, black, black, black, red, black, black, red, black, black, black,
                    black, white, black, black, white, black, black, black, black, black, black,
                    black, black, black, black, black, black, white, white, white, white, black,
                    black, black, white, black, black, black, black, white, black, black, black,
                    black, black, black, black, black, black,
                ]
            }
            _ => {
                // Default neutral
                vec![white; 64]
            }
        }
    }
}

pub struct EmoteTool {
    #[allow(dead_code)]
    config: RobotConfig,
    sounds_dir: PathBuf,
}

impl EmoteTool {
    pub fn new(config: RobotConfig) -> Self {
        let sounds_dir = directories::UserDirs::new()
            .map(|d| d.home_dir().join(".kylinbot/sounds"))
            .unwrap_or_else(|| PathBuf::from("/usr/local/share/kylinbot/sounds"));

        Self { config, sounds_dir }
    }

    /// Set LED matrix expression
    async fn set_expression(&self, expr: Expression) -> Result<()> {
        let pattern = expr.pattern();

        // Convert to format for LED driver
        // In production, use rs_ws281x or similar
        let pattern_json = serde_json::to_string(&pattern)?;

        // Try to write to LED controller
        // Option 1: Write to FIFO/socket if LED daemon is running
        let led_fifo = PathBuf::from("/tmp/kylinbot_led.fifo");
        if led_fifo.exists() {
            tokio::fs::write(&led_fifo, pattern_json).await?;
            return Ok(());
        }

        // Option 2: Shell out to LED control script
        let output = tokio::process::Command::new("kylinbot-led")
            .args(["--pattern", &format!("{:?}", expr)])
            .output()
            .await;

        match output {
            Ok(out) if out.status.success() => Ok(()),
            _ => {
                tracing::info!("LED display: {:?} (hardware not connected)", expr);
                Ok(()) // Don't fail if LED hardware isn't available
            }
        }
    }

    /// Play emotion sound effect
    async fn play_emotion_sound(&self, emotion: &str) -> Result<()> {
        let sound_file = self.sounds_dir.join(format!("{}.wav", emotion));

        if !sound_file.exists() {
            tracing::debug!("No sound file for emotion: {}", emotion);
            return Ok(());
        }

        tokio::process::Command::new("aplay")
            .arg(sound_file)
            .output()
            .await?;

        Ok(())
    }

    /// Animate expression (e.g., blinking)
    async fn animate(&self, animation: &str) -> Result<()> {
        match animation {
            "blink" => {
                self.set_expression(Expression::Happy).await?;
                tokio::time::sleep(std::time::Duration::from_millis(100)).await;
                // "Closed eyes" - simplified
                tokio::time::sleep(std::time::Duration::from_millis(100)).await;
                self.set_expression(Expression::Happy).await?;
            }
            "nod" => {
                // Would control servo if available
                tracing::info!("Animation: nod");
            }
            "shake" => {
                tracing::info!("Animation: shake");
            }
            "dance" => {
                // Cycle through expressions
                for expr in [
                    Expression::Happy,
                    Expression::Excited,
                    Expression::Love,
                    Expression::Happy,
                ] {
                    self.set_expression(expr).await?;
                    tokio::time::sleep(std::time::Duration::from_millis(500)).await;
                }
            }
            _ => {}
        }
        Ok(())
    }
}

#[async_trait]
impl Tool for EmoteTool {
    fn name(&self) -> &str {
        "emote"
    }

    fn description(&self) -> &str {
        "Express emotions through LED display and sounds. Use this to show the robot's \
         emotional state - happy when playing, sad when saying goodbye, excited for games, etc. \
         This makes interactions with kids more engaging!"
    }

    fn parameters_schema(&self) -> Value {
        json!({
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "enum": ["happy", "sad", "surprised", "thinking", "sleepy", "excited", "love", "angry", "confused", "wink"],
                    "description": "Facial expression to display on LED matrix"
                },
                "animation": {
                    "type": "string",
                    "enum": ["blink", "nod", "shake", "dance"],
                    "description": "Optional animation to perform"
                },
                "sound": {
                    "type": "boolean",
                    "description": "Play matching sound effect (default true)"
                },
                "duration": {
                    "type": "integer",
                    "description": "How long to hold expression in seconds (default 3)"
                }
            },
            "required": ["expression"]
        })
    }

    async fn execute(&self, args: Value) -> Result<ToolResult> {
        let expression_str = args["expression"]
            .as_str()
            .ok_or_else(|| anyhow::anyhow!("Missing 'expression' parameter"))?;

        let expression = Expression::from_str(expression_str)
            .ok_or_else(|| anyhow::anyhow!("Unknown expression: {}", expression_str))?;

        let play_sound = args["sound"].as_bool().unwrap_or(true);
        let duration = args["duration"].as_u64().unwrap_or(3);

        // Set expression
        self.set_expression(expression).await?;

        // Play sound if enabled
        if play_sound {
            let _ = self.play_emotion_sound(expression_str).await;
        }

        // Run animation if specified
        if let Some(animation) = args["animation"].as_str() {
            self.animate(animation).await?;
        }

        // Hold expression
        if duration > 0 {
            tokio::time::sleep(std::time::Duration::from_secs(duration.min(10))).await;
        }

        Ok(ToolResult {
            success: true,
            output: format!("Expressing: {} for {}s", expression_str, duration),
            error: None,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn emote_tool_name() {
        let tool = EmoteTool::new(RobotConfig::default());
        assert_eq!(tool.name(), "emote");
    }

    #[test]
    fn expression_parsing() {
        assert!(Expression::from_str("happy").is_some());
        assert!(Expression::from_str("EXCITED").is_some());
        assert!(Expression::from_str("unknown").is_none());
    }

    #[test]
    fn expression_pattern_size() {
        let expr = Expression::Happy;
        assert_eq!(expr.pattern().len(), 64); // 8x8
    }

    #[tokio::test]
    async fn emote_happy() {
        let tool = EmoteTool::new(RobotConfig::default());
        let result = tool
            .execute(json!({
                "expression": "happy",
                "duration": 0
            }))
            .await
            .unwrap();
        assert!(result.success);
    }
}
