//! Application state management - R-STATE-SEPARATION pattern
//!
//! This module implements the R-STATE-SEPARATION design pattern:
//! - Application state is kept completely separate from rendering logic
//! - All state mutations happen through explicit methods
//! - Dirty flag marks when state has changed and redraw is needed
//! - No rendering logic here - that's in ui.rs

use crate::api::ApiClient;
use crate::types::{ChecklistItemDto, ProjectDto, TaskPriority};
use anyhow::Result;
use ratatui::widgets::ListState;
use tui_textarea::TextArea;

/// Application screens - Navigation state
///
/// # R-STATE-SEPARATION
/// Screens are part of state management, not rendering.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Screen {
    /// Projects list screen
    ProjectsList,
    /// Project detail screen
    ProjectDetail,
    /// Security assessment screen
    Security,
    /// Create project screen
    CreateProject,
    /// Tasks/checklist screen
    Tasks,
    /// Create/edit task screen
    CreateTask,
    /// Edit existing task screen
    EditTask,
    /// Task filter and sort screen
    TasksFilter,
    /// Delete confirmation modal
    DeleteConfirm,
    /// Keyboard shortcuts help screen
    Help,
    /// Theme selector screen
    ThemeSelector,
}

/// Sort options for tasks
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) enum TaskSortOption {
    /// Newest first (by creation date)
    #[default]
    CreatedNewest,
    /// Oldest first (by creation date)
    CreatedOldest,
    /// Highest priority first (Critical → High → Medium → Low)
    PriorityHighest,
    /// Lowest priority first (Low → Medium → High → Critical)
    PriorityLowest,
    /// Completed tasks first
    CompletedFirst,
    /// Pending tasks first
    PendingFirst,
}

impl TaskSortOption {
    /// Get all available sort options
    pub(crate) fn all() -> &'static [Self] {
        &[
            Self::CreatedNewest,
            Self::CreatedOldest,
            Self::PriorityHighest,
            Self::PriorityLowest,
            Self::CompletedFirst,
            Self::PendingFirst,
        ]
    }

    /// Get display label for this sort option
    pub(crate) fn label(&self) -> &'static str {
        match self {
            Self::CreatedNewest => "Newest First",
            Self::CreatedOldest => "Oldest First",
            Self::PriorityHighest => "Highest Priority",
            Self::PriorityLowest => "Lowest Priority",
            Self::CompletedFirst => "Completed First",
            Self::PendingFirst => "Pending First",
        }
    }
}

/// Delete confirmation mode
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DeleteMode {
    /// Show modal dialog for confirmation
    Modal,
    /// Show inline message, require second press
    Inline,
    /// Show prompt in status bar
    Prompt,
}

impl DeleteMode {
    /// Parse from string
    pub(crate) fn from_str(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "modal" => Self::Modal,
            "inline" => Self::Inline,
            "prompt" => Self::Prompt,
            _ => Self::Modal, // Default to modal
        }
    }
}

/// Application state - Complete immutable snapshot
///
/// # R-STATE-SEPARATION Pattern
///
/// This struct contains ALL mutable state. All mutations go through explicit methods
/// (see `handle_*` methods below). The rendering layer (ui.rs) reads this state
/// but NEVER modifies it - rendering functions take `&App`, never `&mut App`.
///
/// ## Dirty Flag Optimization
/// The `dirty` flag tracks whether state has changed since last render.
/// Only redraw when `dirty` is true, then set to false.
pub(crate) struct App {
    // === Rendering state ===
    /// Current screen (navigation state)
    pub screen: Screen,
    /// Dirty flag: true if state changed, needs redraw
    pub dirty: bool,

    // === Projects data ===
    /// Loaded projects list
    pub projects: Vec<ProjectDto>,
    /// Projects list state (R-LIST-STATE pattern)
    pub projects_list_state: ListState,
    /// Detailed view of selected project
    pub current_project: Option<ProjectDto>,

    // === Tasks/Checklist data ===
    /// Loaded checklist items for current project
    pub tasks: Vec<ChecklistItemDto>,
    /// Tasks list state (R-LIST-STATE pattern)
    pub tasks_list_state: ListState,

    // === Loading/Error state ===
    /// Whether currently loading from API
    pub is_loading: bool,
    /// Error message to display (None = no error)
    pub error: Option<String>,

    // === Form input state ===
    /// Project name input field (create form)
    pub project_name_input: String,
    /// Project description input field (create form)
    pub project_desc_input: String,

    // === Task creation/editing state ===
    /// Task description textarea (R-TEXTAREA-BASIC pattern)
    pub task_textarea: TextArea<'static>,
    /// Task creation mode: true for new, false for editing
    pub task_creation_mode: bool,
    /// Form error message during task creation
    pub task_form_error: Option<String>,
    /// ID of task being edited (None = creating new)
    pub editing_task_id: Option<String>,

