use std::io::{BufRead, BufReader, Write};
use std::process::{Child, ChildStdin, Command, Stdio};
use std::sync::Arc;
use std::thread;
use tauri::{AppHandle, Emitter};
use tempfile::NamedTempFile;

#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;

use crate::achievements::{get_achievement_info, AchievementUnlockedEvent};
use crate::config::ClaudeStartOptions;
use crate::stats::{StatsUpdateEvent, UsageStats};
use crate::types::{
    CharacterState, ClaudeMessage, ConnectionEvent, ConnectionStatus, ContentBlock, OutputEvent,
    PermissionPromptEvent, QuestionOption, SessionEvent, StateChangeEvent, UserQuestionEvent,
    WorkingDirectoryEvent,
};
use parking_lot::RwLock;

const SEARCH_TOOLS: [&str; 5] = ["Read", "Glob", "Grep", "WebSearch", "WebFetch"];
const CODING_TOOLS: [&str; 3] = ["Edit", "Write", "NotebookEdit"];

fn detect_wsl() -> bool {
    // Check /proc/version for WSL indicators
    if let Ok(version) = std::fs::read_to_string("/proc/version") {
        let version_lower = version.to_lowercase();
        if version_lower.contains("microsoft") || version_lower.contains("wsl") {
            return true;
        }
    }

    // Fallback: check for WSLInterop
    if std::path::Path::new("/proc/sys/fs/binfmt_misc/WSLInterop").exists() {
        return true;
    }

    // Check for WSL environment variable
    if std::env::var("WSL_DISTRO_NAME").is_ok() {
        return true;
    }

    false
}

fn find_claude_binary() -> Option<String> {
    // Check common installation locations for claude
    let home = std::env::var("HOME").ok()?;
    let paths_to_check = [
        format!("{}/.local/bin/claude", home),
        format!("{}/.claude/local/claude", home),
        "/usr/local/bin/claude".to_string(),
        "/usr/bin/claude".to_string(),
    ];

    for path in &paths_to_check {
        if std::path::Path::new(path).exists() {
            return Some(path.clone());
        }
    }

    // Fall back to checking PATH via which
    if let Ok(output) = Command::new("which").arg("claude").output() {
        if output.status.success() {
            let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
            if !path.is_empty() {
                return Some(path);
            }
        }
    }

    None
}

pub struct WslBridge {
    process: Option<Child>,
    stdin: Option<ChildStdin>,
    working_directory: String,
    session_id: Option<String>,
    mcp_config_file: Option<NamedTempFile>,
    stats: Arc<RwLock<UsageStats>>,
    conversation_id: Option<String>,
}

impl WslBridge {
    pub fn new() -> Self {
        WslBridge {
            process: None,
            stdin: None,
            working_directory: String::new(),
            session_id: None,
            mcp_config_file: None,
            stats: Arc::new(RwLock::new(UsageStats::new())),
            conversation_id: None,
        }
    }

    pub fn new_with_conversation_id(conversation_id: String) -> Self {
        WslBridge {
            process: None,
            stdin: None,
            working_directory: String::new(),
            session_id: None,
            mcp_config_file: None,
            stats: Arc::new(RwLock::new(UsageStats::new())),
            conversation_id: Some(conversation_id),
        }
    }

