use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
use ratatui::Frame;
use unicode_width::UnicodeWidthStr;

use crate::action::Action;
use crate::components::Component;
use crate::theme::theme;
use crate::yabridge::types::{PluginOptions, YabridgeConfig};

/// Mode for the config tab
#[derive(Debug, Clone, Copy, PartialEq)]
enum Mode {
    /// Navigating between rules (j/k), add/delete/save
    Browse,
    /// Editing fields within a selected rule (j/k between fields, Space toggle, Enter edit)
    Edit,
}

pub struct ConfigTab {
    pub config: Option<YabridgeConfig>,
    pub plugin_options: Vec<PluginOptions>,
    pub options_state: ListState,
    pub config_path: String,
    pub plugin_config_path: String,

    /// Current interaction mode
    mode: Mode,
    /// Selected field index within a rule (0-8) when in Edit mode
    field_index: usize,
    /// Whether config has been modified since last save
    pub dirty: bool,
}

impl ConfigTab {
    pub fn new() -> Self {
        Self {
            config: None,
            plugin_options: Vec::new(),
            options_state: ListState::default(),
            config_path: String::new(),
            plugin_config_path: String::new(),
            mode: Mode::Browse,
            field_index: 0,
            dirty: false,
        }
    }

    pub fn set_config(&mut self, config: YabridgeConfig) {
        self.config = Some(config);
    }

    pub fn set_plugin_options(&mut self, options: Vec<PluginOptions>) {
        self.plugin_options = options;
        if !self.plugin_options.is_empty() && self.options_state.selected().is_none() {
            self.options_state.select(Some(0));
        }
    }

    pub fn is_editing(&self) -> bool {
        self.mode == Mode::Edit
    }

    pub fn field_index(&self) -> usize {
        self.field_index
    }

    fn selected_rule(&self) -> Option<usize> {
        self.options_state.selected()
    }

    fn select_next_rule(&mut self) {
        if self.plugin_options.is_empty() {
            return;
        }
        let i = match self.options_state.selected() {
            Some(i) => (i + 1).min(self.plugin_options.len() - 1),
            None => 0,
        };
        self.options_state.select(Some(i));
    }

    fn select_prev_rule(&mut self) {
        if self.plugin_options.is_empty() {
            return;
        }
        let i = match self.options_state.selected() {
            Some(i) => i.saturating_sub(1),
            None => 0,
        };
        self.options_state.select(Some(i));
    }

    fn select_next_field(&mut self) {
        let max = PluginOptions::field_labels().len() - 1;
        self.field_index = (self.field_index + 1).min(max);
    }

    fn select_prev_field(&mut self) {
        self.field_index = self.field_index.saturating_sub(1);
    }

    fn num_fields() -> usize {
        PluginOptions::field_labels().len()
    }

    /// Build the save action with current state
    fn save_action(&self) -> Action {
        Action::SaveConfig(self.plugin_config_path.clone(), self.plugin_options.clone())
    }
}

impl Component for ConfigTab {
    fn handle_key(&mut self, key: KeyEvent) -> Action {
        match self.mode {
            Mode::Browse => self.handle_browse_key(key),
            Mode::Edit => self.handle_edit_key(key),
        }
    }

    fn render(&mut self, frame: &mut Frame, area: Rect) {
        let has_blacklist = self
            .config
            .as_ref()
            .map(|c| !c.blacklist.is_empty())
            .unwrap_or(false);

        // Layout: summary panel (fixed) | main content | optional blacklist
        let mut constraints = vec![
            Constraint::Length(10), // yabridgectl config summary
        ];

        if self.mode == Mode::Edit && self.selected_rule().is_some() {
            // Split main area: rule list (left) + field editor (right)
            constraints.push(Constraint::Min(10));
        } else {
            constraints.push(Constraint::Min(5));
        }

        if has_blacklist {
            constraints.push(Constraint::Length(6));
        }

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints(constraints)
            .split(area);

        // ── Panel 1: yabridgectl config summary ──
        self.render_config_summary(frame, chunks[0]);

        // ── Panel 2: Plugin options ──
        if self.mode == Mode::Edit && self.selected_rule().is_some() {
            // Split horizontally: rule list on left, field editor on right
            let hsplit = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(40), Constraint::Percentage(60)])
                .split(chunks[1]);