    // === Task filtering/search state ===
    /// Search query for task description/ID
    pub task_search_query: String,
    /// Filter by completion status: None = all, Some(true) = completed, Some(false) = pending
    pub task_filter_completed: Option<bool>,
    /// Current sort option for tasks
    pub task_sort: TaskSortOption,
    /// Currently selected sort option index (for navigation in filter menu)
    pub filter_sort_selected: usize,

    // === Pagination state ===
    /// Current page for task pagination
    pub current_page: usize,
    /// Total tasks count (from server)
    pub total_tasks: usize,
    /// Tasks per page
    pub tasks_per_page: usize,

    // === Delete confirmation state ===
    /// Delete confirmation mode (modal, inline, prompt)
    pub delete_confirmation_mode: DeleteMode,
    /// Whether delete action is pending confirmation (inline mode)
    pub delete_confirm_pending: bool,
    /// Whether awaiting delete confirmation input (prompt mode)
    pub awaiting_delete_confirm: bool,

    // === Theme and layout state ===
    /// Current theme name
    pub current_theme: String,
    /// Selected theme index in theme selector
    pub theme_selector_index: usize,
    /// Task detail width percentage
    pub task_detail_width_percent: u16,

    // === Help screen state ===
    /// Help screen scroll offset
    pub help_scroll_offset: usize,

    // === Application lifecycle ===
    /// Should quit application (Ctrl+C or 'q')
    pub should_quit: bool,

    // === External dependencies ===
    /// API client for data fetching
    pub api: ApiClient,
}

impl App {
    // === Lifecycle Methods ===

    /// Create a new app instance with default state
    ///
    /// # Dirty Flag
    /// Starts with `dirty: true` to force initial render
    pub(crate) fn new() -> Self {
        let mut projects_list_state = ListState::default();
        projects_list_state.select(Some(0));

        let mut tasks_list_state = ListState::default();
        tasks_list_state.select(Some(0));

        Self {
            screen: Screen::ProjectsList,
            dirty: true, // Initial render needed
            projects: Vec::new(),
            projects_list_state,
            current_project: None,
            tasks: Vec::new(),
            tasks_list_state,
            is_loading: true,
            error: None,
            api: ApiClient::new(),
            project_name_input: String::new(),
            project_desc_input: String::new(),
            task_textarea: TextArea::default(),
            task_creation_mode: true,
            task_form_error: None,
            editing_task_id: None,
            task_search_query: String::new(),
            task_filter_completed: None,
            task_sort: TaskSortOption::default(),
            filter_sort_selected: 0,
            current_page: 0,
            total_tasks: 0,
            tasks_per_page: 50,
            delete_confirmation_mode: DeleteMode::Modal,
            delete_confirm_pending: false,
            awaiting_delete_confirm: false,
            current_theme: "default".to_string(),
            theme_selector_index: 0,
            task_detail_width_percent: 50,
            help_scroll_offset: 0,
            should_quit: false,
        }
    }

    // === Data Loading Methods ===

    /// Load projects from the API
    ///
    /// # Side Effects
    /// - Sets `is_loading` true before fetch
    /// - Sets `dirty = true` after fetch completes
    /// - Clears any previous error
    pub(crate) async fn load_projects(&mut self) -> Result<()> {
        self.is_loading = true;
        self.error = None;
        self.dirty = true;

        match self.api.list_projects().await {
            Ok(projects) => {
                self.projects = projects;
                self.is_loading = false;
                self.dirty = true; // Data changed, redraw needed
                Ok(())
            }
            Err(e) => {
                self.error = Some(format!("Failed to load projects: {}", e));
                self.is_loading = false;
                self.dirty = true; // Error state changed, redraw needed
                Err(e)
            }
        }
    }

    /// Load project detail from the API
    ///
    /// # Side Effects
    /// - Sets `is_loading` true before fetch
    /// - Sets `dirty = true` after fetch completes
    #[allow(dead_code)]
    pub(crate) async fn load_project_detail(&mut self, id: &str) -> Result<()> {
        self.is_loading = true;
        self.error = None;
        self.dirty = true;

        match self.api.get_project(id).await {
            Ok(project) => {
                self.current_project = Some(project);
                self.is_loading = false;
                self.dirty = true;
                Ok(())
            }
            Err(e) => {
                self.error = Some(format!("Failed to load project: {}", e));
                self.is_loading = false;
                self.dirty = true;
                Err(e)
            }
        }
    }

    // === Navigation Methods ===

    /// Select currently highlighted project and show detail view
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    /// # R-LIST-STATE Pattern
    /// Uses ListState for proper list selection management
    pub(crate) fn select_project(&mut self) {
        if let Some(selected) = self.projects_list_state.selected() {
            if let Some(project) = self.projects.get(selected) {
                self.current_project = Some(project.clone());
                self.screen = Screen::ProjectDetail;
                self.dirty = true;
            }
        }
    }