    pub fn start(&mut self, app: AppHandle, options: ClaudeStartOptions) -> Result<(), String> {
        if self.process.is_some() {
            return Err("Process already running".to_string());
        }

        // Load saved achievements and stats when starting a new session
        let app_clone = app.clone();
        let stats = self.stats.clone();
        tauri::async_runtime::spawn(async move {
            println!("Loading saved achievements...");
            let achievements = crate::achievements::load_achievements(&app_clone).await;
            println!(
                "Loaded {} unlocked achievements",
                achievements.unlocked.len()
            );

            println!("Loading saved stats...");
            let persisted_stats = crate::stats::load_stats(&app_clone).await;

            let mut stats_guard = stats.write();
            stats_guard.achievements = achievements;

            if let Some(persisted) = persisted_stats {
                println!("Applying persisted lifetime stats");
                stats_guard.apply_persisted(persisted);
            }
        });

        let working_dir = &options.working_dir;
        self.working_directory = working_dir.clone();

        emit_connection_status(
            &app,
            ConnectionStatus::Connecting,
            self.conversation_id.clone(),
        );

        // Create temp file for MCP config if provided
        let mcp_config_path = if let Some(ref mcp_json) = options.mcp_servers_json {
            if !mcp_json.trim().is_empty() {
                // Validate JSON before writing
                serde_json::from_str::<serde_json::Value>(mcp_json)
                    .map_err(|e| format!("Invalid MCP servers JSON: {}", e))?;

                let mut temp_file = NamedTempFile::new()
                    .map_err(|e| format!("Failed to create temp file for MCP config: {}", e))?;
                temp_file
                    .write_all(mcp_json.as_bytes())
                    .map_err(|e| format!("Failed to write MCP config: {}", e))?;
                temp_file
                    .flush()
                    .map_err(|e| format!("Failed to flush MCP config: {}", e))?;

                let path = temp_file.path().to_string_lossy().to_string();
                self.mcp_config_file = Some(temp_file);
                Some(path)
            } else {
                None
            }
        } else {
            None
        };

        // Detect if we're running inside WSL or on Windows
        let is_wsl = detect_wsl();
        eprintln!("[DEBUG] is_wsl: {}", is_wsl);
        eprintln!("[DEBUG] options: {:?}", options);

        let mut command = if is_wsl {
            // Running inside WSL - call claude directly
            // Try to find claude in common locations since GUI apps may not inherit shell PATH
            let claude_path = find_claude_binary().ok_or_else(|| {
                "Could not find claude binary. Is Claude Code installed?".to_string()
            })?;

            eprintln!("[DEBUG] Found claude at: {}", claude_path);
            eprintln!("[DEBUG] Working dir: {}", working_dir);

            let mut cmd = Command::new(&claude_path);
            cmd.args([
                "--output-format",
                "stream-json",
                "--input-format",
                "stream-json",
                "--verbose",
            ]);

            // Add model if specified
            if let Some(ref model) = options.model {
                if !model.is_empty() {
                    cmd.args(["--model", model]);
                }
            }

            // Add allowed tools if any
            for tool in &options.allowed_tools {
                cmd.args(["--allowedTools", tool]);
            }

            // Add custom instructions as system prompt if specified
            if let Some(ref instructions) = options.custom_instructions {
                if !instructions.is_empty() {
                    cmd.args(["--system-prompt", instructions]);
                }
            }

            // Add MCP config if provided
            if let Some(ref mcp_path) = mcp_config_path {
                cmd.args(["--mcp-config", mcp_path]);
            }

            // Add resume flag if session ID provided
            if let Some(ref session_id) = options.resume_session_id {
                if !session_id.is_empty() {
                    cmd.args(["--resume", session_id]);
                }
            }

            cmd.current_dir(working_dir);

            // Set API key as environment variable if specified
            if let Some(ref api_key) = options.api_key {
                if !api_key.is_empty() {
                    cmd.env("ANTHROPIC_API_KEY", api_key);
                }
            }

            cmd
        } else {
            // Running on Windows - use wsl with bash login shell to ensure PATH is loaded
            eprintln!("[DEBUG] Windows path - using wsl");
            let mut cmd = Command::new("wsl");

            // Build the claude command with all arguments
            let mut claude_cmd = format!("cd '{}' && ", working_dir);

            // Set API key as environment variable if specified
            if let Some(ref api_key) = options.api_key {
                if !api_key.is_empty() {
                    claude_cmd.push_str(&format!("ANTHROPIC_API_KEY='{}' ", api_key));
                }
            }

            claude_cmd.push_str(
                "claude --output-format stream-json --input-format stream-json --verbose",
            );

            // Add model if specified
            if let Some(ref model) = options.model {
                if !model.is_empty() {
                    claude_cmd.push_str(&format!(" --model '{}'", model));
                }
            }

            // Add allowed tools if any
            for tool in &options.allowed_tools {
                claude_cmd.push_str(&format!(" --allowedTools '{}'", tool));
            }

            // Add custom instructions as system prompt if specified
            if let Some(ref instructions) = options.custom_instructions {
                if !instructions.is_empty() {
                    // Escape single quotes in instructions
                    let escaped = instructions.replace('\'', "'\\''");
                    claude_cmd.push_str(&format!(" --system-prompt '{}'", escaped));
                }
            }

            // Add MCP config if provided
            if let Some(ref mcp_path) = mcp_config_path {
                claude_cmd.push_str(&format!(" --mcp-config '{}'", mcp_path));
            }

            // Add resume flag if session ID provided
            if let Some(ref session_id) = options.resume_session_id {
                if !session_id.is_empty() {
                    claude_cmd.push_str(&format!(" --resume '{}'", session_id));
                }
            }

            // Use bash -lc to load login profile (ensures PATH includes claude)
            cmd.args(["-e", "bash", "-lc", &claude_cmd]);

            // Hide the console window on Windows
            #[cfg(target_os = "windows")]
            cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW

            cmd
        };

        command
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped());

