//! TUI application state and logic.

use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use serde_json::Value;
use uuid::Uuid;

/// Available views/resource types
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ViewType {
    Tasks,
    Contracts,
    Files,
}

impl ViewType {
    pub fn as_str(&self) -> &'static str {
        match self {
            ViewType::Tasks => "tasks",
            ViewType::Contracts => "contracts",
            ViewType::Files => "files",
        }
    }
}

/// Input mode for the TUI
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputMode {
    /// Normal navigation mode
    Normal,
    /// Fuzzy search mode
    Search,
    /// Confirmation dialog (e.g., for delete)
    Confirm,
}

/// Actions that can be performed
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
    /// Do nothing
    None,
    /// Move selection up
    Up,
    /// Move selection down
    Down,
    /// Select current item (show details)
    Select,
    /// Edit the selected item (open in editor)
    Edit,
    /// Delete the selected item
    Delete,
    /// Navigate to worktree (output path and exit)
    Navigate,
    /// Confirm pending action
    ConfirmYes,
    /// Cancel pending action
    ConfirmNo,
    /// Enter search mode
    EnterSearch,
    /// Exit search mode
    ExitSearch,
    /// Add character to search
    SearchChar(char),
    /// Backspace in search
    SearchBackspace,
    /// Clear search
    ClearSearch,
    /// Quit the application
    Quit,
    /// Output a path to stdout and exit (for cd integration)
    OutputPath(String),
    /// Launch editor with path
    LaunchEditor(String),
    /// Refresh data
    Refresh,
}

/// A displayable item in the TUI
#[derive(Debug, Clone)]
pub struct ListItem {
    pub id: Uuid,
    pub name: String,
    pub status: Option<String>,
    pub description: Option<String>,
    /// Extra data for actions (e.g., worktree path)
    pub extra: Value,
}

impl ListItem {
    pub fn from_task(value: &Value) -> Option<Self> {
        let id = value.get("id")
            .and_then(|v| v.as_str())
            .and_then(|s| Uuid::parse_str(s).ok())?;

        let name = value.get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("Unnamed")
            .to_string();

        let status = value.get("status")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        let description = value.get("plan")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        Some(Self {
            id,
            name,
            status,
            description,
            extra: value.clone(),
        })
    }

    pub fn from_contract(value: &Value) -> Option<Self> {
        let id = value.get("id")
            .and_then(|v| v.as_str())
            .and_then(|s| Uuid::parse_str(s).ok())?;

        let name = value.get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("Unnamed")
            .to_string();

        let status = value.get("phase")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        let description = value.get("description")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        Some(Self {
            id,
            name,
            status,
            description,
            extra: value.clone(),
        })
    }

    pub fn from_file(value: &Value) -> Option<Self> {
        let id = value.get("id")
            .and_then(|v| v.as_str())
            .and_then(|s| Uuid::parse_str(s).ok())?;

        let name = value.get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("Unnamed")
            .to_string();

        let description = value.get("template_name")
            .and_then(|v| v.as_str())
            .map(|s| format!("Template: {}", s));

        Some(Self {
            id,
            name,
            status: None,
            description,
            extra: value.clone(),
        })
    }

    /// Get the worktree path from task extra data
    pub fn get_worktree_path(&self) -> Option<String> {
        // Try various field names that might contain the worktree path
        self.extra.get("worktreePath")
            .or_else(|| self.extra.get("worktree_path"))
            .or_else(|| self.extra.get("workdir"))
            .and_then(|v| v.as_str())
            .map(|s| s.to_string())
    }