            self.render_rule_list(frame, hsplit[0], true);
            self.render_field_editor(frame, hsplit[1]);
        } else {
            self.render_rule_list(frame, chunks[1], false);
        }

        // ── Panel 3: Blacklist ──
        if has_blacklist {
            let bl_chunk = chunks[2];
            self.render_blacklist(frame, bl_chunk);
        }
    }
}

impl ConfigTab {
    fn handle_browse_key(&mut self, key: KeyEvent) -> Action {
        match key.code {
            KeyCode::Char('j') | KeyCode::Down => {
                self.select_next_rule();
                Action::Noop
            }
            KeyCode::Char('k') | KeyCode::Up => {
                self.select_prev_rule();
                Action::Noop
            }
            KeyCode::Char('g') | KeyCode::Home => {
                if !self.plugin_options.is_empty() {
                    self.options_state.select(Some(0));
                }
                Action::Noop
            }
            KeyCode::Char('G') | KeyCode::End => {
                if !self.plugin_options.is_empty() {
                    self.options_state
                        .select(Some(self.plugin_options.len() - 1));
                }
                Action::Noop
            }
            KeyCode::Enter => {
                // Enter edit mode for the selected rule
                if self.selected_rule().is_some() && !self.plugin_options.is_empty() {
                    self.mode = Mode::Edit;
                    self.field_index = 0;
                }
                Action::Noop
            }
            KeyCode::Char('e') => {
                // Edit the pattern of the selected rule
                if let Some(idx) = self.selected_rule() {
                    if idx < self.plugin_options.len() {
                        return Action::ShowInput(format!("Edit pattern for rule #{}:", idx + 1));
                    }
                }
                Action::Noop
            }
            KeyCode::Char('a') => {
                // Add a new rule — dispatch to app which will show input popup
                Action::ShowInput(
                    "New rule pattern (glob, e.g. \"*\" or \"FabFilter*\")".to_string(),
                )
            }
            KeyCode::Char('d') | KeyCode::Delete => {
                // Delete selected rule
                if let Some(idx) = self.selected_rule() {
                    if idx < self.plugin_options.len() {
                        let pattern = self.plugin_options[idx].pattern.clone();
                        return Action::ShowConfirm {
                            message: format!("Delete rule: [{}]?", pattern),
                            on_confirm: Box::new(Action::DeleteConfigRule(idx)),
                        };
                    }
                }
                Action::Noop
            }
            KeyCode::Char('w') => {
                // Save config
                if self.dirty {
                    self.save_action()
                } else {
                    Action::Noop
                }
            }
            KeyCode::Char('r') => Action::RefreshConfig,
            _ => Action::Noop,
        }
    }