        let mut child = command.spawn().map_err(|e| {
            eprintln!("[DEBUG] Spawn error: {:?}", e);
            format!("Failed to spawn process: {}", e)
        })?;

        let stdin = child.stdin.take();
        let stdout = child.stdout.take();
        let stderr = child.stderr.take();

        self.stdin = stdin;
        self.process = Some(child);

        // Note: We no longer reset stats here - stats persist across reconnects
        // Stats are only reset when explicitly disconnecting via stop()

        // Load saved achievements
        let app_handle = app.clone();
        let stats_clone = self.stats.clone();
        tokio::spawn(async move {
            let saved_progress = crate::achievements::load_achievements(&app_handle).await;
            stats_clone.write().achievements = saved_progress;
        });

        if let Some(stdout) = stdout {
            let app_clone = app.clone();
            let stats_clone = self.stats.clone();
            let conv_id = self.conversation_id.clone();
            thread::spawn(move || {
                handle_stdout(stdout, app_clone, stats_clone, conv_id);
            });
        }

        if let Some(stderr) = stderr {
            let app_clone = app.clone();
            let conv_id = self.conversation_id.clone();
            thread::spawn(move || {
                handle_stderr(stderr, app_clone, conv_id);
            });
        }

        emit_connection_status(
            &app,
            ConnectionStatus::Connected,
            self.conversation_id.clone(),
        );

        Ok(())
    }

    pub fn send_message(&mut self, message: &str) -> Result<(), String> {
        let stdin = self.stdin.as_mut().ok_or("Process not running")?;

        let input = serde_json::json!({
            "type": "user",
            "message": {
                "role": "user",
                "content": [{
                    "type": "text",
                    "text": message
                }]
            }
        });

        let json_line = serde_json::to_string(&input).map_err(|e| e.to_string())?;

        stdin
            .write_all(format!("{}\n", json_line).as_bytes())
            .map_err(|e| format!("Failed to write to stdin: {}", e))?;

        stdin
            .flush()
            .map_err(|e| format!("Failed to flush stdin: {}", e))?;

        Ok(())
    }

    pub fn send_tool_result(
        &mut self,
        tool_use_id: &str,
        result: serde_json::Value,
    ) -> Result<(), String> {
        let stdin = self.stdin.as_mut().ok_or("Process not running")?;

        // The content should be a JSON string representation of the result
        let content_str = serde_json::to_string(&result).map_err(|e| e.to_string())?;

        let input = serde_json::json!({
            "type": "user",
            "message": {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_use_id,
                    "content": content_str
                }]
            }
        });

        let json_line = serde_json::to_string(&input).map_err(|e| e.to_string())?;

        stdin
            .write_all(format!("{}\n", json_line).as_bytes())
            .map_err(|e| format!("Failed to write to stdin: {}", e))?;

        stdin
            .flush()
            .map_err(|e| format!("Failed to flush stdin: {}", e))?;

        Ok(())
    }

    pub fn interrupt(&mut self, app: &AppHandle) -> Result<(), String> {
        // Due to persistent bug in Claude Code where ESC/Ctrl+C doesn't work,
        // we have to kill the process. This is the only reliable way to stop it.
        // See: https://github.com/anthropics/claude-code/issues/3455
        if let Some(mut process) = self.process.take() {
            // Kill the process immediately
            let _ = process.kill();
            let _ = process.wait();

            // Clear stdin
            self.stdin = None;

            // Keep session_id and working directory for user reference
            // The user will see what session was interrupted

            // Emit disconnected status
            emit_connection_status(
                app,
                ConnectionStatus::Disconnected,
                self.conversation_id.clone(),
            );

            Ok(())
        } else {
            Err("No active process to interrupt".to_string())
        }
    }

    pub fn stop(&mut self, app: &AppHandle) {
        if let Some(mut process) = self.process.take() {
            let _ = process.kill();
            let _ = process.wait();
        }
        self.stdin = None;
        self.session_id = None;
        self.mcp_config_file = None; // Temp file is automatically deleted when dropped

        // Save lifetime stats before resetting session
        let stats_snapshot = self.stats.read().clone();
        let app_clone = app.clone();
        tauri::async_runtime::spawn(async move {
            println!("Saving stats on session stop...");
            if let Err(e) = crate::stats::save_stats(&app_clone, &stats_snapshot).await {
                eprintln!("Failed to save stats: {}", e);
            } else {
                println!("Stats saved successfully on session stop");
            }
        });

        // Reset session stats on explicit disconnect
        self.stats.write().reset_session();

        emit_connection_status(
            app,
            ConnectionStatus::Disconnected,
            self.conversation_id.clone(),
        );
    }

    pub fn is_running(&self) -> bool {
        self.process.is_some()
    }

    pub fn get_working_directory(&self) -> &str {
        &self.working_directory
    }

    pub fn get_stats(&self) -> UsageStats {
        self.stats.read().clone()
    }
}