    /// Build a detailed view string for display
    pub fn build_detail_view(&self, view_type: ViewType) -> String {
        let mut lines = Vec::new();

        match view_type {
            ViewType::Tasks => {
                lines.push(format!("╭─ Task Details ─────────────────────────"));
                lines.push(format!("│ Name: {}", self.name));
                lines.push(format!("│ ID: {}", self.id));
                if let Some(ref status) = self.status {
                    lines.push(format!("│ Status: {}", status));
                }
                if let Some(ref desc) = self.description {
                    lines.push(format!("│ Plan: {}", desc));
                }
                if let Some(path) = self.get_worktree_path() {
                    lines.push(format!("│ Worktree: {}", path));
                }
                // Add progress if available
                if let Some(progress) = self.extra.get("progress").and_then(|v| v.as_str()) {
                    lines.push(format!("│ Progress: {}", progress));
                }
                if let Some(error) = self.extra.get("error").and_then(|v| v.as_str()) {
                    lines.push(format!("│ Error: {}", error));
                }
                lines.push(format!("╰────────────────────────────────────────"));
            }
            ViewType::Contracts => {
                lines.push(format!("╭─ Contract Details ─────────────────────"));
                lines.push(format!("│ Name: {}", self.name));
                lines.push(format!("│ ID: {}", self.id));
                if let Some(ref status) = self.status {
                    lines.push(format!("│ Phase: {}", status));
                }
                if let Some(ref desc) = self.description {
                    lines.push(format!("│ Description: {}", desc));
                }
                lines.push(format!("╰────────────────────────────────────────"));
            }
            ViewType::Files => {
                lines.push(format!("╭─ File Details ─────────────────────────"));
                lines.push(format!("│ Name: {}", self.name));
                lines.push(format!("│ ID: {}", self.id));
                if let Some(ref desc) = self.description {
                    lines.push(format!("│ {}", desc));
                }
                // Add content preview if available
                if let Some(content) = self.extra.get("content").and_then(|v| v.as_str()) {
                    lines.push(format!("│"));
                    lines.push(format!("│ Content:"));
                    for line in content.lines().take(10) {
                        lines.push(format!("│ {}", line));
                    }
                    if content.lines().count() > 10 {
                        lines.push(format!("│ ... ({} more lines)", content.lines().count() - 10));
                    }
                }
                lines.push(format!("╰────────────────────────────────────────"));
            }
        }

        lines.join("\n")
    }
}

/// TUI Application state
pub struct App {
    /// Current view type
    pub view_type: ViewType,
    /// All items (unfiltered)
    pub items: Vec<ListItem>,
    /// Filtered items (based on search)
    pub filtered_items: Vec<ListItem>,
    /// Currently selected index in filtered list
    pub selected_index: usize,
    /// Current input mode
    pub input_mode: InputMode,
    /// Search query
    pub search_query: String,
    /// Fuzzy matcher
    matcher: SkimMatcherV2,
    /// Preview content (for selected item)
    pub preview_content: String,
    /// Whether preview is visible
    pub preview_visible: bool,
    /// Pending delete item (for confirmation)
    pub pending_delete: Option<Uuid>,
    /// Status message
    pub status_message: Option<String>,
    /// Whether the app should quit
    pub should_quit: bool,
    /// Action to return when exiting (for OutputPath, LaunchEditor)
    pub exit_action: Option<Action>,
    /// Contract ID (for API calls)
    pub contract_id: Option<Uuid>,
}

impl App {
    pub fn new(view_type: ViewType) -> Self {
        Self {
            view_type,
            items: Vec::new(),
            filtered_items: Vec::new(),
            selected_index: 0,
            input_mode: InputMode::Normal,
            search_query: String::new(),
            matcher: SkimMatcherV2::default(),
            preview_content: String::new(),
            preview_visible: false,
            pending_delete: None,
            status_message: None,
            should_quit: false,
            exit_action: None,
            contract_id: None,
        }
    }

    /// Set items and update filtered list
    pub fn set_items(&mut self, items: Vec<ListItem>) {
        self.items = items;
        self.update_filtered_items();
    }

    /// Update filtered items based on search query
    pub fn update_filtered_items(&mut self) {
        if self.search_query.is_empty() {
            self.filtered_items = self.items.clone();
        } else {
            let mut scored: Vec<_> = self.items
                .iter()
                .filter_map(|item| {
                    let score = self.matcher.fuzzy_match(&item.name, &self.search_query)?;
                    Some((score, item.clone()))
                })
                .collect();

            // Sort by score (highest first)
            scored.sort_by(|a, b| b.0.cmp(&a.0));
            self.filtered_items = scored.into_iter().map(|(_, item)| item).collect();
        }

        // Reset selection if out of bounds
        if self.selected_index >= self.filtered_items.len() {
            self.selected_index = self.filtered_items.len().saturating_sub(1);
        }
    }