    fn handle_edit_key(&mut self, key: KeyEvent) -> Action {
        match key.code {
            KeyCode::Esc => {
                self.mode = Mode::Browse;
                Action::Noop
            }
            KeyCode::Char('j') | KeyCode::Down => {
                self.select_next_field();
                Action::Noop
            }
            KeyCode::Char('k') | KeyCode::Up => {
                self.select_prev_field();
                Action::Noop
            }
            KeyCode::Char('g') | KeyCode::Home => {
                self.field_index = 0;
                Action::Noop
            }
            KeyCode::Char('G') | KeyCode::End => {
                self.field_index = Self::num_fields() - 1;
                Action::Noop
            }
            KeyCode::Char(' ') => {
                // Toggle boolean fields
                if PluginOptions::is_bool_field(self.field_index) {
                    if let Some(idx) = self.selected_rule() {
                        self.plugin_options[idx].toggle_bool(self.field_index);
                        self.dirty = true;
                    }
                }
                Action::Noop
            }
            KeyCode::Enter => {
                // Edit string/numeric fields via popup input, or toggle booleans
                if let Some(rule_idx) = self.selected_rule() {
                    let labels = PluginOptions::field_labels();
                    let (_, label) = labels[self.field_index];

                    if PluginOptions::is_string_field(self.field_index)
                        || PluginOptions::is_numeric_field(self.field_index)
                    {
                        let hint = if PluginOptions::is_numeric_field(self.field_index) {
                            format!("{} (number, empty to clear): ", label)
                        } else {
                            format!("{}: ", label)
                        };
                        return Action::ShowInput(hint);
                    } else if PluginOptions::is_bool_field(self.field_index) {
                        // Toggle on Enter too for booleans
                        self.plugin_options[rule_idx].toggle_bool(self.field_index);
                        self.dirty = true;
                    }
                }
                Action::Noop
            }
            KeyCode::Char('x') => {
                // Clear/reset field to None
                if let Some(rule_idx) = self.selected_rule() {
                    if PluginOptions::is_bool_field(self.field_index) {
                        // Reset bool to None (unset)
                        let opt = &mut self.plugin_options[rule_idx];
                        match self.field_index {
                            1 => opt.disable_pipes = None,
                            2 => opt.editor_coordinate_hack = None,
                            3 => opt.editor_disable_host_scaling = None,
                            4 => opt.editor_force_dnd = None,
                            5 => opt.editor_xembed = None,
                            7 => opt.hide_daw = None,
                            8 => opt.vst3_prefer_32bit = None,
                            _ => {}
                        }
                        self.dirty = true;
                    } else if PluginOptions::is_string_field(self.field_index) {
                        self.plugin_options[rule_idx].set_string(self.field_index, String::new());
                        self.dirty = true;
                    } else if PluginOptions::is_numeric_field(self.field_index) {
                        self.plugin_options[rule_idx].set_numeric(self.field_index, None);
                        self.dirty = true;
                    }
                }
                Action::Noop
            }
            KeyCode::Char('w') => {
                // Save from edit mode too
                if self.dirty {
                    self.save_action()
                } else {
                    Action::Noop
                }
            }
            _ => Action::Noop,
        }
    }

    // ── Rendering helpers ──────────────────────────────