impl Default for WslBridge {
    fn default() -> Self {
        Self::new()
    }
}

fn handle_stdout(
    stdout: std::process::ChildStdout,
    app: AppHandle,
    stats: Arc<RwLock<UsageStats>>,
    conversation_id: Option<String>,
) {
    let reader = BufReader::new(stdout);

    for line in reader.lines() {
        match line {
            Ok(line) if !line.is_empty() => {
                if let Err(e) = process_json_line(&line, &app, &stats, &conversation_id) {
                    eprintln!("Error processing line: {}", e);
                }
            }
            Err(e) => {
                eprintln!("Error reading stdout: {}", e);
                break;
            }
            _ => {}
        }
    }

    emit_connection_status(&app, ConnectionStatus::Disconnected, conversation_id);
}

fn handle_stderr(
    stderr: std::process::ChildStderr,
    app: AppHandle,
    conversation_id: Option<String>,
) {
    let reader = BufReader::new(stderr);

    for line in reader.lines() {
        match line {
            Ok(line) if !line.is_empty() => {
                let _ = app.emit(
                    "claude:output",
                    OutputEvent {
                        line_type: "error".to_string(),
                        content: line,
                        tool_name: None,
                        conversation_id: conversation_id.clone(),
                    },
                );
            }
            Err(_) => break,
            _ => {}
        }
    }
}

