//! Scope picker overlay — modal project filter selector.
//!
//! Renders a centered modal listing all available projects. The user
//! selects "All Projects" or a specific project to filter all screens.

use ftui::core::geometry::Rect;
use ftui::render::cell::{Cell, PackedRgba};
use ftui::render::drawing::{BorderChars, Draw};
use ftui::render::frame::Frame;

use crate::state::ScopeContext;
use crate::state::scope_picker::ScopePickerState;

use super::{ACCENT, BG_SURFACE, BORDER, TEXT, TEXT_MUTED};

/// Selection highlight background.
const SELECTION_BG: PackedRgba = PackedRgba::rgb(0x3A, 0x3A, 0x34);

// ---------------------------------------------------------------------------
// render_scope_picker
// ---------------------------------------------------------------------------

/// Render the scope picker overlay centered on the screen.
///
/// Only renders if `state.visible`. The modal is 50% width, up to 40x20.
pub fn render_scope_picker(
    frame: &mut Frame<'_>,
    state: &ScopePickerState,
    current_scope: &ScopeContext,
    area: Rect,
) {
    if !state.visible {
        return;
    }
    if area.height < 5 || area.width < 20 {
        return;
    }

    // Modal dimensions.
    let modal_width = (area.width / 2).clamp(25, 40);
    let row_count = state.row_count();
    // +3 for border top, title gap, border bottom.
    let modal_height = ((row_count + 3) as u16).clamp(5, 20).min(area.height - 2);

    let modal_x = area.x + (area.width.saturating_sub(modal_width)) / 2;
    let modal_y = area.y + (area.height.saturating_sub(modal_height)) / 2;
    let modal_rect = Rect::new(modal_x, modal_y, modal_width, modal_height);

    // Clear background.
    let bg_cell = Cell {
        fg: TEXT,
        bg: BG_SURFACE,
        ..Cell::default()
    };
    for y in modal_rect.y..modal_rect.bottom() {
        for x in modal_rect.x..modal_rect.right() {
            frame.buffer.set(x, y, bg_cell);
        }
    }

    // Border.
    let border_cell = Cell {
        fg: BORDER,
        bg: BG_SURFACE,
        ..Cell::default()
    };
    frame.draw_border(modal_rect, BorderChars::ROUNDED, border_cell);

    // Title.
    let title = " Project Scope ";
    let title_x = modal_x + (modal_width.saturating_sub(title.len() as u16)) / 2;
    let title_cell = Cell {
        fg: ACCENT,
        bg: BG_SURFACE,
        ..Cell::default()
    };
    frame.print_text_clipped(title_x, modal_y, title, title_cell, modal_rect.right());

    // Content area (inside border).
    let content_x = modal_x + 1;
    let content_max_x = modal_rect.right().saturating_sub(1);
    let content_width = content_max_x.saturating_sub(content_x);
    let first_row_y = modal_y + 1;
    let max_rows = (modal_height.saturating_sub(2)) as usize; // Inside borders.

    // Render rows.
    let visible_end = (state.scroll_offset + max_rows).min(row_count);
    for vis_idx in 0..max_rows {
        let row_idx = state.scroll_offset + vis_idx;
        if row_idx >= row_count {
            break;
        }

        let y = first_row_y + vis_idx as u16;
        let selected = row_idx == state.selected_index;

        let bg = if selected { SELECTION_BG } else { BG_SURFACE };

        // Fill row background.
        if selected {
            let sel_cell = Cell {
                fg: TEXT,
                bg,
                ..Cell::default()
            };
            for x in content_x..content_max_x {
                frame.buffer.set(x, y, sel_cell);
            }
        }

        // Row content.
        let (label, is_active) = if row_idx == 0 {
            let active = current_scope.project_id.is_none();
            ("All Projects".to_string(), active)
        } else {
            let project = &state.projects[row_idx - 1];
            let active = current_scope.project_id == Some(project.id);
            (project.path.clone(), active)
        };

        // Active indicator.
        let prefix = if is_active { "> " } else { "  " };

        let fg = if is_active { ACCENT } else { TEXT };
        let cell = Cell {
            fg,
            bg,
            ..Cell::default()
        };

        // Truncate label to fit.
        let max_label_len = content_width.saturating_sub(2) as usize; // 2 for prefix
        let display = if label.len() > max_label_len {
            format!(
                "{prefix}{}...",
                &label[..label.floor_char_boundary(max_label_len.saturating_sub(3))]
            )
        } else {
            format!("{prefix}{label}")
        };

        frame.print_text_clipped(content_x, y, &display, cell, content_max_x);
    }

    // Scroll indicators.
    if state.scroll_offset > 0 {
        let arrow_cell = Cell {
            fg: TEXT_MUTED,
            bg: BG_SURFACE,
            ..Cell::default()
        };
        frame.print_text_clipped(
            content_max_x.saturating_sub(1),
            first_row_y,
            "^",
            arrow_cell,
            modal_rect.right(),
        );
    }
    if visible_end < row_count {
        let arrow_cell = Cell {
            fg: TEXT_MUTED,
            bg: BG_SURFACE,
            ..Cell::default()
        };
        let bottom_y = first_row_y + (max_rows as u16).saturating_sub(1);
        frame.print_text_clipped(
            content_max_x.saturating_sub(1),
            bottom_y,
            "v",
            arrow_cell,
            modal_rect.right(),
        );
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::scope::ProjectInfo;
    use ftui::render::grapheme_pool::GraphemePool;

    macro_rules! with_frame {
        ($width:expr, $height:expr, |$frame:ident| $body:block) => {{
            let mut pool = GraphemePool::new();
            let mut $frame = Frame::new($width, $height, &mut pool);
            $body
        }};
    }

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

    #[test]
    fn test_render_hidden_noop() {
        with_frame!(80, 24, |frame| {
            let state = ScopePickerState::default();
            let scope = ScopeContext::default();
            let area = frame.bounds();
            render_scope_picker(&mut frame, &state, &scope, area);
            // Should not panic.
        });
    }

    #[test]
    fn test_render_visible_no_panic() {
        with_frame!(80, 24, |frame| {
            let mut state = ScopePickerState::default();
            let scope = ScopeContext::default();
            state.open(sample_projects(), &scope);
            let area = frame.bounds();
            render_scope_picker(&mut frame, &state, &scope, area);
        });
    }

    #[test]
    fn test_render_with_selection() {
        with_frame!(80, 24, |frame| {
            let mut state = ScopePickerState::default();
            let scope = ScopeContext::default();
            state.open(sample_projects(), &scope);
            state.select_next(); // Move to first project
            let area = frame.bounds();
            render_scope_picker(&mut frame, &state, &scope, area);
        });
    }

    #[test]
    fn test_render_tiny_terminal_noop() {
        with_frame!(15, 4, |frame| {
            let mut state = ScopePickerState::default();
            let scope = ScopeContext::default();
            state.open(sample_projects(), &scope);
            let area = frame.bounds();
            render_scope_picker(&mut frame, &state, &scope, area);
            // Should not panic on tiny terminals.
        });
    }

    #[test]
    fn test_render_active_scope_highlighted() {
        with_frame!(80, 24, |frame| {
            let mut state = ScopePickerState::default();
            let scope = ScopeContext {
                project_id: Some(2),
                project_name: Some("beta/repo".into()),
            };
            state.open(sample_projects(), &scope);
            let area = frame.bounds();
            render_scope_picker(&mut frame, &state, &scope, area);
        });
    }

    #[test]
    fn test_render_empty_project_list() {
        with_frame!(80, 24, |frame| {
            let mut state = ScopePickerState::default();
            let scope = ScopeContext::default();
            state.open(vec![], &scope);
            let area = frame.bounds();
            render_scope_picker(&mut frame, &state, &scope, area);
            // Only "All Projects" row, should not panic.
        });
    }
}