    fn render_config_summary(&mut self, frame: &mut Frame, area: Rect) {
        let t = theme();
        let mut lines = Vec::new();

        if let Some(ref config) = self.config {
            lines.push(Line::from(vec![
                Span::styled("  yabridge home: ", t.header),
                Span::styled(
                    config
                        .yabridge_home
                        .as_ref()
                        .map(|p| p.display().to_string())
                        .unwrap_or_else(|| "<auto>".to_string()),
                    t.status_info,
                ),
            ]));
            lines.push(Line::from(vec![
                Span::styled("  VST2 location: ", t.header),
                Span::styled(config.vst2_location.to_string(), t.status_info),
            ]));
            lines.push(Line::from(vec![
                Span::styled("  No verify:     ", t.header),
                Span::styled(
                    if config.no_verify { "yes" } else { "no" },
                    if config.no_verify {
                        t.status_warning
                    } else {
                        t.status_ok
                    },
                ),
            ]));
            lines.push(Line::from(vec![
                Span::styled("  Plugin dirs:   ", t.header),
                Span::styled(config.plugin_dirs.len().to_string(), t.status_info),
            ]));
            lines.push(Line::from(vec![
                Span::styled("  Blacklisted:   ", t.header),
                Span::styled(config.blacklist.len().to_string(), t.status_info),
            ]));
            lines.push(Line::from(""));
            lines.push(Line::from(vec![
                Span::styled("  Config: ", t.tab_inactive),
                Span::styled(&self.config_path, t.tab_inactive),
            ]));
        } else {
            lines.push(Line::from(Span::styled(
                "  No config loaded. Press 'r' to reload.",
                t.status_warning,
            )));
        }

        let config_block = Paragraph::new(lines).block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(t.border_focused)
                .title(" yabridgectl Config ")
                .title_style(t.header),
        );
        frame.render_widget(config_block, area);
    }

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

        if self.plugin_options.is_empty() {
            let hint = if compact {
                "\n  No rules"
            } else {
                "\n  No plugin-specific options configured.\n  Press 'a' to add a new rule."
            };
            let empty = Paragraph::new(hint).style(t.tab_inactive).block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border)
                    .title(self.rule_list_title())
                    .title_style(t.header),
            );
            frame.render_widget(empty, area);
            return;
        }

        let items: Vec<ListItem> = self
            .plugin_options
            .iter()
            .enumerate()
            .map(|(i, opt)| {
                let option_count = opt.option_count();
                let count_str = if option_count > 0 {
                    format!(" ({} opts)", option_count)
                } else {
                    String::new()
                };

                if compact {
                    // Compact: just pattern + count
                    let style = if Some(i) == self.selected_rule() {
                        t.title
                    } else {
                        t.log_info
                    };
                    ListItem::new(Line::from(vec![
                        Span::styled(format!("  [{}]", opt.pattern), style),
                        Span::styled(count_str, t.tab_inactive),
                    ]))
                } else {
                    // Full: pattern + inline summary of active options
                    let mut parts = vec![Span::styled(format!("  [{}]", opt.pattern), t.title)];
                    parts.push(Span::styled(count_str, t.tab_inactive));

                    if let Some(ref group) = opt.group {
                        parts.push(Span::raw(" group="));
                        parts.push(Span::styled(group, t.status_info));
                    }
                    if opt.editor_force_dnd == Some(true) {
                        parts.push(Span::styled(" dnd", t.status_warning));
                    }
                    if opt.editor_xembed == Some(true) {
                        parts.push(Span::styled(" xembed", t.status_warning));
                    }
                    if opt.editor_coordinate_hack == Some(true) {
                        parts.push(Span::styled(" coord-hack", t.status_warning));
                    }
                    if opt.disable_pipes == Some(true) {
                        parts.push(Span::styled(" no-pipes", t.status_warning));
                    }
                    if opt.editor_disable_host_scaling == Some(true) {
                        parts.push(Span::styled(" no-hidpi", t.status_warning));
                    }
                    if opt.hide_daw == Some(true) {
                        parts.push(Span::styled(" hide-daw", t.status_warning));
                    }
                    if opt.vst3_prefer_32bit == Some(true) {
                        parts.push(Span::styled(" 32bit", t.status_warning));
                    }
                    if let Some(fps) = opt.frame_rate {
                        parts.push(Span::raw(format!(" fps={}", fps)));
                    }
                    ListItem::new(Line::from(parts))
                }
            })
            .collect();

        let border_style = if self.mode == Mode::Browse {
            t.border_focused
        } else {
            t.border
        };

        let highlight = if self.mode == Mode::Browse {
            t.selected
        } else {
            t.selected_unfocused
        };
        let highlight_sym = if self.mode == Mode::Browse {
            "▶ "
        } else {
            "  "
        };

        let list = List::new(items)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(border_style)
                    .title(self.rule_list_title())
                    .title_style(t.header),
            )
            .highlight_style(highlight)
            .highlight_symbol(highlight_sym);

        frame.render_stateful_widget(list, area, &mut self.options_state);
    }

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

        let Some(rule_idx) = self.selected_rule() else {
            return;
        };
        let Some(opt) = self.plugin_options.get(rule_idx) else {
            return;
        };

        let labels = PluginOptions::field_labels();
        let mut lines = Vec::new();
        // Available inner width (minus borders)
        let inner_width = area.width.saturating_sub(2) as usize;

        lines.push(Line::from(vec![
            Span::styled("  Pattern: ", t.header),
            Span::styled(&opt.pattern, t.title),
        ]));
        lines.push(Line::from(""));

        for (i, (_, label)) in labels.iter().enumerate() {
            let value = opt.get_field_display(i);
            let is_unset = value == "-";
            let is_selected = i == self.field_index;

            // Selected line gets full-width background via t.selected
            let sel_bg = if is_selected {
                t.selected
            } else {
                Style::default()
            };

            let indicator = if is_selected { " ▶ " } else { "   " };

            let indicator_style = if is_selected {
                Style::new()
                    .fg(Color::Cyan)
                    .add_modifier(Modifier::BOLD)
                    .bg(t.selected.bg.unwrap_or(Color::Reset))
            } else {
                sel_bg
            };

            let label_style = if is_selected { t.selected } else { t.header };

            let value_style = if is_selected {
                // Keep value color visible on selected bg
                let fg = if is_unset {
                    Color::DarkGray
                } else if PluginOptions::is_bool_field(i) {
                    if value == "yes" {
                        Color::Green
                    } else {
                        Color::Red
                    }
                } else {
                    Color::Cyan
                };
                Style::new()
                    .fg(fg)
                    .bg(t.selected.bg.unwrap_or(Color::Reset))
                    .add_modifier(Modifier::BOLD)
            } else if is_unset {
                t.tab_inactive
            } else if PluginOptions::is_bool_field(i) {
                if value == "yes" {
                    t.status_ok
                } else {
                    t.status_error
                }
            } else {
                t.status_info
            };

            let type_hint = if PluginOptions::is_bool_field(i) {
                " [Space: cycle]"
            } else if PluginOptions::is_string_field(i) || PluginOptions::is_numeric_field(i) {
                " [Enter: edit]"
            } else {
                ""
            };

            let hint_style = if is_selected {
                Style::new()
                    .fg(Color::DarkGray)
                    .bg(t.selected.bg.unwrap_or(Color::Reset))
            } else {
                Style::default()
            };

            // Build the default hint for unset fields
            let default_hint = if is_unset {
                PluginOptions::field_default_hint(i)
                    .map(|d| format!("(default: {})", d))
                    .unwrap_or_default()
            } else {
                String::new()
            };

            let mut spans = vec![
                Span::styled(indicator, indicator_style),
                Span::styled(format!("{:<30}", label), label_style),
                Span::styled(format!("{:<8}", value), value_style),
            ];

            if !default_hint.is_empty() {
                spans.push(Span::styled(
                    format!("{:<16}", default_hint),
                    if is_selected {
                        hint_style
                    } else {
                        t.tab_inactive
                    },
                ));
            }

            if is_selected {
                spans.push(Span::styled(type_hint, hint_style));
            }

            // Pad the selected line to fill the entire width with bg color
            if is_selected {
                let content_len: usize = spans.iter().map(|s| s.content.width()).sum();
                if content_len < inner_width {
                    spans.push(Span::styled(" ".repeat(inner_width - content_len), sel_bg));
                }
            }

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

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

        // Hint line at the bottom
        lines.push(Line::from(vec![
            Span::styled("  j/k", t.status_bar_key),
            Span::styled(" nav  ", t.tab_inactive),
            Span::styled("Space", t.status_bar_key),
            Span::styled(" cycle  ", t.tab_inactive),
            Span::styled("Enter", t.status_bar_key),
            Span::styled(" edit  ", t.tab_inactive),
            Span::styled("x", t.status_bar_key),
            Span::styled(" clear  ", t.tab_inactive),
            Span::styled("Esc", t.status_bar_key),
            Span::styled(" back", t.tab_inactive),
        ]));

        let dirty_indicator = if self.dirty { " * " } else { " " };

        let editor = Paragraph::new(lines).block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(t.border_focused)
                .title(format!(" Edit Rule{}", dirty_indicator))
                .title_style(t.header),
        );
        frame.render_widget(editor, area);
    }

    fn render_blacklist(&mut self, frame: &mut Frame, area: Rect) {
        let t = theme();
        if let Some(ref config) = self.config {
            if config.blacklist.is_empty() {
                return;
            }
            let items: Vec<ListItem> = config
                .blacklist
                .iter()
                .map(|path| {
                    ListItem::new(Line::from(Span::styled(
                        format!("  {}", path.display()),
                        t.status_warning,
                    )))
                })
                .collect();

            let list = List::new(items).block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border)
                    .title(format!(" Blacklist ({}) ", config.blacklist.len()))
                    .title_style(t.header),
            );
            frame.render_widget(list, area);
        }
    }

    fn rule_list_title(&self) -> String {
        let count = self.plugin_options.len();
        let dirty = if self.dirty { " *" } else { "" };
        let path_display = if self.plugin_config_path.is_empty() {
            String::new()
        } else {
            // Show just the filename
            format!(
                " - {}",
                std::path::Path::new(&self.plugin_config_path)
                    .file_name()
                    .map(|n| n.to_string_lossy().to_string())
                    .unwrap_or_default()
            )
        };
        format!(" Plugin Options ({}){}{} ", count, path_display, dirty)
    }
}