    /// Get currently selected item
    pub fn selected_item(&self) -> Option<&ListItem> {
        self.filtered_items.get(self.selected_index)
    }

    /// Handle an action and return the resulting action
    pub fn handle_action(&mut self, action: Action) -> Action {
        match action {
            Action::Up => {
                if self.selected_index > 0 {
                    self.selected_index -= 1;
                }
                Action::None
            }
            Action::Down => {
                if self.selected_index < self.filtered_items.len().saturating_sub(1) {
                    self.selected_index += 1;
                }
                Action::None
            }
            Action::Select => {
                // Build detailed view for selected item
                if let Some(item) = self.selected_item() {
                    self.preview_content = item.build_detail_view(self.view_type);
                    self.preview_visible = true;
                }
                Action::None
            }
            Action::Edit => {
                // Get worktree path and signal to launch editor
                if let Some(item) = self.selected_item() {
                    if let Some(path) = item.get_worktree_path() {
                        self.should_quit = true;
                        self.exit_action = Some(Action::LaunchEditor(path.clone()));
                        return Action::LaunchEditor(path);
                    } else {
                        self.status_message = Some("No worktree path for this item".to_string());
                    }
                }
                Action::None
            }
            Action::Delete => {
                // First press: enter confirm mode
                // Clone the values we need to avoid borrow issues
                if let Some(item) = self.selected_item() {
                    let id = item.id;
                    let name = item.name.clone();
                    self.pending_delete = Some(id);
                    self.input_mode = InputMode::Confirm;
                    self.status_message = Some(format!("Delete '{}'? (y/n)", name));
                }
                Action::None
            }
            Action::Navigate => {
                // Get worktree path and output it
                if let Some(item) = self.selected_item() {
                    if let Some(path) = item.get_worktree_path() {
                        self.should_quit = true;
                        self.exit_action = Some(Action::OutputPath(path.clone()));
                        return Action::OutputPath(path);
                    } else {
                        self.status_message = Some("No worktree path for this item".to_string());
                    }
                }
                Action::None
            }
            Action::ConfirmYes => {
                if self.input_mode == InputMode::Confirm {
                    if let Some(_delete_id) = self.pending_delete.take() {
                        // TODO: Make API call to delete the item
                        // For now, just show status
                        self.status_message = Some("Delete confirmed (API call not implemented)".to_string());
                    }
                    self.input_mode = InputMode::Normal;
                }
                Action::None
            }
            Action::ConfirmNo => {
                if self.input_mode == InputMode::Confirm {
                    self.pending_delete = None;
                    self.input_mode = InputMode::Normal;
                    self.status_message = Some("Delete cancelled".to_string());
                }
                Action::None
            }
            Action::EnterSearch => {
                self.input_mode = InputMode::Search;
                Action::None
            }
            Action::ExitSearch => {
                self.input_mode = InputMode::Normal;
                Action::None
            }
            Action::SearchChar(c) => {
                self.search_query.push(c);
                self.update_filtered_items();
                Action::None
            }
            Action::SearchBackspace => {
                self.search_query.pop();
                self.update_filtered_items();
                Action::None
            }
            Action::ClearSearch => {
                self.search_query.clear();
                self.update_filtered_items();
                Action::None
            }
            Action::Quit => {
                self.should_quit = true;
                Action::Quit
            }
            Action::Refresh => {
                // Signal to caller to refresh data
                Action::Refresh
            }
            Action::OutputPath(path) => {
                self.should_quit = true;
                self.exit_action = Some(Action::OutputPath(path.clone()));
                Action::OutputPath(path)
            }
            Action::LaunchEditor(path) => {
                self.should_quit = true;
                self.exit_action = Some(Action::LaunchEditor(path.clone()));
                Action::LaunchEditor(path)
            }
            Action::None => Action::None,
        }
    }

    /// Get the name of the item being deleted (for confirmation dialog)
    pub fn get_pending_delete_name(&self) -> Option<String> {
        self.pending_delete.and_then(|id| {
            self.filtered_items.iter()
                .find(|item| item.id == id)
                .map(|item| item.name.clone())
        })
    }
}
