use std::path::{Path, PathBuf};

use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::Rect;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Paragraph, Wrap};
use ratatui::Frame;

use crate::action::Action;
use crate::components::{ui_utils, Component};
use crate::theme::theme;

/// Different kinds of popups
#[derive(Debug, Clone)]
pub enum PopupKind {
    /// Confirmation dialog (yes/no)
    Confirm {
        message: String,
        on_confirm: Box<Action>,
    },
    /// Text input dialog with optional autocompletion
    Input {
        prompt: String,
        input: String,
        cursor_pos: usize,
        on_submit: InputTarget,
        /// Completion matches from last Tab press
        completions: Vec<String>,
        /// Currently selected completion index
        completion_idx: Option<usize>,
        /// Static list of candidates for list-based autocompletion (e.g. plugin names).
        /// Empty means use path-based autocompletion (for AddPath) or no completion.
        candidates: Vec<String>,
    },
    /// Info/error message
    Message {
        title: String,
        message: String,
        is_error: bool,
    },
    /// Directory browser for selecting a path
    FileBrowser {
        current_dir: PathBuf,
        /// All directory entries. First is "..", then dirs sorted alphabetically.
        entries: Vec<DirEntry>,
        selected: usize,
        filter_active: bool,
        filter_query: String,
    },
}

#[derive(Debug, Clone)]
pub struct DirEntry {
    pub name: String,
    pub path: PathBuf,
    pub is_dir: bool,
}

/// What to do with the submitted input
#[derive(Debug, Clone)]
pub enum InputTarget {
    AddPath,
    AddConfigRule,
    EditConfigField(usize, usize), // rule_index, field_index
    EditConfigPattern(usize),      // rule_index
}

pub struct Popup {
    pub kind: Option<PopupKind>,
}

impl Popup {
    pub fn new() -> Self {
        Self { kind: None }
    }

    pub fn is_open(&self) -> bool {
        self.kind.is_some()
    }

    pub fn show_confirm(&mut self, message: String, on_confirm: Action) {
        self.kind = Some(PopupKind::Confirm {
            message,
            on_confirm: Box::new(on_confirm),
        });
    }

    pub fn show_input(&mut self, prompt: String, target: InputTarget) {
        // Pre-fill with home directory to give a nice starting point
        let home = dirs::home_dir().unwrap_or_default();
        let initial = format!("{}/", home.display());
        let cursor = initial.len();
        self.kind = Some(PopupKind::Input {
            prompt,
            input: initial,
            cursor_pos: cursor,
            on_submit: target,
            completions: Vec::new(),
            completion_idx: None,
            candidates: Vec::new(),
        });
    }

    pub fn show_input_plain(&mut self, prompt: String, initial: String, target: InputTarget) {
        let cursor = initial.len();
        self.kind = Some(PopupKind::Input {
            prompt,
            input: initial,
            cursor_pos: cursor,
            on_submit: target,
            completions: Vec::new(),
            completion_idx: None,
            candidates: Vec::new(),
        });
    }

    /// Open an input popup with a list of autocompletion candidates (e.g. plugin names)
    pub fn show_input_with_candidates(
        &mut self,
        prompt: String,
        initial: String,
        target: InputTarget,
        candidates: Vec<String>,
    ) {
        let cursor = initial.len();
        self.kind = Some(PopupKind::Input {
            prompt,
            input: initial,
            cursor_pos: cursor,
            on_submit: target,
            completions: Vec::new(),
            completion_idx: None,
            candidates,
        });
    }

    pub fn show_file_browser(&mut self) {
        let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("/"));
        let entries = list_directory(&home);
        self.kind = Some(PopupKind::FileBrowser {
            current_dir: home,
            entries,
            selected: 0,
            filter_active: false,
            filter_query: String::new(),
        });
    }

    pub fn show_message(&mut self, title: String, message: String, is_error: bool) {
        self.kind = Some(PopupKind::Message {
            title,
            message,
            is_error,
        });
    }

    pub fn close(&mut self) {
        self.kind = None;
    }
}