fn process_json_line(
    line: &str,
    app: &AppHandle,
    stats: &Arc<RwLock<UsageStats>>,
    conversation_id: &Option<String>,
) -> Result<(), String> {
    let message: ClaudeMessage = serde_json::from_str(line)
        .map_err(|e| format!("Failed to parse JSON: {} - Line: {}", e, line))?;

    match &message {
        ClaudeMessage::System {
            subtype,
            session_id,
            cwd,
            ..
        } => {
            if subtype == "init" {
                if let Some(id) = session_id {
                    let _ = app.emit(
                        "claude:session",
                        SessionEvent {
                            session_id: id.clone(),
                            conversation_id: conversation_id.clone(),
                        },
                    );
                }
                if let Some(dir) = cwd {
                    let _ = app.emit(
                        "claude:cwd",
                        WorkingDirectoryEvent {
                            directory: dir.clone(),
                            conversation_id: conversation_id.clone(),
                        },
                    );
                }
                emit_state_change(app, CharacterState::Idle, None, conversation_id.clone());
            }
        }

        ClaudeMessage::Assistant { message, .. } => {
            let mut state = CharacterState::Typing;
            let mut tool_name = None;

            // Only update stats if we have usage information
            if let Some(usage) = &message.usage {
                if let Some(model) = &message.model {
                    // Batch all stats updates in a single write lock
                    {
                        let mut stats_guard = stats.write();
                        stats_guard.increment_messages();
                        stats_guard.add_usage(usage.input_tokens, usage.output_tokens, model);
                        stats_guard.get_session_duration();
                    }

                    // Don't emit here - we'll emit on Result message instead
                    // This reduces the frequency of updates
                } else {
                    // Just increment message count if no usage info
                    stats.write().increment_messages();
                }
            } else {
                // Just increment message count if no usage info
                stats.write().increment_messages();
            }

            for block in &message.content {
                match block {
                    ContentBlock::ToolUse { name, input, .. } => {
                        tool_name = Some(name.clone());
                        state = get_tool_state(name);

                        // Batch tool tracking updates
                        {
                            let mut stats_guard = stats.write();
                            stats_guard.increment_tool_usage(name);

                            // Track file operations
                            match name.as_str() {
                                "Edit" => stats_guard.increment_files_edited(),
                                "Write" => stats_guard.increment_files_created(),
                                _ => {}
                            }
                        }

                        let desc = format_tool_description(name, input);
                        let _ = app.emit(
                            "claude:output",
                            OutputEvent {
                                line_type: "tool".to_string(),
                                content: desc,
                                tool_name: Some(name.clone()),
                                conversation_id: conversation_id.clone(),
                            },
                        );
                    }
                    ContentBlock::Text { text } => {
                        // Count code blocks in the text
                        let code_blocks = text.matches("```").count() / 2;
                        for _ in 0..code_blocks {
                            stats.write().increment_code_blocks();
                        }

                        let _ = app.emit(
                            "claude:output",
                            OutputEvent {
                                line_type: "assistant".to_string(),
                                content: text.clone(),
                                tool_name: None,
                                conversation_id: conversation_id.clone(),
                            },
                        );
                    }
                    ContentBlock::Thinking { thinking } => {
                        state = CharacterState::Thinking;
                        let _ = app.emit(
                            "claude:output",
                            OutputEvent {
                                line_type: "system".to_string(),
                                content: format!("[Thinking] {}", thinking),
                                tool_name: None,
                                conversation_id: conversation_id.clone(),
                            },
                        );
                    }
                    _ => {}
                }
            }

            emit_state_change(app, state, tool_name, conversation_id.clone());
        }

        ClaudeMessage::StreamEvent { event } => {
            if event.event_type == "content_block_start" {
                if let Some(block) = &event.content_block {
                    let state = match block.block_type.as_str() {
                        "thinking" => CharacterState::Thinking,
                        "text" => CharacterState::Typing,
                        "tool_use" => {
                            if let Some(name) = &block.name {
                                get_tool_state(name)
                            } else {
                                CharacterState::Typing
                            }
                        }
                        _ => CharacterState::Typing,
                    };
                    emit_state_change(app, state, block.name.clone(), conversation_id.clone());
                }
            } else if event.event_type == "content_block_delta" {
                if let Some(delta) = &event.delta {
                    if let Some(text) = &delta.text {
                        let _ = app.emit("claude:stream", text.clone());
                    }
                }
            }
        }

        ClaudeMessage::Result {
            subtype,
            result,
            permission_denials,
            usage,
            ..
        } => {
            let state = if subtype == "success" {
                CharacterState::Success
            } else {
                CharacterState::Error
            };

            // Track token usage from Result messages if available
            // This captures tokens from tool outputs and other operations
            if let Some(usage_info) = usage {
                // We need the model info to calculate cost properly
                // For now, use the last known model from stats
                let model = {
                    let stats_guard = stats.read();
                    stats_guard.model.clone().unwrap_or_else(|| "claude-opus-4-20250514".to_string())
                };

                let mut stats_guard = stats.write();
                stats_guard.add_usage(usage_info.input_tokens, usage_info.output_tokens, &model);
                println!("Result message tokens - input: {}, output: {}", usage_info.input_tokens, usage_info.output_tokens);
            }

            // Always emit updated stats on result message (less frequent)
            // This includes the latest session duration
            let newly_unlocked = {
                let mut stats_guard = stats.write();
                stats_guard.get_session_duration();
                println!("Checking achievements after result message...");
                let unlocked = stats_guard.check_achievements();
                println!("Newly unlocked achievements: {:?}", unlocked);
                unlocked
            };

            // Emit achievement events for any newly unlocked achievements
            for achievement_id in &newly_unlocked {
                let info = get_achievement_info(achievement_id);
                let _ = app.emit(
                    "achievement:unlocked",
                    AchievementUnlockedEvent { achievement: info },
                );
            }

            // Save achievements after unlocking new ones
            if !newly_unlocked.is_empty() {
                println!("Saving newly unlocked achievements: {:?}", newly_unlocked);
                let app_handle = app.clone();
                let achievements_progress = stats.read().achievements.clone();

                // Use Tauri's async runtime instead of tokio::spawn
                tauri::async_runtime::spawn(async move {
                    println!("Spawned save task for achievements");
                    if let Err(e) =
                        crate::achievements::save_achievements(&app_handle, &achievements_progress)
                            .await
                    {
                        eprintln!("Failed to save achievements: {}", e);
                    } else {
                        println!("Achievement save task completed successfully");
                    }
                });
            }

            let current_stats = stats.read().clone();
            let stats_event = StatsUpdateEvent {
                stats: current_stats.clone(),
            };
            let _ = app.emit("claude:stats", stats_event);

            // Save stats periodically (every 10 messages to avoid excessive disk writes)
            if current_stats.session_messages_exchanged.is_multiple_of(10)
                && current_stats.session_messages_exchanged > 0
            {
                let app_handle = app.clone();
                tauri::async_runtime::spawn(async move {
                    println!("Periodic stats save (every 10 messages)...");
                    if let Err(e) = crate::stats::save_stats(&app_handle, &current_stats).await {
                        eprintln!("Failed to save stats: {}", e);
                    }
                });
            }

            // Only emit error results - success content is already sent via Assistant message
            if subtype != "success" {
                if let Some(text) = result {
                    let _ = app.emit(
                        "claude:output",
                        OutputEvent {
                            line_type: "error".to_string(),
                            content: text.clone(),
                            tool_name: None,
                            conversation_id: conversation_id.clone(),
                        },
                    );
                }
            }

            // Check for permission denials and emit prompts for each
            if let Some(denials) = permission_denials {
                let mut has_regular_denials = false;

                for denial in denials {
                    // Special handling for AskUserQuestion tool
                    if denial.tool_name == "AskUserQuestion" {
                        if let Some(questions) = denial
                            .tool_input
                            .get("questions")
                            .and_then(|q| q.as_array())
                        {
                            // For now, handle the first question (most common case)
                            if let Some(first_question) = questions.first() {
                                let question_text = first_question
                                    .get("question")
                                    .and_then(|q| q.as_str())
                                    .unwrap_or("Claude has a question for you")
                                    .to_string();

                                let header = first_question
                                    .get("header")
                                    .and_then(|h| h.as_str())
                                    .map(|s| s.to_string());

                                let multi_select = first_question
                                    .get("multiSelect")
                                    .and_then(|m| m.as_bool())
                                    .unwrap_or(false);

                                let options: Vec<QuestionOption> = first_question
                                    .get("options")
                                    .and_then(|opts| opts.as_array())
                                    .map(|opts| {
                                        opts.iter()
                                            .filter_map(|opt| {
                                                let label =
                                                    opt.get("label").and_then(|l| l.as_str())?;
                                                let description = opt
                                                    .get("description")
                                                    .and_then(|d| d.as_str())
                                                    .map(|s| s.to_string());
                                                Some(QuestionOption {
                                                    label: label.to_string(),
                                                    description,
                                                })
                                            })
                                            .collect()
                                    })
                                    .unwrap_or_default();

                                let _ = app.emit(
                                    "claude:question",
                                    UserQuestionEvent {
                                        id: denial.tool_use_id.clone(),
                                        question: question_text,
                                        header,
                                        options,
                                        multi_select,
                                        conversation_id: conversation_id.clone(),
                                    },
                                );
                            }
                        }
                    } else {
                        has_regular_denials = true;
                        let description =
                            format_tool_description(&denial.tool_name, &denial.tool_input);
                        let _ = app.emit(
                            "claude:permission",
                            PermissionPromptEvent {
                                id: denial.tool_use_id.clone(),
                                tool_name: denial.tool_name.clone(),
                                tool_input: denial.tool_input.clone(),
                                description,
                                conversation_id: conversation_id.clone(),
                            },
                        );
                    }
                }

                // Show permission state if there were any denials (questions or regular)
                if has_regular_denials || !denials.is_empty() {
                    emit_state_change(
                        app,
                        CharacterState::Permission,
                        None,
                        conversation_id.clone(),
                    );
                    return Ok(());
                }
            }

            emit_state_change(app, state, None, conversation_id.clone());
        }

        ClaudeMessage::User { message } => {
            // Increment message count for user messages
            stats.write().increment_messages();

            // Extract text content from the message
            let message_text = message
                .content
                .iter()
                .filter_map(|block| match block {
                    crate::types::ContentBlock::Text { text } => Some(text.clone()),
                    _ => None,
                })
                .collect::<Vec<String>>()
                .join(" ");

            // Check achievements after user message
            let newly_unlocked = {
                let mut stats_guard = stats.write();
                println!("User sent message, checking achievements...");

                // Check message-based achievements
                let mut unlocked = crate::achievements::check_message_achievements(
                    &message_text,
                    &mut stats_guard.achievements,
                );

                // Check stats-based achievements
                let stats_unlocked = stats_guard.check_achievements();
                unlocked.extend(stats_unlocked);

                unlocked
            };

            // Emit achievement events for any newly unlocked achievements
            for achievement_id in &newly_unlocked {
                println!("User message unlocked achievement: {:?}", achievement_id);
                let info = get_achievement_info(achievement_id);
                let _ = app.emit(
                    "achievement:unlocked",
                    AchievementUnlockedEvent { achievement: info },
                );
            }

            // Save achievements after unlocking new ones
            if !newly_unlocked.is_empty() {
                println!("Saving newly unlocked achievements from user message");
                let app_handle = app.clone();
                let achievements_progress = stats.read().achievements.clone();
                tauri::async_runtime::spawn(async move {
                    if let Err(e) =
                        crate::achievements::save_achievements(&app_handle, &achievements_progress)
                            .await
                    {
                        eprintln!("Failed to save achievements: {}", e);
                    } else {
                        println!("Achievements saved after user message");
                    }
                });
            }

            emit_state_change(app, CharacterState::Thinking, None, conversation_id.clone());
        }
    }

    Ok(())
}

