//! Scope picker overlay state.
//!
//! The scope picker lets users filter all screens to a specific project.
//! It appears as a modal overlay when the user presses `P`.

use crate::scope::ProjectInfo;
use crate::state::ScopeContext;

/// State for the scope picker overlay.
#[derive(Debug, Default)]
pub struct ScopePickerState {
    /// Available projects (populated on open).
    pub projects: Vec<ProjectInfo>,
    /// Currently highlighted index (0 = "All Projects", 1..N = specific projects).
    pub selected_index: usize,
    /// Whether the picker overlay is visible.
    pub visible: bool,
    /// Scroll offset for long project lists.
    pub scroll_offset: usize,
}

/// Max visible rows in the picker before scrolling kicks in.
const MAX_VISIBLE_ROWS: usize = 15;

impl ScopePickerState {
    /// Open the picker with the given project list.
    ///
    /// Pre-selects the row matching the current scope, or "All Projects" (index 0)
    /// if no project filter is active.
    pub fn open(&mut self, projects: Vec<ProjectInfo>, current_scope: &ScopeContext) {
        self.projects = projects;
        self.visible = true;
        self.scroll_offset = 0;

        // Pre-select the currently active scope.
        self.selected_index = match current_scope.project_id {
            None => 0, // "All Projects" row
            Some(id) => self
                .projects
                .iter()
                .position(|p| p.id == id)
                .map_or(0, |i| i + 1), // +1 because index 0 is "All Projects"
        };

        self.ensure_visible();
    }

    /// Close the picker without changing scope.
    pub fn close(&mut self) {
        self.visible = false;
    }

    /// Move selection up.
    pub fn select_prev(&mut self) {
        if self.selected_index > 0 {
            self.selected_index -= 1;
            self.ensure_visible();
        }
    }

    /// Move selection down.
    pub fn select_next(&mut self) {
        let max_index = self.projects.len(); // 0="All" + N projects
        if self.selected_index < max_index {
            self.selected_index += 1;
            self.ensure_visible();
        }
    }

    /// Confirm the current selection and return the new scope.
    #[must_use]
    pub fn confirm(&self) -> ScopeContext {
        if self.selected_index == 0 {
            // "All Projects"
            ScopeContext {
                project_id: None,
                project_name: None,
            }
        } else {
            let project = &self.projects[self.selected_index - 1];
            ScopeContext {
                project_id: Some(project.id),
                project_name: Some(project.path.clone()),
            }
        }
    }

    /// Total number of rows (1 for "All" + project count).
    #[must_use]
    pub fn row_count(&self) -> usize {
        1 + self.projects.len()
    }

    /// Ensure the selected index is within the visible scroll window.
    fn ensure_visible(&mut self) {
        if self.selected_index < self.scroll_offset {
            self.scroll_offset = self.selected_index;
        } else if self.selected_index >= self.scroll_offset + MAX_VISIBLE_ROWS {
            self.scroll_offset = self.selected_index.saturating_sub(MAX_VISIBLE_ROWS - 1);
        }
    }
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

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

    fn sample_projects() -> Vec<ProjectInfo> {
        vec![
            ProjectInfo {
                id: 1,
                path: "alpha/repo".into(),
            },
            ProjectInfo {
                id: 2,
                path: "beta/repo".into(),
            },
            ProjectInfo {
                id: 3,
                path: "gamma/repo".into(),
            },
        ]
    }

    #[test]
    fn test_open_no_scope_selects_all() {
        let mut picker = ScopePickerState::default();
        let scope = ScopeContext::default();
        picker.open(sample_projects(), &scope);

        assert!(picker.visible);
        assert_eq!(picker.selected_index, 0); // "All Projects"
        assert_eq!(picker.projects.len(), 3);
    }

    #[test]
    fn test_open_with_scope_preselects_project() {
        let mut picker = ScopePickerState::default();
        let scope = ScopeContext {
            project_id: Some(2),
            project_name: Some("beta/repo".into()),
        };
        picker.open(sample_projects(), &scope);

        assert_eq!(picker.selected_index, 2); // index 1 in projects = index 2 in picker
    }

    #[test]
    fn test_select_prev_and_next() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());

        picker.select_next();
        assert_eq!(picker.selected_index, 1);
        picker.select_next();
        assert_eq!(picker.selected_index, 2);
        picker.select_prev();
        assert_eq!(picker.selected_index, 1);
    }

    #[test]
    fn test_select_prev_at_zero_stays() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());

        picker.select_prev();
        assert_eq!(picker.selected_index, 0);
    }

    #[test]
    fn test_select_next_at_max_stays() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());

        // 4 total rows (All + 3 projects), max index = 3
        for _ in 0..10 {
            picker.select_next();
        }
        assert_eq!(picker.selected_index, 3);
    }

    #[test]
    fn test_confirm_all_projects() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());

        let scope = picker.confirm();
        assert!(scope.project_id.is_none());
        assert!(scope.project_name.is_none());
    }

    #[test]
    fn test_confirm_specific_project() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());

        picker.select_next(); // index 1 = first project (alpha/repo, id=1)
        let scope = picker.confirm();
        assert_eq!(scope.project_id, Some(1));
        assert_eq!(scope.project_name.as_deref(), Some("alpha/repo"));
    }

    #[test]
    fn test_close_hides_picker() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());
        assert!(picker.visible);

        picker.close();
        assert!(!picker.visible);
    }

    #[test]
    fn test_row_count() {
        let mut picker = ScopePickerState::default();
        picker.open(sample_projects(), &ScopeContext::default());
        assert_eq!(picker.row_count(), 4); // "All" + 3 projects
    }

    #[test]
    fn test_open_with_unknown_project_selects_all() {
        let mut picker = ScopePickerState::default();
        let scope = ScopeContext {
            project_id: Some(999), // Not in list
            project_name: Some("unknown".into()),
        };
        picker.open(sample_projects(), &scope);
        assert_eq!(picker.selected_index, 0); // Falls back to "All"
    }
}