impl Component for Popup {
    fn handle_key(&mut self, key: KeyEvent) -> Action {
        let Some(ref mut kind) = self.kind else {
            return Action::Noop;
        };

        match kind {
            PopupKind::Confirm { on_confirm, .. } => match key.code {
                KeyCode::Char('y') | KeyCode::Char('Y') | KeyCode::Enter => {
                    let action = *on_confirm.clone();
                    self.close();
                    action
                }
                KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => {
                    self.close();
                    Action::Noop
                }
                _ => Action::Noop,
            },
            PopupKind::Input {
                input,
                cursor_pos,
                on_submit,
                completions,
                completion_idx,
                candidates,
                ..
            } => match key.code {
                KeyCode::Enter => {
                    let value = input.clone();
                    let target = on_submit.clone();
                    self.close();
                    match target {
                        InputTarget::AddPath => Action::AddPath(PathBuf::from(value)),
                        InputTarget::AddConfigRule => Action::AddConfigRule(value),
                        InputTarget::EditConfigField(rule_idx, field_idx) => {
                            Action::EditConfigField(rule_idx, field_idx, value)
                        }
                        InputTarget::EditConfigPattern(rule_idx) => {
                            Action::EditConfigPattern(rule_idx, value)
                        }
                    }
                }
                KeyCode::Esc => {
                    self.close();
                    Action::Noop
                }
                KeyCode::Tab => {
                    if !candidates.is_empty() {
                        // List-based autocompletion (plugin names, etc.)
                        list_autocomplete(
                            input,
                            cursor_pos,
                            completions,
                            completion_idx,
                            candidates,
                        );
                    } else if matches!(on_submit, InputTarget::AddPath) {
                        // Path autocompletion (filesystem)
                        path_autocomplete(input, cursor_pos, completions, completion_idx);
                    }
                    // EditConfigField: no autocompletion
                    Action::Noop
                }
                KeyCode::BackTab => {
                    // Reverse cycle through completions
                    if !completions.is_empty() {
                        let idx = completion_idx.unwrap_or(0);
                        let new_idx = if idx == 0 {
                            completions.len() - 1
                        } else {
                            idx - 1
                        };
                        *completion_idx = Some(new_idx);
                        *input = completions[new_idx].clone();
                        *cursor_pos = input.len();
                    }
                    Action::Noop
                }
                KeyCode::Char(c) => {
                    input.insert(*cursor_pos, c);
                    *cursor_pos += c.len_utf8();
                    // Clear completions when typing
                    completions.clear();
                    *completion_idx = None;
                    Action::Noop
                }
                KeyCode::Backspace => {
                    if *cursor_pos > 0 {
                        let prev_char = input[..*cursor_pos].chars().last().unwrap();
                        *cursor_pos -= prev_char.len_utf8();
                        input.remove(*cursor_pos);
                    }
                    completions.clear();
                    *completion_idx = None;
                    Action::Noop
                }
                KeyCode::Left => {
                    if *cursor_pos > 0 {
                        let prev_char = input[..*cursor_pos].chars().last().unwrap();
                        *cursor_pos -= prev_char.len_utf8();
                    }
                    Action::Noop
                }
                KeyCode::Right => {
                    if *cursor_pos < input.len() {
                        let next_char = input[*cursor_pos..].chars().next().unwrap();
                        *cursor_pos += next_char.len_utf8();
                    }
                    Action::Noop
                }
                KeyCode::Home => {
                    *cursor_pos = 0;
                    Action::Noop
                }
                KeyCode::End => {
                    *cursor_pos = input.len();
                    Action::Noop
                }
                _ => Action::Noop,
            },
            PopupKind::FileBrowser {
                current_dir,
                entries,
                selected,
                filter_active,
                filter_query,
            } => {
                // Helper: get filtered entry indices
                let filtered_indices = fb_filtered_indices(entries, filter_query);
                let filtered_len = filtered_indices.len();

                // Helper closure: navigate to directory, reset filter
                let navigate_to = |dir: PathBuf,
                                   current_dir: &mut PathBuf,
                                   entries: &mut Vec<DirEntry>,
                                   selected: &mut usize,
                                   filter_active: &mut bool,
                                   filter_query: &mut String| {
                    *entries = list_directory(&dir);
                    *current_dir = dir;
                    *selected = 0;
                    *filter_active = false;
                    filter_query.clear();
                };

                // ── Filter input mode (typing) ──
                if *filter_active {
                    match key.code {
                        KeyCode::Esc | KeyCode::Enter => {
                            // Freeze filter, return to navigation mode
                            *filter_active = false;
                            return Action::Noop;
                        }
                        KeyCode::Char(c) => {
                            filter_query.push(c);
                            *selected = 0;
                            return Action::Noop;
                        }
                        KeyCode::Backspace => {
                            if filter_query.is_empty() {
                                *filter_active = false;
                            } else {
                                filter_query.pop();
                                *selected = 0;
                            }
                            return Action::Noop;
                        }
                        KeyCode::Down => {
                            if filtered_len > 0 {
                                *selected = (*selected + 1).min(filtered_len - 1);
                            }
                            return Action::Noop;
                        }
                        KeyCode::Up => {
                            *selected = selected.saturating_sub(1);
                            return Action::Noop;
                        }
                        _ => return Action::Noop,
                    }
                }

                // ── Normal mode (also handles frozen filter) ──
                match key.code {
                    KeyCode::Char('j') | KeyCode::Down => {
                        if filtered_len > 0 {
                            *selected = (*selected + 1).min(filtered_len - 1);
                        }
                        Action::Noop
                    }
                    KeyCode::Char('k') | KeyCode::Up => {
                        *selected = selected.saturating_sub(1);
                        Action::Noop
                    }
                    KeyCode::Char('g') | KeyCode::Home => {
                        *selected = 0;
                        Action::Noop
                    }
                    KeyCode::Char('G') | KeyCode::End => {
                        if filtered_len > 0 {
                            *selected = filtered_len - 1;
                        }
                        Action::Noop
                    }
                    KeyCode::Char('u') | KeyCode::PageUp => {
                        *selected = selected.saturating_sub(10);
                        Action::Noop
                    }
                    KeyCode::Char('d') | KeyCode::PageDown => {
                        if filtered_len > 0 {
                            *selected = (*selected + 10).min(filtered_len - 1);
                        }
                        Action::Noop
                    }
                    KeyCode::Enter => {
                        // Enter selects the current directory
                        let path = current_dir.clone();
                        self.close();
                        Action::AddPath(path)
                    }
                    KeyCode::Char('l') | KeyCode::Right => {
                        // l/Right navigates into the selected directory
                        if let Some(&real_idx) = filtered_indices.get(*selected) {
                            if let Some(entry) = entries.get(real_idx) {
                                if entry.name == ".." {
                                    if let Some(parent) = current_dir.parent() {
                                        let parent = parent.to_path_buf();
                                        navigate_to(
                                            parent,
                                            current_dir,
                                            entries,
                                            selected,
                                            filter_active,
                                            filter_query,
                                        );
                                    }
                                } else if entry.is_dir {
                                    let new_dir = entry.path.clone();
                                    navigate_to(
                                        new_dir,
                                        current_dir,
                                        entries,
                                        selected,
                                        filter_active,
                                        filter_query,
                                    );
                                }
                            }
                        }
                        Action::Noop
                    }
                    KeyCode::Backspace | KeyCode::Char('h') | KeyCode::Left => {
                        if let Some(parent) = current_dir.parent() {
                            let parent = parent.to_path_buf();
                            navigate_to(
                                parent,
                                current_dir,
                                entries,
                                selected,
                                filter_active,
                                filter_query,
                            );
                        }
                        Action::Noop
                    }
                    KeyCode::Char('f') | KeyCode::Char('/') => {
                        *filter_active = true;
                        filter_query.clear();
                        *selected = 0;
                        Action::Noop
                    }
                    KeyCode::Esc => {
                        if !filter_query.is_empty() {
                            // Clear frozen filter, back to full list
                            filter_query.clear();
                            *selected = 0;
                        } else {
                            self.close();
                        }
                        Action::Noop
                    }
                    KeyCode::Char('q') => {
                        self.close();
                        Action::Noop
                    }
                    _ => Action::Noop,
                }
            }
            PopupKind::Message { .. } => match key.code {
                KeyCode::Enter | KeyCode::Esc | KeyCode::Char('q') => {
                    self.close();
                    Action::Noop
                }
                _ => Action::Noop,
            },
        }
    }

