use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Direction, Layout, Margin, Rect};
use ratatui::text::{Line, Span, Text};
use ratatui::widgets::{
    Block, Borders, Cell, Paragraph, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table,
    TableState,
};
use ratatui::Frame;

use crate::action::Action;
use crate::components::{ui_utils, Component};
use crate::theme::theme;
use crate::yabridge::types::{Plugin, PluginType, SyncOptions, SyncStatus};

/// Filter plugins by type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeFilter {
    All,
    Vst2,
    Vst3,
    Clap,
}

impl TypeFilter {
    pub fn next(self) -> Self {
        match self {
            TypeFilter::All => TypeFilter::Vst2,
            TypeFilter::Vst2 => TypeFilter::Vst3,
            TypeFilter::Vst3 => TypeFilter::Clap,
            TypeFilter::Clap => TypeFilter::All,
        }
    }

    pub fn label(self) -> &'static str {
        match self {
            TypeFilter::All => "All",
            TypeFilter::Vst2 => "VST2",
            TypeFilter::Vst3 => "VST3",
            TypeFilter::Clap => "CLAP",
        }
    }

    fn matches(self, plugin_type: &PluginType) -> bool {
        match self {
            TypeFilter::All => true,
            TypeFilter::Vst2 => *plugin_type == PluginType::Vst2,
            TypeFilter::Vst3 => *plugin_type == PluginType::Vst3,
            TypeFilter::Clap => *plugin_type == PluginType::Clap,
        }
    }
}

/// Sort plugins by column
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortColumn {
    Name,
    Type,
    Status,
}

impl SortColumn {
    pub fn next(self) -> Self {
        match self {
            SortColumn::Name => SortColumn::Type,
            SortColumn::Type => SortColumn::Status,
            SortColumn::Status => SortColumn::Name,
        }
    }

    pub fn label(self) -> &'static str {
        match self {
            SortColumn::Name => "name",
            SortColumn::Type => "type",
            SortColumn::Status => "status",
        }
    }
}

pub struct PluginsTab {
    /// All plugins (unfiltered)
    all_plugins: Vec<Plugin>,
    /// Indices into all_plugins that match the current filter
    filtered_indices: Vec<usize>,
    pub table_state: TableState,
    pub loading: bool,
    /// Whether we're in filter input mode
    pub filter_active: bool,
    /// Current filter query
    pub filter_query: String,
    /// Filter by plugin type
    pub type_filter: TypeFilter,
    /// Sort column
    pub sort_column: SortColumn,
    /// Cached plugin counts (synced, vst2, vst3, clap) — updated in set_plugins()
    cached_counts: PluginCounts,
}

/// Pre-computed plugin type/status counts to avoid iterating per frame
#[derive(Default)]
struct PluginCounts {
    synced: usize,
    vst2: usize,
    vst3: usize,
    clap: usize,
}

impl PluginsTab {
    pub fn new() -> Self {
        Self {
            all_plugins: Vec::new(),
            filtered_indices: Vec::new(),
            table_state: TableState::default(),
            loading: false,
            filter_active: false,
            filter_query: String::new(),
            type_filter: TypeFilter::All,
            sort_column: SortColumn::Name,
            cached_counts: PluginCounts::default(),
        }
    }

    pub fn set_plugins(&mut self, plugins: Vec<Plugin>) {
        self.cached_counts = PluginCounts {
            synced: plugins
                .iter()
                .filter(|p| p.sync_status == SyncStatus::Synced)
                .count(),
            vst2: plugins
                .iter()
                .filter(|p| p.plugin_type == PluginType::Vst2)
                .count(),
            vst3: plugins
                .iter()
                .filter(|p| p.plugin_type == PluginType::Vst3)
                .count(),
            clap: plugins
                .iter()
                .filter(|p| p.plugin_type == PluginType::Clap)
                .count(),
        };
        self.all_plugins = plugins;
        self.apply_filter();
        self.loading = false;
    }

