use std::path::PathBuf;

use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::text::{Line, Span, Text};
use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
use ratatui::Frame;

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

pub struct PathsTab {
    /// All paths (unfiltered)
    all_paths: Vec<PathBuf>,
    /// Cached existence check for each path (parallel to all_paths)
    path_exists: Vec<bool>,
    /// Indices into all_paths that match the filter
    filtered_indices: Vec<usize>,
    pub list_state: ListState,
    pub loading: bool,
    /// Whether we're in filter input mode
    pub filter_active: bool,
    /// Current filter query
    pub filter_query: String,
}

impl PathsTab {
    pub fn new() -> Self {
        Self {
            all_paths: Vec::new(),
            path_exists: Vec::new(),
            filtered_indices: Vec::new(),
            list_state: ListState::default(),
            loading: false,
            filter_active: false,
            filter_query: String::new(),
        }
    }

    pub fn set_paths(&mut self, paths: Vec<PathBuf>) {
        self.path_exists = paths.iter().map(|p| p.exists()).collect();
        self.all_paths = paths;
        self.apply_filter();
        self.loading = false;
    }

    fn apply_filter(&mut self) {
        if self.filter_query.is_empty() {
            self.filtered_indices = (0..self.all_paths.len()).collect();
        } else {
            let query = self.filter_query.to_lowercase();
            let terms: Vec<&str> = query.split_whitespace().collect();

            self.filtered_indices = self
                .all_paths
                .iter()
                .enumerate()
                .filter(|(_, p)| {
                    let haystack = p.display().to_string().to_lowercase();
                    terms.iter().all(|term| haystack.contains(term))
                })
                .map(|(i, _)| i)
                .collect();
        }

        let len = self.filtered_indices.len();
        if len == 0 {
            self.list_state.select(None);
        } else if let Some(idx) = self.list_state.selected() {
            if idx >= len {
                self.list_state.select(Some(len - 1));
            }
        } else {
            self.list_state.select(Some(0));
        }
    }

    fn filtered_paths(&self) -> Vec<&PathBuf> {
        self.filtered_indices
            .iter()
            .map(|&i| &self.all_paths[i])
            .collect()
    }

    fn filtered_len(&self) -> usize {
        self.filtered_indices.len()
    }

    /// Get the selected path (from filtered view, mapped back to real path)
    pub fn selected_path(&self) -> Option<&PathBuf> {
        self.list_state
            .selected()
            .and_then(|i| self.filtered_indices.get(i))
            .map(|&real_i| &self.all_paths[real_i])
    }

    fn select_next(&mut self) {
        let len = self.filtered_len();
        if len == 0 {
            return;
        }
        let i = match self.list_state.selected() {
            Some(i) => (i + 1) % len,
            None => 0,
        };
        self.list_state.select(Some(i));
    }

    fn select_prev(&mut self) {
        let len = self.filtered_len();
        if len == 0 {
            return;
        }
        let i = match self.list_state.selected() {
            Some(i) => {
                if i == 0 {
                    len - 1
                } else {
                    i - 1
                }
            }
            None => 0,
        };
        self.list_state.select(Some(i));
    }
}

impl Component for PathsTab {
    fn handle_key(&mut self, key: KeyEvent) -> Action {
        // ── Filter input mode ───────────────────
        if self.filter_active {
            match key.code {
                KeyCode::Esc => {
                    self.filter_active = false;
                    self.filter_query.clear();
                    self.apply_filter();
                    return Action::Noop;
                }
                KeyCode::Enter => {
                    self.filter_active = false;
                    return Action::Noop;
                }
                KeyCode::Char(c) => {
                    self.filter_query.push(c);
                    self.apply_filter();
                    return Action::Noop;
                }
                KeyCode::Backspace => {
                    self.filter_query.pop();
                    self.apply_filter();
                    return Action::Noop;
                }
                KeyCode::Down => {
                    self.select_next();
                    return Action::Noop;
                }
                KeyCode::Up => {
                    self.select_prev();
                    return Action::Noop;
                }
                _ => return Action::Noop,
            }
        }

        // ── Normal mode ─────────────────────────
        match key.code {
            KeyCode::Char('j') | KeyCode::Down => {
                self.select_next();
                Action::Noop
            }
            KeyCode::Char('k') | KeyCode::Up => {
                self.select_prev();
                Action::Noop
            }
            KeyCode::Char('g') | KeyCode::Home => {
                if self.filtered_len() > 0 {
                    self.list_state.select(Some(0));
                }
                Action::Noop
            }
            KeyCode::Char('G') | KeyCode::End => {
                let len = self.filtered_len();
                if len > 0 {
                    self.list_state.select(Some(len - 1));
                }
                Action::Noop
            }
            KeyCode::Char('u') | KeyCode::PageUp => {
                if let Some(i) = self.list_state.selected() {
                    self.list_state.select(Some(i.saturating_sub(10)));
                }
                Action::Noop
            }
            KeyCode::Char('f') | KeyCode::Char('/') => {
                self.filter_active = true;
                self.filter_query.clear();
                Action::Noop
            }
            KeyCode::Esc => {
                if !self.filter_query.is_empty() {
                    self.filter_query.clear();
                    self.apply_filter();
                }
                Action::Noop
            }
            KeyCode::Char('a') => Action::ShowInput("Add plugin directory path:".to_string()),
            KeyCode::PageDown => {
                let len = self.filtered_len();
                if let Some(i) = self.list_state.selected() {
                    self.list_state
                        .select(Some((i + 10).min(if len > 0 { len - 1 } else { 0 })));
                }
                Action::Noop
            }
            KeyCode::Char('d') | KeyCode::Delete => {
                if let Some(path) = self.selected_path().cloned() {
                    Action::ShowConfirm {
                        message: format!("Remove path: {}?", path.display()),
                        on_confirm: Box::new(Action::RemovePath(path)),
                    }
                } else {
                    Action::Noop
                }
            }
            _ => Action::Noop,
        }
    }