    fn render(&mut self, frame: &mut Frame, area: Rect) {
        let Some(ref kind) = self.kind else {
            return;
        };

        let t = theme();

        match kind {
            PopupKind::Confirm { message, .. } => {
                let popup_area = ui_utils::centered_rect(60, 30, area);
                frame.render_widget(Clear, popup_area);

                let lines = vec![
                    Line::from(""),
                    Line::from(vec![
                        Span::styled("  ", t.header),
                        Span::styled(message.as_str(), t.title),
                    ]),
                    Line::from(""),
                    Line::from(vec![
                        Span::raw("       "),
                        Span::styled(" y ", t.status_bar_key),
                        Span::styled(" Yes  ", t.popup_title),
                        Span::styled(" n ", t.status_bar_key),
                        Span::styled(" No  ", t.popup_title),
                    ]),
                ];

                let popup = Paragraph::new(lines)
                    .block(
                        Block::default()
                            .borders(Borders::ALL)
                            .border_style(t.popup_border)
                            .title(" Confirm ")
                            .title_style(t.popup_title),
                    )
                    .wrap(Wrap { trim: false });
                frame.render_widget(popup, popup_area);
            }
            PopupKind::Input {
                prompt,
                input,
                cursor_pos,
                on_submit,
                completions,
                completion_idx,
                ..
            } => {
                let popup_title = match on_submit {
                    InputTarget::AddPath => " Add Path ",
                    InputTarget::AddConfigRule => " Add Rule ",
                    InputTarget::EditConfigField(_, _) => " Edit Value ",
                    InputTarget::EditConfigPattern(_) => " Edit Pattern ",
                };

                // Determine popup height based on completions
                let comp_lines = completions.len().min(8);
                let base_height = 8u16;
                let total_height = base_height + comp_lines as u16;
                let popup_area = centered_rect_abs(
                    (area.width * 70 / 100).max(40),
                    total_height.min(area.height * 80 / 100),
                    area,
                );
                frame.render_widget(Clear, popup_area);

                let mut lines = vec![
                    Line::from(""),
                    Line::from(Span::styled(format!("  {}", prompt), t.header)),
                    Line::from(""),
                ];

                // Render input with cursor
                let (before, after) = input.split_at(*cursor_pos);
                let cursor_char = after.chars().next().unwrap_or(' ');
                let after_cursor = if after.is_empty() {
                    String::new()
                } else {
                    after[cursor_char.len_utf8()..].to_string()
                };

                lines.push(Line::from(vec![
                    Span::raw("  > "),
                    Span::raw(before.to_string()),
                    Span::styled(
                        cursor_char.to_string(),
                        ratatui::style::Style::new()
                            .bg(ratatui::style::Color::Cyan)
                            .fg(ratatui::style::Color::Black),
                    ),
                    Span::raw(after_cursor),
                ]));

                lines.push(Line::from(""));

                // Show completions
                if !completions.is_empty() {
                    let display_count = completions.len().min(8);
                    for (i, comp) in completions.iter().take(display_count).enumerate() {
                        let is_selected = Some(i) == *completion_idx;
                        // Show just the last component of the path for readability
                        let display = Path::new(comp)
                            .file_name()
                            .map(|n| n.to_string_lossy().to_string())
                            .unwrap_or_else(|| comp.clone());

                        let display = if comp.ends_with('/') {
                            format!("{}/ ", display)
                        } else {
                            format!("  {}", display)
                        };

                        let style = if is_selected { t.selected } else { t.log_info };
                        let prefix = if is_selected { "  > " } else { "    " };
                        lines.push(Line::from(Span::styled(
                            format!("{}{}", prefix, display),
                            style,
                        )));
                    }
                    if completions.len() > 8 {
                        lines.push(Line::from(Span::styled(
                            format!("    ... and {} more", completions.len() - 8),
                            t.tab_inactive,
                        )));
                    }
                }

                lines.push(Line::from(""));
                let hint = match on_submit {
                    InputTarget::EditConfigField(_, _) => "  Enter: submit  Esc: cancel",
                    _ => "  Tab: complete  Enter: submit  Esc: cancel",
                };
                lines.push(Line::from(Span::styled(hint, t.tab_inactive)));

                let popup = Paragraph::new(lines)
                    .block(
                        Block::default()
                            .borders(Borders::ALL)
                            .border_style(t.popup_border)
                            .title(popup_title)
                            .title_style(t.popup_title),
                    )
                    .wrap(Wrap { trim: false });
                frame.render_widget(popup, popup_area);
            }
            PopupKind::FileBrowser {
                current_dir,
                entries,
                selected,
                filter_active,
                filter_query,
            } => {
                let filtered = fb_filtered_indices(entries, filter_query);
                let filtered_len = filtered.len();

                let popup_height = (area.height * 80 / 100).max(12);
                let popup_width = (area.width * 70 / 100).max(50);
                let popup_area = centered_rect_abs(popup_width, popup_height, area);
                frame.render_widget(Clear, popup_area);

                let mut lines = Vec::new();

                // Current path
                lines.push(Line::from(""));
                lines.push(Line::from(vec![
                    Span::styled("  ", t.header),
                    Span::styled(current_dir.display().to_string(), t.title),
                ]));

                // Filter bar (if active or has query)
                let has_filter = *filter_active || !filter_query.is_empty();
                if has_filter {
                    let cursor = if *filter_active { "█" } else { "" };
                    lines.push(Line::from(vec![
                        Span::styled("  / ", t.status_bar_key),
                        Span::styled(filter_query.as_str(), t.title),
                        Span::styled(cursor, t.title),
                        Span::styled(format!("  ({} matches)", filtered_len), t.tab_inactive),
                    ]));
                }

                lines.push(Line::from(""));

                // Visible entries
                // Reserve lines: path(2) + filter(0-1) + blank(1) + hint(2) + border(2) + scroll indicator(0-1)
                let base_reserved = if has_filter { 8u16 } else { 7u16 };
                let needs_scroll_indicator =
                    filtered_len > popup_height.saturating_sub(base_reserved) as usize;
                let reserved = base_reserved + if needs_scroll_indicator { 1 } else { 0 };
                let visible_height = popup_height.saturating_sub(reserved) as usize;

                // Scroll window: keep selected in view with smooth tracking
                let scroll_start = if visible_height == 0 || filtered_len <= visible_height {
                    0
                } else {
                    (*selected).min(filtered_len - visible_height)
                };

                let visible_slice = filtered
                    .iter()
                    .enumerate()
                    .skip(scroll_start)
                    .take(visible_height);

                for (display_idx, &real_idx) in visible_slice {
                    let entry = &entries[real_idx];
                    let is_sel = display_idx == *selected;
                    let indicator = if is_sel { " > " } else { "   " };

                    let (name_display, style) = if entry.name == ".." {
                        (
                            "..".to_string(),
                            if is_sel { t.selected } else { t.tab_inactive },
                        )
                    } else {
                        (
                            format!("{}/", entry.name),
                            if is_sel { t.selected } else { t.status_info },
                        )
                    };

                    lines.push(Line::from(vec![
                        Span::styled(indicator, style),
                        Span::styled(name_display, style),
                    ]));
                }

                // Scroll position indicator
                if filtered_len > visible_height {
                    let pct = if filtered_len <= 1 {
                        100
                    } else {
                        (*selected * 100) / (filtered_len - 1)
                    };
                    lines.push(Line::from(Span::styled(
                        format!("   ({}/{} — {}%)", selected + 1, filtered_len, pct),
                        t.tab_inactive,
                    )));
                }

                lines.push(Line::from(""));
                lines.push(Line::from(vec![
                    Span::styled("  Enter", t.status_bar_key),
                    Span::styled(" select  ", t.tab_inactive),
                    Span::styled("l", t.status_bar_key),
                    Span::styled(" open  ", t.tab_inactive),
                    Span::styled("h/Bksp", t.status_bar_key),
                    Span::styled(" back  ", t.tab_inactive),
                    Span::styled("f /", t.status_bar_key),
                    Span::styled(" filter  ", t.tab_inactive),
                    Span::styled("Esc", t.status_bar_key),
                    Span::styled(" cancel", t.tab_inactive),
                ]));

                let popup = Paragraph::new(lines)
                    .block(
                        Block::default()
                            .borders(Borders::ALL)
                            .border_style(t.popup_border)
                            .title(" Select Directory ")
                            .title_style(t.popup_title),
                    )
                    .wrap(Wrap { trim: false });
                frame.render_widget(popup, popup_area);
            }
            PopupKind::Message {
                title,
                message,
                is_error,
            } => {
                let popup_area = ui_utils::centered_rect(60, 30, area);
                frame.render_widget(Clear, popup_area);

                let border_style = if *is_error {
                    t.status_error
                } else {
                    t.popup_border
                };
                let lines = vec![
                    Line::from(""),
                    Line::from(Span::raw(format!("  {}", message))),
                    Line::from(""),
                    Line::from(Span::styled("  Press Enter to close", t.tab_inactive)),
                ];
                let popup = Paragraph::new(lines)
                    .block(
                        Block::default()
                            .borders(Borders::ALL)
                            .border_style(border_style)
                            .title(format!(" {} ", title))
                            .title_style(if *is_error {
                                t.status_error
                            } else {
                                t.popup_title
                            }),
                    )
                    .wrap(Wrap { trim: true });
                frame.render_widget(popup, popup_area);
            }
        }
    }
}

