mutiny-diff 0.1.22

TUI git diff viewer with worktree management
use crate::theme::Theme;

use super::{
    AgentOutputsState, AgentSelectorState, AnnotationState, BookmarkState, ChecklistState,
    CommandBarState, DiffOptions, DiffState, FilePickerState, GlobalSearchState, NavigatorState,
    ReviewState, SelectionState, TextBuffer, WorktreeState,
};

use super::annotation_state::{AnnotationCategory, AnnotationSeverity};
use super::settings_state::SettingsState;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CategoryPickerPhase {
    SelectCategory,
    SelectSeverity,
}

/// Snapshot of an annotation for the annotation menu (owned to avoid borrow issues).
#[derive(Debug, Clone)]
pub struct AnnotationMenuItem {
    pub file_path: String,
    pub old_range: Option<(u32, u32)>,
    pub new_range: Option<(u32, u32)>,
    pub comment: String,
    pub category: AnnotationCategory,
    pub severity: AnnotationSeverity,
}

impl AnnotationMenuItem {
    /// Representative line for display, preferring new-file.
    pub fn sort_line(&self) -> u32 {
        self.new_range
            .map(|(s, _)| s)
            .or(self.old_range.map(|(s, _)| s))
            .unwrap_or(0)
    }

    /// Format a human-readable range string.
    pub fn range_text(&self) -> String {
        match (self.old_range, self.new_range) {
            (_, Some((s, e))) if s == e => format!("Line {s}"),
            (_, Some((s, e))) => format!("Lines {s}-{e}"),
            (Some((s, e)), None) if s == e => format!("Removed line {s} (old)"),
            (Some((s, e)), None) => format!("Removed lines {s}-{e} (old)"),
            (None, None) => "Line ?".to_string(),
        }
    }
}

/// Context for editing an existing annotation (set when user presses `e` in annotation menu).
#[derive(Debug, Clone)]
pub struct EditingAnnotation {
    pub file_path: String,
    pub old_range: Option<(u32, u32)>,
    pub new_range: Option<(u32, u32)>,
    pub old_comment: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ActiveView {
    DiffExplorer,
    WorktreeBrowser,
    AgentOutputs,
    FeedbackSummary,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusPanel {
    // DiffExplorer panels
    Navigator,
    DiffView,
    ReviewPanel,
    ChecklistPanel,
    // AgentOutputs panels
    AgentRunList,
    AgentOutput,
}

pub struct AppState {
    pub active_view: ActiveView,
    pub focus: FocusPanel,
    pub diff: DiffState,
    pub navigator: NavigatorState,
    pub worktree: WorktreeState,
    pub should_quit: bool,
    pub commit_dialog_open: bool,
    pub commit_message: TextBuffer,
    pub target_dialog_open: bool,
    pub target_dialog_input: TextBuffer,
    pub status_message: Option<(String, bool)>, // (message, is_error)
    pub target_label: String,
    pub hud_expanded: bool,

    // Visual selection
    pub selection: SelectionState,

    // Annotations
    pub annotations: AnnotationState,

    // Comment editor
    pub comment_editor_open: bool,
    pub comment_editor_text: TextBuffer,

    // Category/severity picker
    pub category_picker_open: bool,
    pub category_picker_phase: CategoryPickerPhase,
    pub pending_category: Option<AnnotationCategory>,
    pub pending_severity: Option<AnnotationSeverity>,

    // Prompt preview
    pub prompt_preview_visible: bool,
    pub prompt_preview_text: String,

    // Annotation menu
    pub annotation_menu_open: bool,
    pub annotation_menu_items: Vec<AnnotationMenuItem>,
    pub annotation_menu_selected: usize,
    pub editing_annotation: Option<EditingAnnotation>,

    // Agent
    pub agent_outputs: AgentOutputsState,
    pub agent_selector: AgentSelectorState,

    // PTY focus mode
    pub pty_focus: bool,

    // Review state tracking
    pub review: ReviewState,

    // Restore confirm
    pub restore_confirm_open: bool,

    // Theme
    pub theme: Theme,

    // Settings modal
    pub settings: SettingsState,

    // Which-key overlay
    pub which_key_visible: bool,

    // Global search across all diff content
    pub global_search: GlobalSearchState,

    // Feedback summary
    pub feedback_summary_scroll: usize,

    // Checklist
    pub checklist: ChecklistState,

    // Bookmarks
    pub bookmarks: BookmarkState,

    // Command bar (`:` vim-style)
    pub command_bar: CommandBarState,

    // File picker (Ctrl+P)
    pub file_picker: FilePickerState,

    // Agentic review
    pub agentic_review_modal_open: bool, // kept for KeyContext compat, always false now
    pub agentic_review_composing: bool,
    pub agentic_review_text: TextBuffer,
    pub agentic_review_running: bool,
    pub agentic_review_panel_open: bool,
    pub agentic_review_stream_output: String,
    pub agentic_review_child_done: usize,
    pub agentic_review_child_total: usize,
    pub agentic_review_scroll: usize,
    pub agentic_review_auto_scroll: bool,
}

impl AppState {
    pub fn new(diff_options: DiffOptions, theme: Theme) -> Self {
        Self {
            active_view: ActiveView::DiffExplorer,
            focus: FocusPanel::Navigator,
            diff: DiffState::new(diff_options),
            navigator: NavigatorState::new(),
            worktree: WorktreeState::new(),
            should_quit: false,
            commit_dialog_open: false,
            commit_message: TextBuffer::new(),
            target_dialog_open: false,
            target_dialog_input: TextBuffer::new(),
            status_message: None,
            target_label: String::new(),
            hud_expanded: false,
            selection: SelectionState::default(),
            annotations: AnnotationState::default(),
            comment_editor_open: false,
            comment_editor_text: TextBuffer::new(),
            category_picker_open: false,
            category_picker_phase: CategoryPickerPhase::SelectCategory,
            pending_category: None,
            pending_severity: None,
            prompt_preview_visible: false,
            prompt_preview_text: String::new(),
            annotation_menu_open: false,
            annotation_menu_items: Vec::new(),
            annotation_menu_selected: 0,
            editing_annotation: None,
            agent_outputs: AgentOutputsState::default(),
            agent_selector: AgentSelectorState::default(),
            pty_focus: false,
            review: ReviewState::default(),
            restore_confirm_open: false,
            theme,
            settings: SettingsState::default(),
            global_search: GlobalSearchState::default(),
            feedback_summary_scroll: 0,
            which_key_visible: false,
            checklist: ChecklistState::new(),
            bookmarks: BookmarkState::new(),
            command_bar: CommandBarState::new(),
            file_picker: FilePickerState::new(),
            agentic_review_modal_open: false,
            agentic_review_composing: false,
            agentic_review_text: TextBuffer::new(),
            agentic_review_running: false,
            agentic_review_panel_open: false,
            agentic_review_stream_output: String::new(),
            agentic_review_child_done: 0,
            agentic_review_child_total: 0,
            agentic_review_scroll: 0,
            agentic_review_auto_scroll: true,
        }
    }
}