//! Application state — projects, agents, settings.
//!
//! All mutable state lives in `AppState`, wrapped in `Entity<AppState>` for
//! GPUI reactivity. Components read via `entity.read(cx)` and mutate via
//! `entity.update(cx, |state, cx| { ... cx.notify(); })`.

use serde::{Deserialize, Serialize};
use uuid::Uuid;

// ── Agent Message Types ─────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageRole {
    User,
    Assistant,
    System,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMessage {
    pub id: String,
    pub role: MessageRole,
    pub content: String,
    pub timestamp: u64,
    /// For tool calls: the tool name.
    pub tool_name: Option<String>,
    /// For tool results: the result content.
    pub tool_result: Option<String>,
    /// Whether this message block is collapsed in the UI.
    pub collapsed: bool,
}

// ── Agent State ─────────────────────────────────────────────────────

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AgentStatus {
    Idle,
    Running,
    Done,
    Error,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentSession {
    pub session_id: String,
    pub status: AgentStatus,
    pub messages: Vec<AgentMessage>,
    pub cost_usd: f64,
    pub tokens_used: u64,
    pub model: String,
}

impl AgentSession {
    pub fn new() -> Self {
        Self {
            session_id: Uuid::new_v4().to_string(),
            status: AgentStatus::Idle,
            messages: Vec::new(),
            cost_usd: 0.0,
            tokens_used: 0,
            model: "claude-sonnet-4-20250514".to_string(),
        }
    }
}

// ── Project ─────────────────────────────────────────────────────────

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ProjectTab {
    Model,
    Docs,
    Files,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Project {
    pub id: String,
    pub name: String,
    pub cwd: String,
    pub active_tab: ProjectTab,
    pub agent: AgentSession,
    /// Accent color index (cycles through palette).
    pub accent_index: usize,
}

impl Project {
    pub fn new(name: &str, cwd: &str, accent_index: usize) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            name: name.to_string(),
            cwd: cwd.to_string(),
            active_tab: ProjectTab::Model,
            agent: AgentSession::new(),
            accent_index,
        }
    }
}

// ── Settings ────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Settings {
    pub theme: String,
    pub ui_font_family: String,
    pub ui_font_size: f32,
    pub term_font_family: String,
    pub term_font_size: f32,
    pub default_shell: String,
    pub default_cwd: String,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            theme: "Catppuccin Mocha".to_string(),
            ui_font_family: "Inter".to_string(),
            ui_font_size: 14.0,
            term_font_family: "JetBrains Mono".to_string(),
            term_font_size: 14.0,
            default_shell: std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".to_string()),
            default_cwd: dirs::home_dir()
                .map(|p| p.to_string_lossy().to_string())
                .unwrap_or_else(|| "/".to_string()),
        }
    }
}

// ── Command Palette ─────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct PaletteCommand {
    pub id: &'static str,
    pub label: &'static str,
    pub shortcut: Option<&'static str>,
}

pub fn all_commands() -> Vec<PaletteCommand> {
    vec![
        PaletteCommand {
            id: "settings",
            label: "Open Settings",
            shortcut: Some("Ctrl+,"),
        },
        PaletteCommand {
            id: "new_project",
            label: "Add Project",
            shortcut: Some("Ctrl+N"),
        },
        PaletteCommand {
            id: "toggle_sidebar",
            label: "Toggle Sidebar",
            shortcut: Some("Ctrl+B"),
        },
        PaletteCommand {
            id: "focus_next",
            label: "Focus Next Project",
            shortcut: Some("Ctrl+]"),
        },
        PaletteCommand {
            id: "focus_prev",
            label: "Focus Previous Project",
            shortcut: Some("Ctrl+["),
        },
        PaletteCommand {
            id: "close_project",
            label: "Close Focused Project",
            shortcut: None,
        },
        PaletteCommand {
            id: "restart_agent",
            label: "Restart Agent",
            shortcut: None,
        },
        PaletteCommand {
            id: "stop_agent",
            label: "Stop Agent",
            shortcut: None,
        },
    ]
}

// ── Root Application State ──────────────────────────────────────────

pub struct AppState {
    pub projects: Vec<Project>,
    pub focused_project_idx: Option<usize>,
    pub settings: Settings,
    pub sidebar_open: bool,
    pub settings_open: bool,
    pub palette_open: bool,
    pub palette_query: String,
}

impl AppState {
    /// Create initial state with demo projects.
    pub fn new_demo() -> Self {
        let mut p1 = Project::new("agent-orchestrator", "~/code/ai/agent-orchestrator", 0);
        p1.agent.status = AgentStatus::Running; // Triggers pulsing dot animation
        Self {
            projects: vec![
                p1,
                Project::new("quanta-discord-bot", "~/code/quanta-discord-bot", 1),
            ],
            focused_project_idx: Some(0),
            settings: Settings::default(),
            sidebar_open: true,
            settings_open: false,
            palette_open: false,
            palette_query: String::new(),
        }
    }

    pub fn focused_project(&self) -> Option<&Project> {
        self.focused_project_idx
            .and_then(|i| self.projects.get(i))
    }

    pub fn focused_project_mut(&mut self) -> Option<&mut Project> {
        self.focused_project_idx
            .and_then(|i| self.projects.get_mut(i))
    }

    pub fn toggle_sidebar(&mut self) {
        self.sidebar_open = !self.sidebar_open;
    }

    pub fn toggle_settings(&mut self) {
        self.settings_open = !self.settings_open;
    }

    pub fn toggle_palette(&mut self) {
        self.palette_open = !self.palette_open;
        if self.palette_open {
            self.palette_query.clear();
        }
    }

    /// Filtered palette commands based on current query.
    pub fn filtered_commands(&self) -> Vec<PaletteCommand> {
        let q = self.palette_query.to_lowercase();
        all_commands()
            .into_iter()
            .filter(|cmd| q.is_empty() || cmd.label.to_lowercase().contains(&q))
            .collect()
    }

    /// Total running agents.
    pub fn running_agent_count(&self) -> usize {
        self.projects
            .iter()
            .filter(|p| p.agent.status == AgentStatus::Running)
            .count()
    }

    /// Total cost across all agents.
    pub fn total_cost(&self) -> f64 {
        self.projects.iter().map(|p| p.agent.cost_usd).sum()
    }

    /// Total tokens across all agents.
    pub fn total_tokens(&self) -> u64 {
        self.projects.iter().map(|p| p.agent.tokens_used).sum()
    }
}
