//! File/directory browser modal widget.

use crate::theme;
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::Frame;

use super::ModalFrame;

/// Modal listing files and directories with a filter input line.
pub struct FileBrowserModal<'a> {
    title: &'a str,
    input: &'a str,
    entries: &'a [(String, bool, bool)],
    audio_counts: &'a [Option<usize>],
    selected: usize,
    scroll_offset: usize,
    border_color: Option<Color>,
    width: u16,
    height: u16,
    hints: Option<Line<'a>>,
    color_path: bool,
}

impl<'a> FileBrowserModal<'a> {
    pub fn new(title: &'a str, input: &'a str, entries: &'a [(String, bool, bool)]) -> Self {
        Self {
            title,
            input,
            entries,
            audio_counts: &[],
            selected: 0,
            scroll_offset: 0,
            border_color: None,
            width: 60,
            height: 16,
            hints: None,
            color_path: false,
        }
    }

    pub fn selected(mut self, idx: usize) -> Self {
        self.selected = idx;
        self
    }

    pub fn scroll_offset(mut self, offset: usize) -> Self {
        self.scroll_offset = offset;
        self
    }

    pub fn border_color(mut self, c: Color) -> Self {
        self.border_color = Some(c);
        self
    }

    pub fn width(mut self, w: u16) -> Self {
        self.width = w;
        self
    }

    pub fn height(mut self, h: u16) -> Self {
        self.height = h;
        self
    }

    pub fn hints(mut self, hints: Line<'a>) -> Self {
        self.hints = Some(hints);
        self
    }

    pub fn audio_counts(mut self, counts: &'a [Option<usize>]) -> Self {
        self.audio_counts = counts;
        self
    }

    pub fn color_path(mut self) -> Self {
        self.color_path = true;
        self
    }

    pub fn render_centered(self, frame: &mut Frame, term: Rect) -> Rect {
        let colors = theme::get();
        let border_color = self.border_color.unwrap_or(colors.ui.text_primary);

        let inner = ModalFrame::new(self.title)
            .width(self.width)
            .height(self.height)
            .border_color(border_color)
            .render_centered(frame, term);

        let has_hints = self.hints.is_some();
        let constraints = if has_hints {
            vec![
                Constraint::Length(1),
                Constraint::Min(1),
                Constraint::Length(1),
            ]
        } else {
            vec![Constraint::Length(1), Constraint::Min(1)]
        };
        let rows = Layout::vertical(constraints).split(inner);

        // Input line
        let input_spans = if self.color_path {
            let (path_part, filter_part) = match self.input.rfind('/') {
                Some(pos) => (&self.input[..=pos], &self.input[pos + 1..]),
                None => ("", self.input),
            };
            vec![
                Span::raw("> "),
                Span::styled(path_part.to_string(), Style::new().fg(colors.browser.directory)),
                Span::styled(filter_part.to_string(), Style::new().fg(colors.input.text)),
                Span::styled("█", Style::new().fg(colors.input.cursor)),
            ]
        } else {
            vec![
                Span::raw("> "),
                Span::styled(self.input, Style::new().fg(colors.input.text)),
                Span::styled("█", Style::new().fg(colors.input.cursor)),
            ]
        };
        frame.render_widget(Paragraph::new(Line::from(input_spans)), rows[0]);

        // Hints bar
        if let Some(hints) = self.hints {
            let hint_row = rows[2];
            frame.render_widget(
                Paragraph::new(hints).alignment(ratatui::layout::Alignment::Right),
                hint_row,
            );
        }

        // Entries list
        let visible_height = rows[1].height as usize;
        let visible_entries = self
            .entries
            .iter()
            .enumerate()
            .skip(self.scroll_offset)
            .take(visible_height);

        let lines: Vec<Line> = visible_entries
            .map(|(abs_idx, (name, is_dir, is_cagire))| {
                let is_selected = abs_idx == self.selected;
                let prefix = if is_selected { "> " } else { "  " };
                let color = if is_selected {
                    colors.browser.selected
                } else if *is_dir {
                    colors.browser.directory
                } else if *is_cagire {
                    colors.browser.project_file
                } else {
                    colors.browser.file
                };
                let display = if *is_dir {
                    format!("{prefix}{name}/")
                } else {
                    format!("{prefix}{name}")
                };
                let mut spans = vec![Span::styled(display, Style::new().fg(color))];
                if *is_dir && name != ".." {
                    if let Some(Some(count)) = self.audio_counts.get(abs_idx) {
                        spans.push(Span::styled(
                            format!("  ({count})"),
                            Style::new().fg(colors.browser.file),
                        ));
                    }
                }
                Line::from(spans)
            })
            .collect();

        frame.render_widget(Paragraph::new(lines), rows[1]);

        inner
    }
}