/// Perform filesystem path autocompletion
/// Perform autocompletion against a static list of candidates (e.g. plugin filenames)
fn list_autocomplete(
    input: &mut String,
    cursor_pos: &mut usize,
    completions: &mut Vec<String>,
    completion_idx: &mut Option<usize>,
    candidates: &[String],
) {
    // If we already have completions and user presses Tab again, cycle forward
    if !completions.is_empty() {
        let idx = completion_idx
            .map(|i| (i + 1) % completions.len())
            .unwrap_or(0);
        *completion_idx = Some(idx);
        *input = completions[idx].clone();
        *cursor_pos = input.len();
        return;
    }

    let query = input.to_lowercase();

    let mut matches: Vec<String> = candidates
        .iter()
        .filter(|c| c.to_lowercase().contains(&query))
        .cloned()
        .collect();

    matches.sort();

    if matches.is_empty() {
        return;
    }

    if matches.len() == 1 {
        // Single match — complete directly
        *input = matches[0].clone();
        *cursor_pos = input.len();
        completions.clear();
        *completion_idx = None;
    } else {
        // Multiple matches — show list, select first
        *completions = matches;
        *completion_idx = Some(0);
        *input = completions[0].clone();
        *cursor_pos = input.len();
    }
}

/// Perform filesystem path autocompletion
fn path_autocomplete(
    input: &mut String,
    cursor_pos: &mut usize,
    completions: &mut Vec<String>,
    completion_idx: &mut Option<usize>,
) {
    // If we already have completions and user presses Tab again, cycle forward
    if !completions.is_empty() {
        let idx = completion_idx
            .map(|i| (i + 1) % completions.len())
            .unwrap_or(0);
        *completion_idx = Some(idx);
        *input = completions[idx].clone();
        *cursor_pos = input.len();
        return;
    }

    // Expand ~ to home dir
    let expanded = expand_tilde(input);
    let path = Path::new(&expanded);

    // Determine the directory to search and the prefix to match
    let (search_dir, prefix) = if expanded.ends_with('/') {
        // User typed a complete directory path, list its contents
        (PathBuf::from(&expanded), String::new())
    } else if path.parent().is_some() {
        // User is typing a partial name in some directory
        let parent = path.parent().unwrap().to_path_buf();
        let file_prefix = path
            .file_name()
            .map(|n| n.to_string_lossy().to_string())
            .unwrap_or_default();
        (parent, file_prefix)
    } else {
        // Just a plain word, search in current dir
        (PathBuf::from("."), expanded.clone())
    };

    // Read directory contents
    let Ok(entries) = std::fs::read_dir(&search_dir) else {
        return;
    };

    let mut candidates: Vec<String> = entries
        .filter_map(|e| e.ok())
        .filter(|e| {
            // Only show directories (we're looking for plugin directories)
            e.file_type().map(|ft| ft.is_dir()).unwrap_or(false)
        })
        .filter_map(|e| {
            let name = e.file_name().to_string_lossy().to_string();
            // Skip hidden directories unless user typed a dot
            if name.starts_with('.') && !prefix.starts_with('.') {
                return None;
            }
            // Match prefix (case-insensitive)
            if prefix.is_empty() || name.to_lowercase().starts_with(&prefix.to_lowercase()) {
                let full_path = search_dir.join(&name);
                // Always add trailing slash for directories
                Some(format!("{}/", full_path.display()))
            } else {
                None
            }
        })
        .collect();

    candidates.sort();

    if candidates.is_empty() {
        return;
    }

    if candidates.len() == 1 {
        // Only one match - complete directly
        *input = candidates[0].clone();
        *cursor_pos = input.len();
        // Keep completions empty so next Tab will list subdirs
        completions.clear();
        *completion_idx = None;
    } else {
        // Multiple matches - find common prefix and show list
        let common = longest_common_prefix(&candidates);
        if common.len() > expanded.len() {
            // We can extend the input to the common prefix
            *input = common;
            *cursor_pos = input.len();
        }
        *completions = candidates;
        *completion_idx = Some(0);
    }
}