    /// Move selection down one project
    ///
    /// # Behavior
    /// - Wraps around to top when at bottom
    /// - Only works if projects list is not empty
    /// - Sets `dirty = true`
    /// # R-LIST-STATE Pattern
    /// Uses ListState for proper selection
    pub(crate) fn nav_down(&mut self) {
        if !self.projects.is_empty() {
            if let Some(selected) = self.projects_list_state.selected() {
                let next = if selected >= self.projects.len().saturating_sub(1) {
                    0 // Wrap to top
                } else {
                    selected + 1
                };
                self.projects_list_state.select(Some(next));
            }
            self.dirty = true;
        }
    }

    /// Move selection up one project
    ///
    /// # Behavior
    /// - Wraps around to bottom when at top
    /// - Only works if projects list is not empty
    /// - Sets `dirty = true`
    /// # R-LIST-STATE Pattern
    /// Uses ListState for proper selection
    pub(crate) fn nav_up(&mut self) {
        if !self.projects.is_empty() {
            if let Some(selected) = self.projects_list_state.selected() {
                let prev = if selected == 0 {
                    self.projects.len().saturating_sub(1) // Wrap to bottom
                } else {
                    selected - 1
                };
                self.projects_list_state.select(Some(prev));
            }
            self.dirty = true;
        }
    }

    /// Go back from detail view to projects list
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn back_to_projects(&mut self) {
        self.screen = Screen::ProjectsList;
        self.current_project = None;
        self.dirty = true;
    }

    /// Navigate to security assessment screen
    pub(crate) fn view_security(&mut self) {
        self.screen = Screen::Security;
        self.dirty = true;
    }

    /// Navigate to create project screen
    pub(crate) fn create_project_screen(&mut self) {
        self.screen = Screen::CreateProject;
        self.reset_create_project_form();
        self.dirty = true;
    }

    /// Load tasks from the API for the current project
    ///
    /// # Side Effects
    /// - Sets `is_loading` true before fetch
    /// - Sets `dirty = true` after fetch completes
    /// - Clears any previous error
    pub(crate) async fn load_tasks(&mut self) -> Result<()> {
        if let Some(project) = &self.current_project {
            self.is_loading = true;
            self.error = None;
            self.dirty = true;
            self.tasks_list_state.select(Some(0)); // Reset task selection

            match self.api.get_checklists(&project.id).await {
                Ok(tasks) => {
                    self.tasks = tasks;
                    self.is_loading = false;
                    self.dirty = true;
                    Ok(())
                }
                Err(e) => {
                    self.error = Some(format!("Failed to load tasks: {}", e));
                    self.is_loading = false;
                    self.dirty = true;
                    Err(e)
                }
            }
        } else {
            Err(anyhow::anyhow!("No project selected"))
        }
    }

    /// Navigate to tasks screen for the currently selected project
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn view_tasks(&mut self) {
        if self.current_project.is_some() {
            self.screen = Screen::Tasks;
            self.tasks_list_state.select(Some(0));
            self.dirty = true;
        }
    }

    /// Move task selection down one position
    ///
    /// # Behavior
    /// - Wraps around to top when at bottom
    /// - Only works if tasks list is not empty
    /// - Sets `dirty = true`
    /// # R-LIST-STATE Pattern
    /// Uses ListState for proper selection
    pub(crate) fn task_nav_down(&mut self) {
        if !self.tasks.is_empty() {
            if let Some(selected) = self.tasks_list_state.selected() {
                let next = if selected >= self.tasks.len().saturating_sub(1) {
                    0 // Wrap to top
                } else {
                    selected + 1
                };
                self.tasks_list_state.select(Some(next));
            }
            self.dirty = true;
        }
    }

    /// Move task selection up one position
    ///
    /// # Behavior
    /// - Wraps around to bottom when at top
    /// - Only works if tasks list is not empty
    /// - Sets `dirty = true`
    /// # R-LIST-STATE Pattern
    /// Uses ListState for proper selection
    pub(crate) fn task_nav_up(&mut self) {
        if !self.tasks.is_empty() {
            if let Some(selected) = self.tasks_list_state.selected() {
                let prev = if selected == 0 {
                    self.tasks.len().saturating_sub(1) // Wrap to bottom
                } else {
                    selected - 1
                };
                self.tasks_list_state.select(Some(prev));
            }
            self.dirty = true;
        }
    }

    /// Toggle the completion status of the selected task
    ///
    /// # Behavior
    /// - Toggles `completed` status of the selected task
    /// - Marks `dirty = true` to trigger redraw
    /// - Does nothing if no task is selected
    /// # R-LIST-STATE Pattern
    /// Uses ListState for proper selection
    pub(crate) fn toggle_task_completion(&mut self) {
        if let Some(selected) = self.tasks_list_state.selected() {
            if let Some(task) = self.tasks.get_mut(selected) {
                task.completed = !task.completed;
                self.dirty = true;
                // Note: In a full implementation, this would also call the API to persist the change
            }
        }
    }

    /// Go back from tasks screen to project detail
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn back_to_project_detail(&mut self) {
        self.screen = Screen::ProjectDetail;
        self.tasks.clear();
        self.tasks_list_state.select(Some(0));
        self.dirty = true;
    }