    /// Return deduplicated sorted list of plugin filenames (e.g. "FabFilter Pro-Q 3.vst3")
    /// for use as autocompletion candidates in config rule patterns.
    pub fn plugin_filenames(&self) -> Vec<String> {
        let mut names: Vec<String> = self
            .all_plugins
            .iter()
            .filter_map(|p| p.path.file_name().map(|n| n.to_string_lossy().to_string()))
            .collect();
        names.sort();
        names.dedup();
        names
    }

    /// Recompute filtered_indices from filter_query, type_filter, and sort_column
    fn apply_filter(&mut self) {
        // Step 1: filter by type + text query
        let has_text = !self.filter_query.is_empty();
        let query = self.filter_query.to_lowercase();
        let terms: Vec<&str> = if has_text {
            query.split_whitespace().collect()
        } else {
            Vec::new()
        };

        self.filtered_indices = self
            .all_plugins
            .iter()
            .enumerate()
            .filter(|(_, p)| {
                // Type filter
                if !self.type_filter.matches(&p.plugin_type) {
                    return false;
                }
                // Text filter
                if has_text {
                    let haystack = format!(
                        "{} {} {} {} {}",
                        p.name,
                        p.plugin_type,
                        p.architecture,
                        p.sync_status,
                        p.parent_dir.display()
                    )
                    .to_lowercase();
                    if !terms.iter().all(|term| haystack.contains(term)) {
                        return false;
                    }
                }
                true
            })
            .map(|(i, _)| i)
            .collect();

        // Step 2: sort
        let plugins = &self.all_plugins;
        match self.sort_column {
            SortColumn::Name => {
                self.filtered_indices.sort_by(|&a, &b| {
                    plugins[a]
                        .name
                        .to_lowercase()
                        .cmp(&plugins[b].name.to_lowercase())
                });
            }
            SortColumn::Type => {
                self.filtered_indices.sort_by(|&a, &b| {
                    let type_ord = |p: &Plugin| match p.plugin_type {
                        PluginType::Vst2 => 0,
                        PluginType::Vst3 => 1,
                        PluginType::Clap => 2,
                    };
                    type_ord(&plugins[a])
                        .cmp(&type_ord(&plugins[b]))
                        .then_with(|| {
                            plugins[a]
                                .name
                                .to_lowercase()
                                .cmp(&plugins[b].name.to_lowercase())
                        })
                });
            }
            SortColumn::Status => {
                self.filtered_indices.sort_by(|&a, &b| {
                    let status_ord = |p: &Plugin| match p.sync_status {
                        SyncStatus::Synced => 0,
                        SyncStatus::NotSynced => 1,
                        SyncStatus::Symlink => 2,
                        SyncStatus::Invalid => 3,
                        SyncStatus::Unknown => 4,
                    };
                    status_ord(&plugins[a])
                        .cmp(&status_ord(&plugins[b]))
                        .then_with(|| {
                            plugins[a]
                                .name
                                .to_lowercase()
                                .cmp(&plugins[b].name.to_lowercase())
                        })
                });
            }
        }

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

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

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

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

    fn select_first(&mut self) {
        if self.filtered_len() > 0 {
            self.table_state.select(Some(0));
        }
    }

    fn select_last(&mut self) {
        let len = self.filtered_len();
        if len > 0 {
            self.table_state.select(Some(len - 1));
        }
    }

    fn page_down(&mut self, page_size: usize) {
        let len = self.filtered_len();
        if len == 0 {
            return;
        }
        let i = match self.table_state.selected() {
            Some(i) => (i + page_size).min(len - 1),
            None => 0,
        };
        self.table_state.select(Some(i));
    }

    fn page_up(&mut self, page_size: usize) {
        if self.filtered_len() == 0 {
            return;
        }
        let i = match self.table_state.selected() {
            Some(i) => i.saturating_sub(page_size),
            None => 0,
        };
        self.table_state.select(Some(i));
    }

    fn plugin_count_summary(&self) -> String {
        let total = self.all_plugins.len();
        let filtered = self.filtered_len();
        let synced = self.cached_counts.synced;
        let vst2 = self.cached_counts.vst2;
        let vst3 = self.cached_counts.vst3;
        let clap = self.cached_counts.clap;

        let has_text_filter = !self.filter_query.is_empty();
        let has_type_filter = self.type_filter != TypeFilter::All;
        let has_sort = self.sort_column != SortColumn::Name;

        // Build count part
        let count_part = if has_text_filter || has_type_filter {
            format!("{}/{} plugins", filtered, total)
        } else {
            format!("{} plugins", total)
        };

        // Build modifiers
        let mut modifiers = Vec::new();
        if has_type_filter {
            modifiers.push(self.type_filter.label().to_string());
        }
        if has_sort {
            modifiers.push(format!("by {}", self.sort_column.label()));
        }

        let modifier_part = if modifiers.is_empty() {
            String::new()
        } else {
            format!(", {}", modifiers.join(", "))
        };

        format!(
            " {} ({} synced{}) │ VST2: {} │ VST3: {} │ CLAP: {} ",
            count_part, synced, modifier_part, vst2, vst3, clap
        )
    }
}

impl Component for PluginsTab {
    fn handle_key(&mut self, key: KeyEvent) -> Action {
        // ── Filter input mode ───────────────────
        if self.filter_active {
            match key.code {
                KeyCode::Esc => {
                    // Exit filter mode, clear filter
                    self.filter_active = false;
                    self.filter_query.clear();
                    self.apply_filter();
                    return Action::Noop;
                }
                KeyCode::Enter => {
                    // Exit filter mode, keep filter applied
                    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;
                }
                // Allow navigation even while typing
                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('d') | KeyCode::PageDown => {
                self.page_down(10);
                Action::Noop
            }
            KeyCode::Char('u') | KeyCode::PageUp => {
                self.page_up(10);
                Action::Noop
            }
            KeyCode::Char('g') | KeyCode::Home => {
                self.select_first();
                Action::Noop
            }
            KeyCode::Char('G') | KeyCode::End => {
                self.select_last();
                Action::Noop
            }
            KeyCode::Char('f') | KeyCode::Char('/') => {
                // Enter filter mode
                self.filter_active = true;
                self.filter_query.clear();
                Action::Noop
            }
            KeyCode::Esc => {
                // Clear any active filter
                if !self.filter_query.is_empty() {
                    self.filter_query.clear();
                    self.apply_filter();
                    Action::Noop
                } else {
                    Action::Noop
                }
            }
            KeyCode::Char('t') => {
                self.type_filter = self.type_filter.next();
                self.apply_filter();
                Action::Noop
            }
            KeyCode::Char('o') => {
                self.sort_column = self.sort_column.next();
                self.apply_filter();
                Action::Noop
            }
            KeyCode::Char('s') => Action::SyncPlugins(SyncOptions {
                verbose: true,
                ..Default::default()
            }),
            KeyCode::Char('S') => Action::SyncPlugins(SyncOptions {
                force: true,
                verbose: true,
                ..Default::default()
            }),
            KeyCode::Char('p') => Action::SyncPlugins(SyncOptions {
                prune: true,
                verbose: true,
                ..Default::default()
            }),
            KeyCode::Char('r') => Action::RefreshPlugins,
            _ => Action::Noop,
        }
    }

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

        if self.loading {
            let loading = Paragraph::new("Loading plugins...").style(t.spinner).block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(self.plugin_count_summary())
                    .title_style(t.header),
            );
            frame.render_widget(loading, area);
            return;
        }