fn get_tool_state(tool_name: &str) -> CharacterState {
    if SEARCH_TOOLS.contains(&tool_name) {
        CharacterState::Searching
    } else if CODING_TOOLS.contains(&tool_name) {
        CharacterState::Coding
    } else if tool_name.starts_with("mcp__") {
        CharacterState::Mcp
    } else if tool_name == "Task" {
        CharacterState::Thinking
    } else {
        CharacterState::Typing
    }
}

fn format_tool_description(name: &str, input: &serde_json::Value) -> String {
    match name {
        "Read" => {
            if let Some(path) = input.get("file_path").and_then(|v| v.as_str()) {
                format!("Reading file: {}", path)
            } else {
                "Reading file...".to_string()
            }
        }
        "Glob" => {
            if let Some(pattern) = input.get("pattern").and_then(|v| v.as_str()) {
                format!("Searching for files: {}", pattern)
            } else {
                "Searching for files...".to_string()
            }
        }
        "Grep" => {
            if let Some(pattern) = input.get("pattern").and_then(|v| v.as_str()) {
                format!("Searching for: {}", pattern)
            } else {
                "Searching in files...".to_string()
            }
        }
        "Edit" | "Write" => {
            if let Some(path) = input.get("file_path").and_then(|v| v.as_str()) {
                format!("Editing: {}", path)
            } else {
                "Editing file...".to_string()
            }
        }
        "Bash" => {
            if let Some(cmd) = input.get("command").and_then(|v| v.as_str()) {
                let truncated = if cmd.len() > 50 {
                    format!("{}...", &cmd[..50])
                } else {
                    cmd.to_string()
                };
                format!("Running: {}", truncated)
            } else {
                "Running command...".to_string()
            }
        }
        _ => format!("Using tool: {}", name),
    }
}