    /// Navigate to task creation screen
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn create_task_screen(&mut self) {
        self.screen = Screen::CreateTask;
        self.task_textarea = TextArea::default();
        self.task_creation_mode = true;
        self.task_form_error = None;
        self.dirty = true;
    }

    /// Validate task description with comprehensive checks
    ///
    /// # Validation Rules
    /// - Content cannot be empty or whitespace only
    /// - Minimum 3 characters (trimmed)
    /// - Maximum 500 characters (including newlines)
    /// - First line cannot be empty or whitespace only
    ///
    /// # Errors
    /// Returns specific error messages for each validation failure
    pub(crate) fn validate_task_description(&self) -> Result<(), String> {
        let content = self.task_textarea.lines().join("\n");
        let trimmed = content.trim();

        // Check for empty input
        if trimmed.is_empty() {
            return Err("Please enter a task description (at least 3 characters)".to_string());
        }

        // Check for minimum length (based on trimmed content)
        if trimmed.len() < 3 {
            return Err(format!(
                "Task description must be at least 3 characters (currently {} chars)",
                trimmed.len()
            ));
        }

        // Check for maximum length (allow more for multi-line descriptions)
        if content.len() > 500 {
            return Err(format!(
                "Task description is too long ({} chars, maximum is 500)",
                content.len()
            ));
        }

        // Check that first line is not just whitespace
        if let Some(first_line) = self.task_textarea.lines().first() {
            if first_line.trim().is_empty() {
                return Err("First line of task cannot be empty".to_string());
            }
        }

        Ok(())
    }

    /// Go back from task creation to tasks screen
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn back_to_tasks(&mut self) {
        self.screen = Screen::Tasks;
        self.task_textarea = TextArea::default();
        self.task_form_error = None;
        self.dirty = true;
    }

    // === Task Filtering/Search Methods ===

    /// Get filtered and sorted tasks
    ///
    /// Applies search query, completion filter, and sort order to the task list.
    /// This is a read-only method that doesn't modify state.
    pub(crate) fn filtered_tasks(&self) -> Vec<ChecklistItemDto> {
        let mut filtered: Vec<_> = self
            .tasks
            .iter()
            .filter(|task| {
                // Apply completion filter
                match self.task_filter_completed {
                    Some(true) => task.completed,
                    Some(false) => !task.completed,
                    None => true, // Show all
                }
            })
            .filter(|task| {
                // Apply search query
                if self.task_search_query.is_empty() {
                    return true;
                }
                let query = self.task_search_query.to_lowercase();
                task.description.to_lowercase().contains(&query)
                    || task.id.to_lowercase().contains(&query)
            })
            .cloned()
            .collect();

        // Apply sorting
        match self.task_sort {
            TaskSortOption::CreatedNewest => {
                filtered.sort_by(|a, b| b.created_at.cmp(&a.created_at));
            }
            TaskSortOption::CreatedOldest => {
                filtered.sort_by(|a, b| a.created_at.cmp(&b.created_at));
            }
            TaskSortOption::PriorityHighest => {
                filtered.sort_by(|a, b| {
                    let priority_order = |p: &TaskPriority| match p {
                        TaskPriority::Critical => 0,
                        TaskPriority::High => 1,
                        TaskPriority::Medium => 2,
                        TaskPriority::Low => 3,
                    };
                    priority_order(&a.priority).cmp(&priority_order(&b.priority))
                });
            }
            TaskSortOption::PriorityLowest => {
                filtered.sort_by(|a, b| {
                    let priority_order = |p: &TaskPriority| match p {
                        TaskPriority::Critical => 0,
                        TaskPriority::High => 1,
                        TaskPriority::Medium => 2,
                        TaskPriority::Low => 3,
                    };
                    priority_order(&b.priority).cmp(&priority_order(&a.priority))
                });
            }
            TaskSortOption::CompletedFirst => {
                filtered.sort_by(|a, b| b.completed.cmp(&a.completed));
            }
            TaskSortOption::PendingFirst => {
                filtered.sort_by(|a, b| a.completed.cmp(&b.completed));
            }
        }

        filtered
    }

    /// Calculate task statistics from all tasks
    ///
    /// Returns (total, completed, pending, completion_percentage)
    pub(crate) fn calculate_statistics(&self) -> (usize, usize, usize, f64) {
        let total = self.tasks.len();
        let completed = self.tasks.iter().filter(|t| t.completed).count();
        let pending = total - completed;
        let completion_percentage = if total > 0 {
            (completed as f64 / total as f64) * 100.0
        } else {
            0.0
        };

        (total, completed, pending, completion_percentage)
    }

    /// Toggle filter view to filter/sort screen
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn show_filters(&mut self) {
        self.screen = Screen::TasksFilter;
        self.dirty = true;
    }