        // Layout: optional filter bar + table
        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 table_area = chunks[1];

        // ── Empty state ─────────────────────────
        if self.all_plugins.is_empty() {
            let empty = Paragraph::new(Text::raw(
                "\n  No plugins found.\n\n  Add plugin directories in the Paths tab,\n  then run sync with 's'.",
            ))
            .style(t.status_warning)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(" Plugins ")
                    .title_style(t.header),
            );
            frame.render_widget(empty, table_area);
            return;
        }

        if self.filtered_len() == 0 && !self.filter_query.is_empty() {
            let no_match = Paragraph::new(Text::raw(format!(
                "\n  No plugins matching \"{}\"",
                self.filter_query
            )))
            .style(t.status_warning)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(self.plugin_count_summary())
                    .title_style(t.header),
            );
            frame.render_widget(no_match, table_area);
            return;
        }

        // ── Table header ────────────────────────
        let header = Row::new(vec![
            Cell::from("  Plugin"),
            Cell::from("Type"),
            Cell::from("Arch"),
            Cell::from("Status"),
            Cell::from("Directory"),
        ])
        .style(t.header)
        .height(1)
        .bottom_margin(1);

        // ── Table rows (filtered) ───────────────
        let query_lower = self.filter_query.to_lowercase();
        let rows: Vec<Row> = self
            .filtered_indices
            .iter()
            .map(|&idx| {
                let plugin = &self.all_plugins[idx];
                let type_style = match plugin.plugin_type {
                    PluginType::Vst2 => t.vst2,
                    PluginType::Vst3 => t.vst3,
                    PluginType::Clap => t.clap,
                };

                let status_style = match plugin.sync_status {
                    SyncStatus::Synced => t.status_ok,
                    SyncStatus::NotSynced => t.status_warning,
                    SyncStatus::Invalid => t.status_error,
                    SyncStatus::Symlink => t.status_warning,
                    SyncStatus::Unknown => t.status_info,
                };

                let status_text = format!("{} {}", plugin.sync_status.symbol(), plugin.sync_status);

                let dir_display = plugin
                    .parent_dir
                    .file_name()
                    .map(|n| n.to_string_lossy().to_string())
                    .unwrap_or_else(|| plugin.parent_dir.display().to_string());

                // Highlight matching text in plugin name
                let name_cell = if !query_lower.is_empty() {
                    let mut spans = vec![Span::raw("  ")];
                    spans.extend(ui_utils::highlight_match(
                        &plugin.name,
                        &query_lower,
                        ratatui::style::Style::default(),
                        t.filter_match,
                    ));
                    Line::from(spans)
                } else {
                    Line::from(format!("  {}", plugin.name))
                };

                Row::new(vec![
                    Cell::from(name_cell),
                    Cell::from(plugin.plugin_type.to_string()).style(type_style),
                    Cell::from(plugin.architecture.to_string()),
                    Cell::from(status_text).style(status_style),
                    Cell::from(dir_display).style(t.tab_inactive),
                ])
            })
            .collect();

        let widths = [
            Constraint::Min(25),
            Constraint::Length(6),
            Constraint::Length(7),
            Constraint::Length(14),
            Constraint::Min(15),
        ];

        let table = Table::new(rows, widths)
            .header(header)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(self.plugin_count_summary())
                    .title_style(t.header),
            )
            .column_spacing(1)
            .row_highlight_style(t.selected)
            .highlight_symbol("▶ ");

        frame.render_stateful_widget(table, table_area, &mut self.table_state);

        // Scrollbar
        let visible_rows = table_area.height.saturating_sub(4) as usize;
        if self.filtered_len() > visible_rows {
            let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
                .begin_symbol(Some("↑"))
                .end_symbol(Some("↓"));
            let mut scrollbar_state = ScrollbarState::new(self.filtered_len())
                .position(self.table_state.selected().unwrap_or(0));
            frame.render_stateful_widget(
                scrollbar,
                table_area.inner(Margin {
                    vertical: 1,
                    horizontal: 0,
                }),
                &mut scrollbar_state,
            );
        }
    }
}