fn emit_state_change(
    app: &AppHandle,
    state: CharacterState,
    tool_name: Option<String>,
    conversation_id: Option<String>,
) {
    let _ = app.emit(
        "claude:state",
        StateChangeEvent {
            state,
            tool_name,
            conversation_id,
        },
    );
}

fn emit_connection_status(
    app: &AppHandle,
    status: ConnectionStatus,
    conversation_id: Option<String>,
) {
    let _ = app.emit(
        "claude:connection",
        ConnectionEvent {
            status,
            conversation_id,
        },
    );
}

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

    #[test]
    fn test_get_tool_state_search_tools() {
        assert!(matches!(get_tool_state("Read"), CharacterState::Searching));
        assert!(matches!(get_tool_state("Glob"), CharacterState::Searching));
        assert!(matches!(get_tool_state("Grep"), CharacterState::Searching));
        assert!(matches!(
            get_tool_state("WebSearch"),
            CharacterState::Searching
        ));
        assert!(matches!(
            get_tool_state("WebFetch"),
            CharacterState::Searching
        ));
    }

    #[test]
    fn test_get_tool_state_coding_tools() {
        assert!(matches!(get_tool_state("Edit"), CharacterState::Coding));
        assert!(matches!(get_tool_state("Write"), CharacterState::Coding));
        assert!(matches!(
            get_tool_state("NotebookEdit"),
            CharacterState::Coding
        ));
    }

    #[test]
    fn test_get_tool_state_mcp_tools() {
        assert!(matches!(
            get_tool_state("mcp__github__create_issue"),
            CharacterState::Mcp
        ));
        assert!(matches!(
            get_tool_state("mcp__notion__search"),
            CharacterState::Mcp
        ));
    }

    #[test]
    fn test_get_tool_state_task() {
        assert!(matches!(get_tool_state("Task"), CharacterState::Thinking));
    }

    #[test]
    fn test_get_tool_state_unknown() {
        assert!(matches!(
            get_tool_state("SomeUnknownTool"),
            CharacterState::Typing
        ));
        assert!(matches!(get_tool_state("Bash"), CharacterState::Typing));
    }

    #[test]
    fn test_format_tool_description_read() {
        let input = serde_json::json!({"file_path": "/home/test/file.txt"});
        let desc = format_tool_description("Read", &input);
        assert_eq!(desc, "Reading file: /home/test/file.txt");
    }

    #[test]
    fn test_format_tool_description_read_no_path() {
        let input = serde_json::json!({});
        let desc = format_tool_description("Read", &input);
        assert_eq!(desc, "Reading file...");
    }

    #[test]
    fn test_format_tool_description_glob() {
        let input = serde_json::json!({"pattern": "**/*.rs"});
        let desc = format_tool_description("Glob", &input);
        assert_eq!(desc, "Searching for files: **/*.rs");
    }

    #[test]
    fn test_format_tool_description_grep() {
        let input = serde_json::json!({"pattern": "TODO"});
        let desc = format_tool_description("Grep", &input);
        assert_eq!(desc, "Searching for: TODO");
    }

    #[test]
    fn test_format_tool_description_edit() {
        let input = serde_json::json!({"file_path": "/home/test/main.rs"});
        let desc = format_tool_description("Edit", &input);
        assert_eq!(desc, "Editing: /home/test/main.rs");
    }

    #[test]
    fn test_format_tool_description_write() {
        let input = serde_json::json!({"file_path": "/home/test/new.txt"});
        let desc = format_tool_description("Write", &input);
        assert_eq!(desc, "Editing: /home/test/new.txt");
    }

    #[test]
    fn test_format_tool_description_bash_short() {
        let input = serde_json::json!({"command": "ls -la"});
        let desc = format_tool_description("Bash", &input);
        assert_eq!(desc, "Running: ls -la");
    }

    #[test]
    fn test_format_tool_description_bash_long() {
        let long_cmd = "a".repeat(100);
        let input = serde_json::json!({"command": long_cmd});
        let desc = format_tool_description("Bash", &input);
        assert!(desc.starts_with("Running: "));
        assert!(desc.ends_with("..."));
        assert!(desc.len() < 70);
    }

    #[test]
    fn test_format_tool_description_unknown() {
        let input = serde_json::json!({"some": "data"});
        let desc = format_tool_description("CustomTool", &input);
        assert_eq!(desc, "Using tool: CustomTool");
    }

    #[test]
    fn test_wsl_bridge_new() {
        let bridge = WslBridge::new();
        assert!(!bridge.is_running());
        assert_eq!(bridge.get_working_directory(), "");
    }

    #[test]
    fn test_wsl_bridge_default() {
        let bridge = WslBridge::default();
        assert!(!bridge.is_running());
    }

    #[test]
    fn test_create_shared_bridge_manager() {
        use crate::bridge_manager::create_shared_bridge_manager;
        let shared = create_shared_bridge_manager();
        let manager = shared.lock();
        assert!(manager.get_active_conversations().is_empty());
    }
}