    /// Return from filter screen to tasks screen
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` when screen changes
    pub(crate) fn back_to_task_view(&mut self) {
        self.screen = Screen::Tasks;
        self.dirty = true;
    }

    /// Toggle completion filter (all/completed/pending)
    pub(crate) fn toggle_completion_filter(&mut self) {
        self.task_filter_completed = match self.task_filter_completed {
            None => Some(true),        // all → completed
            Some(true) => Some(false), // completed → pending
            Some(false) => None,       // pending → all
        };
        self.dirty = true;
    }

    /// Clear all filters (show all tasks in default sort order)
    pub(crate) fn clear_all_filters(&mut self) {
        self.task_search_query.clear();
        self.task_filter_completed = None;
        self.task_sort = TaskSortOption::default();
        self.dirty = true;
    }

    /// Add character to search query
    pub(crate) fn add_search_char(&mut self, c: char) {
        self.task_search_query.push(c);
        self.dirty = true;
    }

    /// Remove last character from search query
    pub(crate) fn backspace_search(&mut self) {
        self.task_search_query.pop();
        self.dirty = true;
    }

    /// Navigate to next sort option
    pub(crate) fn next_sort_option(&mut self) {
        let max = TaskSortOption::all().len();
        self.filter_sort_selected = (self.filter_sort_selected + 1) % max;
        self.dirty = true;
    }

    /// Navigate to previous sort option
    pub(crate) fn prev_sort_option(&mut self) {
        let max = TaskSortOption::all().len();
        self.filter_sort_selected = if self.filter_sort_selected == 0 {
            max - 1
        } else {
            self.filter_sort_selected - 1
        };
        self.dirty = true;
    }

    /// Apply selected sort option
    pub(crate) fn apply_sort_option(&mut self) {
        if let Some(&sort) = TaskSortOption::all().get(self.filter_sort_selected) {
            self.task_sort = sort;
            self.dirty = true;
        }
    }

    // === Error Handling ===

    /// Clear the current error message
    ///
    /// # Dirty Flag
    /// Sets `dirty = true` if error was present
    #[allow(dead_code)]
    pub(crate) fn clear_error(&mut self) {
        if self.error.is_some() {
            self.error = None;
            self.dirty = true;
        }
    }

    // === Form Management ===

    /// Reset create project form to empty state
    pub(crate) fn reset_create_project_form(&mut self) {
        self.project_name_input.clear();
        self.project_desc_input.clear();
    }

    /// Update project name input
    #[allow(dead_code)]
    pub(crate) fn set_project_name(&mut self, name: String) {
        self.project_name_input = name;
        self.dirty = true;
    }

    /// Update project description input
    #[allow(dead_code)]
    pub(crate) fn set_project_description(&mut self, desc: String) {
        self.project_desc_input = desc;
        self.dirty = true;
    }

    // === Application Lifecycle ===

    /// Request application quit
    pub(crate) fn quit(&mut self) {
        self.should_quit = true;
    }

    /// Mark state as rendered (clear dirty flag)
    ///
    /// # Usage
    /// Call this after successfully rendering, in main event loop:
    /// ```no_run
    /// if app.dirty {
    ///     terminal.draw(|f| ui::render(f, &app))?;
    ///     app.mark_rendered();  // Clear dirty flag
    /// }
    /// ```
    pub(crate) fn mark_rendered(&mut self) {
        self.dirty = false;
    }

    // === Task Editing Methods ===

    /// Get the currently selected task
    pub(crate) fn selected_task(&self) -> Option<&ChecklistItemDto> {
        if let Some(selected) = self.tasks_list_state.selected() {
            self.tasks.get(selected)
        } else {
            None
        }
    }

    /// Start editing the currently selected task
    pub(crate) fn start_edit_task(&mut self) {
        if let Some(task) = self.selected_task() {
            let description = task.description.clone();
            let task_id = task.id.clone();

            self.task_textarea = TextArea::from(vec![description.as_str()]);
            self.editing_task_id = Some(task_id);
            self.task_creation_mode = false;
            self.task_form_error = None;
            self.screen = Screen::EditTask;
            self.dirty = true;
        }
    }

    /// Submit task edit to API
    pub(crate) async fn submit_task_edit(&mut self) -> Result<()> {
        if let Some(task_id) = self.editing_task_id.take() {
            let description = self.task_textarea.lines().join("\n");

            // Validate description
            if let Err(e) = self.validate_task_description() {
                self.task_form_error = Some(e);
                self.editing_task_id = Some(task_id); // Restore ID
                self.dirty = true;
                return Err(anyhow::anyhow!("Validation failed"));
            }

            // Update via API (placeholder - implement when API method exists)
            // self.api.update_task(&task_id, &description).await?;

            // Update local state
            if let Some(task) = self.tasks.iter_mut().find(|t| t.id == task_id) {
                task.description = description;
            }

            self.screen = Screen::Tasks;
            self.task_form_error = None;
            self.dirty = true;
            Ok(())
        } else {
            Err(anyhow::anyhow!("No task being edited"))
        }
    }

    /// Cancel task editing
    pub(crate) fn cancel_edit_task(&mut self) {
        self.screen = Screen::Tasks;
        self.editing_task_id = None;
        self.task_textarea = TextArea::default();
        self.task_form_error = None;
        self.dirty = true;
    }

    // === Task Deletion Methods ===