/// Expand ~ at the start of a path to the home directory
fn expand_tilde(path: &str) -> String {
    if path.starts_with('~') {
        if let Some(home) = dirs::home_dir() {
            return path.replacen('~', &home.display().to_string(), 1);
        }
    }
    path.to_string()
}

/// Find the longest common prefix among a list of strings
fn longest_common_prefix(strings: &[String]) -> String {
    if strings.is_empty() {
        return String::new();
    }
    if strings.len() == 1 {
        return strings[0].clone();
    }

    let first = &strings[0];
    let mut prefix_len = first.chars().count();

    for s in &strings[1..] {
        prefix_len = first
            .chars()
            .zip(s.chars())
            .take(prefix_len)
            .take_while(|(a, b)| a == b)
            .count();
        if prefix_len == 0 {
            break;
        }
    }

    // Convert char count back to byte position
    first
        .char_indices()
        .nth(prefix_len)
        .map(|(i, _)| &first[..i])
        .unwrap_or(first)
        .to_string()
}

/// Return indices of entries that match the filter. The ".." entry always passes.
fn fb_filtered_indices(entries: &[DirEntry], query: &str) -> Vec<usize> {
    if query.is_empty() {
        return (0..entries.len()).collect();
    }
    let query_lower = query.to_lowercase();
    entries
        .iter()
        .enumerate()
        .filter(|(_, e)| e.name == ".." || e.name.to_lowercase().contains(&query_lower))
        .map(|(i, _)| i)
        .collect()
}

