//! Tree-view sample browser with search filtering.

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

/// Node type in the sample tree.
#[derive(Clone, Copy)]
pub enum TreeLineKind {
    Root { expanded: bool },
    Folder { expanded: bool },
    File,
}

/// A single row in the sample browser tree.
#[derive(Clone)]
pub struct TreeLine {
    pub depth: u8,
    pub kind: TreeLineKind,
    pub label: String,
    pub folder: String,
    pub index: usize,
}

/// Tree-view browser for navigating sample folders.
pub struct SampleBrowser<'a> {
    entries: &'a [TreeLine],
    cursor: usize,
    scroll_offset: usize,
    search_query: &'a str,
    search_active: bool,
    focused: bool,
}

impl<'a> SampleBrowser<'a> {
    pub fn new(entries: &'a [TreeLine], cursor: usize) -> Self {
        Self {
            entries,
            cursor,
            scroll_offset: 0,
            search_query: "",
            search_active: false,
            focused: false,
        }
    }

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

    pub fn search(mut self, query: &'a str, active: bool) -> Self {
        self.search_query = query;
        self.search_active = active;
        self
    }

    pub fn focused(mut self, focused: bool) -> Self {
        self.focused = focused;
        self
    }

    pub fn render(self, frame: &mut Frame, area: Rect) {
        let colors = theme::get();
        let border_style = if self.focused {
            Style::new().fg(colors.browser.focused_border)
        } else {
            Style::new().fg(colors.browser.unfocused_border)
        };

        let block = Block::default()
            .borders(Borders::ALL)
            .border_style(border_style)
            .title(" Samples ");

        let inner = block.inner(area);
        frame.render_widget(block, area);

        if inner.height == 0 || inner.width == 0 {
            return;
        }

        let show_search = self.search_active || !self.search_query.is_empty();
        let (search_area, list_area) = if show_search {
            let [s, l] = Layout::vertical([
                Constraint::Length(1),
                Constraint::Fill(1),
            ])
            .areas(inner);
            (Some(s), l)
        } else {
            (None, inner)
        };

        if let Some(sa) = search_area {
            self.render_search(frame, sa, &colors);
        }
        self.render_tree(frame, list_area, &colors);
    }

    fn render_search(&self, frame: &mut Frame, area: Rect, colors: &theme::ThemeColors) {
        let style = if self.search_active {
            Style::new().fg(colors.search.active)
        } else {
            Style::new().fg(colors.search.inactive)
        };
        let cursor = if self.search_active { "_" } else { "" };
        let text = format!("/{}{}", self.search_query, cursor);
        let line = Line::from(Span::styled(text, style));
        frame.render_widget(Paragraph::new(vec![line]), area);
    }

    fn render_tree(&self, frame: &mut Frame, area: Rect, colors: &theme::ThemeColors) {
        let height = area.height as usize;
        if self.entries.is_empty() {
            if self.search_query.is_empty() {
                self.render_empty_guide(frame, area, colors);
            } else {
                let line =
                    Line::from(Span::styled("No matches", Style::new().fg(colors.browser.empty_text)));
                frame.render_widget(Paragraph::new(vec![line]), area);
            }
            return;
        }

        let visible_end = (self.scroll_offset + height).min(self.entries.len());
        let mut lines: Vec<Line> = Vec::with_capacity(height);

        for i in self.scroll_offset..visible_end {
            let entry = &self.entries[i];
            let is_cursor = i == self.cursor;
            let indent = "  ".repeat(entry.depth as usize);

            let (icon, icon_color) = match entry.kind {
                TreeLineKind::Root { expanded: true } | TreeLineKind::Folder { expanded: true } => {
                    ("\u{25BC} ", colors.browser.folder_icon)
                }
                TreeLineKind::Root { expanded: false }
                | TreeLineKind::Folder { expanded: false } => ("\u{25B6} ", colors.browser.folder_icon),
                TreeLineKind::File => ("\u{266A} ", colors.browser.file_icon),
            };

            let label_style = if is_cursor && self.focused {
                Style::new().fg(colors.browser.selected).add_modifier(Modifier::BOLD)
            } else if is_cursor {
                Style::new().fg(colors.browser.file)
            } else {
                match entry.kind {
                    TreeLineKind::Root { .. } => {
                        Style::new().fg(colors.browser.root).add_modifier(Modifier::BOLD)
                    }
                    TreeLineKind::Folder { .. } => Style::new().fg(colors.browser.directory),
                    TreeLineKind::File => Style::default(),
                }
            };

            let icon_style = if is_cursor && self.focused {
                label_style
            } else {
                Style::new().fg(icon_color)
            };

            let mut spans = vec![
                Span::raw(indent),
                Span::styled(icon, icon_style),
                Span::styled(&entry.label, label_style),
            ];

            if matches!(entry.kind, TreeLineKind::File) {
                let idx_style = Style::new().fg(colors.browser.empty_text);
                spans.push(Span::styled(format!(" {}", entry.index), idx_style));
            }

            lines.push(Line::from(spans));
        }

        frame.render_widget(Paragraph::new(lines), area);
    }

    fn render_empty_guide(&self, frame: &mut Frame, area: Rect, colors: &theme::ThemeColors) {
        let muted = Style::new().fg(colors.browser.empty_text);
        let heading = Style::new().fg(colors.ui.text_primary);
        let key = Style::new().fg(colors.hint.key);
        let desc = Style::new().fg(colors.hint.text);
        let code = Style::new().fg(colors.ui.accent);

        let lines = vec![
            Line::from(Span::styled(" No samples loaded.", muted)),
            Line::from(""),
            Line::from(Span::styled(" Load from the Engine page:", heading)),
            Line::from(""),
            Line::from(vec![
                Span::styled("   F6  ", key),
                Span::styled("Go to Engine page", desc),
            ]),
            Line::from(vec![
                Span::styled("    A  ", key),
                Span::styled("Add a sample folder", desc),
            ]),
            Line::from(""),
            Line::from(Span::styled(" Organize samples like this:", heading)),
            Line::from(""),
            Line::from(Span::styled("   samples/", code)),
            Line::from(Span::styled("   \u{251C}\u{2500}\u{2500} kick/", code)),
            Line::from(Span::styled("   \u{2502}   \u{2514}\u{2500}\u{2500} kick.wav", code)),
            Line::from(Span::styled("   \u{251C}\u{2500}\u{2500} snare/", code)),
            Line::from(Span::styled("   \u{2502}   \u{2514}\u{2500}\u{2500} snare.wav", code)),
            Line::from(Span::styled("   \u{2514}\u{2500}\u{2500} hats/", code)),
            Line::from(Span::styled("       \u{251C}\u{2500}\u{2500} closed.wav", code)),
            Line::from(Span::styled("       \u{251C}\u{2500}\u{2500} open.wav", code)),
            Line::from(Span::styled("       \u{2514}\u{2500}\u{2500} pedal.wav", code)),
            Line::from(""),
            Line::from(Span::styled(" Folders become Forth words:", heading)),
            Line::from(""),
            Line::from(Span::styled("   kick sound .", code)),
            Line::from(Span::styled("   hats sound 2 n .", code)),
            Line::from(Span::styled("   snare sound 0.5 speed .", code)),
        ];

        frame.render_widget(Paragraph::new(lines).wrap(Wrap { trim: false }), area);
    }
}