    /// Start task deletion flow (mode-dependent)
    pub(crate) fn start_delete_task(&mut self) {
        if self.selected_task().is_none() {
            return; // No task selected
        }

        match self.delete_confirmation_mode {
            DeleteMode::Modal => {
                self.screen = Screen::DeleteConfirm;
                self.dirty = true;
            }
            DeleteMode::Inline => {
                if self.delete_confirm_pending {
                    // Second press - actually delete
                    // This will be handled by async operation
                    self.delete_confirm_pending = false;
                } else {
                    // First press - mark pending
                    self.delete_confirm_pending = true;
                    self.dirty = true;
                }
            }
            DeleteMode::Prompt => {
                self.awaiting_delete_confirm = true;
                self.dirty = true;
            }
        }
    }

    /// Confirm and execute task deletion
    pub(crate) async fn confirm_delete_task(&mut self) -> Result<()> {
        if let Some(task) = self.selected_task() {
            let task_id = task.id.clone();

            // Delete via API (placeholder - implement when API method exists)
            // self.api.delete_task(&task_id).await?;

            // Remove from local state
            self.tasks.retain(|t| t.id != task_id);

            // Reset delete state
            self.delete_confirm_pending = false;
            self.awaiting_delete_confirm = false;

            // Return to tasks screen
            if self.screen == Screen::DeleteConfirm {
                self.screen = Screen::Tasks;
            }

            self.dirty = true;
            Ok(())
        } else {
            Err(anyhow::anyhow!("No task selected"))
        }
    }

    /// Cancel task deletion
    pub(crate) fn cancel_delete_task(&mut self) {
        self.delete_confirm_pending = false;
        self.awaiting_delete_confirm = false;
        if self.screen == Screen::DeleteConfirm {
            self.screen = Screen::Tasks;
        }
        self.dirty = true;
    }

    // === Help Screen Methods ===

    /// Show help screen
    pub(crate) fn show_help(&mut self) {
        self.screen = Screen::Help;
        self.help_scroll_offset = 0;
        self.dirty = true;
    }

    /// Close help screen
    pub(crate) fn close_help(&mut self) {
        self.screen = Screen::Tasks; // Or previous screen
        self.dirty = true;
    }

    /// Scroll help screen down
    pub(crate) fn help_scroll_down(&mut self) {
        self.help_scroll_offset = self.help_scroll_offset.saturating_add(1);
        self.dirty = true;
    }

    /// Scroll help screen up
    pub(crate) fn help_scroll_up(&mut self) {
        self.help_scroll_offset = self.help_scroll_offset.saturating_sub(1);
        self.dirty = true;
    }

    // === Theme Selector Methods ===

    /// Show theme selector
    pub(crate) fn show_theme_selector(&mut self) {
        self.screen = Screen::ThemeSelector;
        self.dirty = true;
    }

    /// Close theme selector
    pub(crate) fn close_theme_selector(&mut self) {
        self.screen = Screen::Tasks; // Or previous screen
        self.dirty = true;
    }

    /// Navigate theme selector down
    pub(crate) fn theme_nav_down(&mut self) {
        let max_themes = 5; // default, dark, light, minimal, professional
        self.theme_selector_index = (self.theme_selector_index + 1) % max_themes;
        self.dirty = true;
    }

    /// Navigate theme selector up
    pub(crate) fn theme_nav_up(&mut self) {
        let max_themes = 5;
        self.theme_selector_index = if self.theme_selector_index == 0 {
            max_themes - 1
        } else {
            self.theme_selector_index - 1
        };
        self.dirty = true;
    }

    /// Apply selected theme
    pub(crate) fn apply_selected_theme(&mut self) -> Result<()> {
        let themes = ["default", "dark", "light", "minimal", "professional"];
        if let Some(&theme_name) = themes.get(self.theme_selector_index) {
            self.current_theme = theme_name.to_string();
            self.close_theme_selector();
            // TODO: Save to config file
            Ok(())
        } else {
            Err(anyhow::anyhow!("Invalid theme index"))
        }
    }

    /// Toggle dark mode (quick switch)
    #[allow(dead_code)]
    pub(crate) fn toggle_dark_mode(&mut self) {
        self.current_theme = if self.current_theme == "dark" {
            "default".to_string()
        } else {
            "dark".to_string()
        };
        self.dirty = true;
    }

    // === Layout Methods ===

    /// Save current layout to file
    #[allow(dead_code)]
    pub(crate) fn save_layout(&self) -> Result<()> {
        use crate::config::LayoutConfig;

        let layout = LayoutConfig {
            task_detail_width_percent: self.task_detail_width_percent,
            window_width: 0, // Will be set by terminal size
            window_height: 0,
            restore_on_startup: true,
        };

        layout
            .save()
            .map_err(|e| anyhow::anyhow!("Failed to save layout: {}", e))
    }