/// List directory entries: ".." first, then directories sorted.
fn list_directory(path: &Path) -> Vec<DirEntry> {
    let mut entries = Vec::new();

    // Always add ".." unless we're at root
    if path.parent().is_some() {
        entries.push(DirEntry {
            name: "..".to_string(),
            path: path.parent().unwrap().to_path_buf(),
            is_dir: true,
        });
    }

    if let Ok(read_dir) = std::fs::read_dir(path) {
        let mut dirs: Vec<DirEntry> = read_dir
            .filter_map(|e| e.ok())
            .filter_map(|e| {
                let metadata = e.metadata().ok()?;
                let name = e.file_name().to_string_lossy().to_string();
                // Only show directories
                if metadata.is_dir() {
                    Some(DirEntry {
                        name,
                        path: e.path(),
                        is_dir: true,
                    })
                } else {
                    None
                }
            })
            .collect();

        // Sort alphabetically, case-insensitive, hidden dirs mixed in
        dirs.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
        entries.extend(dirs);
    }

    entries
}

/// Create a centered rectangle with absolute width/height
fn centered_rect_abs(width: u16, height: u16, r: Rect) -> Rect {
    let x = r.x + (r.width.saturating_sub(width)) / 2;
    let y = r.y + (r.height.saturating_sub(height)) / 2;
    Rect::new(x, y, width.min(r.width), height.min(r.height))
}