    fn render(&mut self, frame: &mut Frame, area: Rect) {
        let t = theme();

        if self.all_paths.is_empty() && self.filter_query.is_empty() {
            let empty = Paragraph::new(Text::raw(
                "\n  No plugin directories configured.\n\n  Press 'a' to add a directory.\n  Common paths:\n    ~/.wine/drive_c/Program Files/Steinberg/VstPlugins\n    ~/.wine/drive_c/Program Files/Common Files/VST3\n    ~/.wine/drive_c/Program Files/Common Files/CLAP",
            ))
            .style(t.status_warning)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(" Plugin Directories ")
                    .title_style(t.header),
            );
            frame.render_widget(empty, area);
            return;
        }

        // Layout: optional filter bar + list
        let has_filter = self.filter_active || !self.filter_query.is_empty();
        let chunks = if has_filter {
            Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(3), Constraint::Min(1)])
                .split(area)
        } else {
            Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(0), Constraint::Min(1)])
                .split(area)
        };

        // ── Filter bar ──────────────────────────
        if has_filter {
            ui_utils::render_filter_bar(
                frame,
                chunks[0],
                &self.filter_query,
                self.filter_active,
                &format!("{} matches", self.filtered_len()),
            );
        }

        let list_area = chunks[1];

        // ── No match state ──────────────────────
        let filtered = self.filtered_paths();
        if filtered.is_empty() && !self.filter_query.is_empty() {
            let no_match = Paragraph::new(Text::raw(format!(
                "\n  No paths matching \"{}\"",
                self.filter_query
            )))
            .style(t.status_warning)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(format!(" Plugin Directories (0/{}) ", self.all_paths.len()))
                    .title_style(t.header),
            );
            frame.render_widget(no_match, list_area);
            return;
        }

        // ── Path list ───────────────────────────
        let query_lower = self.filter_query.to_lowercase();
        let items: Vec<ListItem> = self
            .filtered_indices
            .iter()
            .map(|&idx| {
                let path = &self.all_paths[idx];
                let exists = self.path_exists.get(idx).copied().unwrap_or(false);
                let style = if exists { t.status_ok } else { t.status_error };
                let suffix = if exists { "" } else { " (not found!)" };
                let path_str = format!("{}{}", path.display(), suffix);

                let line = if !query_lower.is_empty() {
                    let mut spans = vec![Span::styled("  ".to_string(), style)];
                    spans.extend(ui_utils::highlight_match_owned(
                        &path_str,
                        &query_lower,
                        style,
                        t.filter_match,
                    ));
                    Line::from(spans)
                } else {
                    Line::from(Span::styled(format!("  {}", path_str), style))
                };

                ListItem::new(line)
            })
            .collect();

        let title = if !self.filter_query.is_empty() {
            format!(
                " Plugin Directories ({}/{}) ",
                self.filtered_len(),
                self.all_paths.len()
            )
        } else {
            format!(" Plugin Directories ({}) ", self.all_paths.len())
        };

        let list = List::new(items)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(title)
                    .title_style(t.header),
            )
            .highlight_style(t.selected)
            .highlight_symbol("▶ ");

        frame.render_stateful_widget(list, list_area, &mut self.list_state);
    }
}