    /// Restore layout from file
    pub(crate) fn restore_layout(&mut self) -> Result<()> {
        use crate::config::LayoutConfig;

        match LayoutConfig::load() {
            Ok(layout) => {
                self.task_detail_width_percent = layout.task_detail_width_percent;
                self.dirty = true;
                Ok(())
            }
            Err(e) => {
                tracing::warn!("Failed to restore layout: {}", e);
                Ok(()) // Don't fail, just use defaults
            }
        }
    }

    /// Reset layout to defaults
    #[allow(dead_code)]
    pub(crate) fn reset_layout(&mut self) {
        self.task_detail_width_percent = 50;
        self.dirty = true;
    }

    // === Pagination Methods ===

    /// Load more tasks (next page)
    pub(crate) async fn load_more_tasks(&mut self) -> Result<()> {
        if self.current_page * self.tasks_per_page < self.total_tasks {
            self.current_page += 1;
            // TODO: Implement server-side pagination
            // For now, just mark as loading
            self.is_loading = true;
            self.dirty = true;
        }
        Ok(())
    }

    /// Get pagination info string
    #[allow(dead_code)]
    pub(crate) fn pagination_info(&self) -> String {
        let start = self.current_page * self.tasks_per_page + 1;
        let end = ((self.current_page + 1) * self.tasks_per_page).min(self.total_tasks);
        format!("Showing {}-{} of {} tasks", start, end, self.total_tasks)
    }

    // === Configuration Methods ===

    /// Load configuration from app config
    pub(crate) fn load_from_config(&mut self, config: &crate::config::TuiConfig) {
        use crate::app::DeleteMode;

        self.delete_confirmation_mode = DeleteMode::from_str(&config.ui.delete_confirmation_mode);
        self.current_theme = config.app.theme.clone();

        // Restore layout if configured
        if let Err(e) = self.restore_layout() {
            tracing::warn!("Failed to restore layout: {}", e);
        }

        self.dirty = true;
    }
}

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

// ============================================================================
// TESTS - R-STATE-SEPARATION pattern validation
// ============================================================================

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

    /// Helper function to create a test ProjectDto
    fn create_test_project(id: &str, name: &str, phase: &str) -> ProjectDto {
        ProjectDto {
            id: id.to_string(),
            name: name.to_string(),
            current_phase: phase.to_string(),
            version: "1.0".to_string(),
            project_type: "Web".to_string(),
            description: "Test project".to_string(),
            created_at: "2024-01-01".to_string(),
            updated_at: "2024-01-01".to_string(),
        }
    }

    // === APP INITIALIZATION TESTS ===

    #[test]
    fn test_app_new_default_state() {
        let app = App::new();

        assert_eq!(app.screen, Screen::ProjectsList);
        assert!(app.dirty); // Should start dirty to force initial render
        assert!(app.projects.is_empty());
        assert_eq!(app.projects_list_state.selected(), Some(0)); // ListState starts with first item selected
        assert!(app.current_project.is_none());
        assert!(!app.should_quit);
        assert!(app.error.is_none());
    }

    #[test]
    fn test_app_default_trait() {
        let app = App::default();
        let new_app = App::new();

        assert_eq!(app.screen, new_app.screen);
        assert_eq!(app.dirty, new_app.dirty);
        assert_eq!(app.should_quit, new_app.should_quit);
    }

    // === NAVIGATION TESTS ===

    #[test]
    fn test_nav_down_single_item() {
        let mut app = App::new();
        app.projects = vec![create_test_project("1", "Project 1", "Phase 1")];
        app.dirty = false;

        app.nav_down();

        // Should wrap around when at single item
        assert_eq!(app.projects_list_state.selected(), Some(0));
        assert!(app.dirty); // Dirty flag should be set
    }

    #[test]
    fn test_nav_down_multiple_items() {
        let mut app = App::new();
        app.projects = vec![
            create_test_project("1", "Project 1", "Phase 1"),
            create_test_project("2", "Project 2", "Phase 2"),
            create_test_project("3", "Project 3", "Phase 3"),
        ];
        app.dirty = false;
        app.projects_list_state.select(Some(0));

        app.nav_down();
        assert_eq!(app.projects_list_state.selected(), Some(1));
        assert!(app.dirty);

        app.dirty = false;
        app.nav_down();
        assert_eq!(app.projects_list_state.selected(), Some(2));
        assert!(app.dirty);

        // Test wrapping
        app.dirty = false;
        app.nav_down();
        assert_eq!(app.projects_list_state.selected(), Some(0)); // Should wrap to start
        assert!(app.dirty);
    }

    #[test]
    fn test_nav_up_wrapping() {
        let mut app = App::new();
        app.projects = vec![
            create_test_project("1", "P1", "Phase"),
            create_test_project("2", "P2", "Phase"),
        ];
        app.projects_list_state.select(Some(0));
        app.dirty = false;

        app.nav_up();

        // Should wrap to end when at start
        assert_eq!(app.projects_list_state.selected(), Some(1));
        assert!(app.dirty);
    }

    // === SCREEN NAVIGATION TESTS ===

    #[test]
    fn test_select_project() {
        let mut app = App::new();
        app.projects = vec![create_test_project("1", "Project 1", "Phase 1")];
        app.projects_list_state.select(Some(0));
        app.dirty = false;

        app.select_project();

        assert_eq!(app.screen, Screen::ProjectDetail);
        assert!(app.current_project.is_some());
        assert_eq!(app.current_project.as_ref().unwrap().id, "1");
        assert!(app.dirty);
    }

    #[test]
    fn test_back_to_projects() {
        let mut app = App::new();
        app.screen = Screen::ProjectDetail;
        app.current_project = Some(create_test_project("1", "Project 1", "Phase 1"));
        app.dirty = false;

        app.back_to_projects();

        assert_eq!(app.screen, Screen::ProjectsList);
        assert!(app.current_project.is_none());
        assert!(app.dirty);
    }

    #[test]
    fn test_create_project_screen() {
        let mut app = App::new();
        app.project_name_input = "Old Name".to_string();
        app.project_desc_input = "Old Desc".to_string();
        app.dirty = false;

        app.create_project_screen();

        assert_eq!(app.screen, Screen::CreateProject);
        assert!(app.project_name_input.is_empty()); // Form should be reset
        assert!(app.project_desc_input.is_empty());
        assert!(app.dirty);
    }

    #[test]
    fn test_view_security() {
        let mut app = App::new();
        app.dirty = false;

        app.view_security();

        assert_eq!(app.screen, Screen::Security);
        assert!(app.dirty);
    }

    // === ERROR HANDLING TESTS ===

    #[test]
    fn test_clear_error_when_present() {
        let mut app = App::new();
        app.error = Some("Error message".to_string());
        app.dirty = false;

        app.clear_error();

        assert!(app.error.is_none());
        assert!(app.dirty);
    }

    #[test]
    fn test_clear_error_when_not_present() {
        let mut app = App::new();
        app.error = None;
        app.dirty = false;

        app.clear_error();

        assert!(app.error.is_none());
        // Dirty should remain false if no error was present
        assert!(!app.dirty);
    }

    // === FORM INPUT TESTS ===

    #[test]
    fn test_set_project_name() {
        let mut app = App::new();
        app.dirty = false;

        app.set_project_name("New Project".to_string());

        assert_eq!(app.project_name_input, "New Project");
        assert!(app.dirty);
    }

    #[test]
    fn test_set_project_description() {
        let mut app = App::new();
        app.dirty = false;

        app.set_project_description("New Description".to_string());

        assert_eq!(app.project_desc_input, "New Description");
        assert!(app.dirty);
    }

    #[test]
    fn test_reset_create_project_form() {
        let mut app = App::new();
        app.project_name_input = "Name".to_string();
        app.project_desc_input = "Desc".to_string();

        app.reset_create_project_form();

        assert!(app.project_name_input.is_empty());
        assert!(app.project_desc_input.is_empty());
    }

    // === DIRTY FLAG TESTS ===

    #[test]
    fn test_mark_rendered_clears_dirty() {
        let mut app = App::new();
        assert!(app.dirty); // Starts dirty

        app.mark_rendered();

        assert!(!app.dirty);
    }

    #[test]
    fn test_dirty_flag_set_on_mutations() {
        let mut app = App::new();
        app.projects = vec![create_test_project("1", "P1", "Phase")];
        app.dirty = false;

        // Each mutation should set dirty
        app.nav_down();
        assert!(app.dirty);

        app.dirty = false;
        app.nav_up();
        assert!(app.dirty);

        app.dirty = false;
        app.create_project_screen();
        assert!(app.dirty);

        app.dirty = false;
        app.view_security();
        assert!(app.dirty);
    }

    // === APPLICATION LIFECYCLE TESTS ===

    #[test]
    fn test_quit() {
        let mut app = App::new();
        assert!(!app.should_quit);

        app.quit();

        assert!(app.should_quit);
    }

    // === EDGE CASES ===

    #[test]
    fn test_nav_empty_projects() {
        let mut app = App::new();
        assert!(app.projects.is_empty());
        app.dirty = false;

        app.nav_down();

        // Should not panic, dirty should remain false
        assert!(!app.dirty);
        assert_eq!(app.projects_list_state.selected(), Some(0));
    }

    #[test]
    fn test_select_project_empty_list() {
        let mut app = App::new();
        assert!(app.projects.is_empty());
        app.dirty = false;

        app.select_project();

        // Should not panic, screen should not change
        assert_eq!(app.screen, Screen::ProjectsList);
        assert!(app.current_project.is_none());
        assert!(!app.dirty);
    }

    #[test]
    fn test_multiple_state_changes() {
        let mut app = App::new();
        app.projects = vec![create_test_project("1", "P1", "Phase")];

        // Simulate user interaction sequence
        app.nav_down();
        assert!(app.dirty);
        app.mark_rendered();

        app.select_project();
        assert!(app.dirty);
        assert_eq!(app.screen, Screen::ProjectDetail);
        app.mark_rendered();

        app.back_to_projects();
        assert!(app.dirty);
        assert_eq!(app.screen, Screen::ProjectsList);
    }
}
