use std::path::PathBuf;

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::layout::{Constraint, Direction, Layout};
use ratatui::Frame;
use tokio::sync::mpsc;

use crate::action::Action;
use crate::components::config_tab::ConfigTab;
use crate::components::help::render_help;
use crate::components::log_tab::LogTab;
use crate::components::paths_tab::PathsTab;
use crate::components::plugins_tab::PluginsTab;
use crate::components::popup::{InputTarget, Popup};
use crate::components::status_bar::render_status_bar;
use crate::components::tab_bar::{render_tab_bar, Tab};
use crate::components::Component;
use crate::event::Event;
use crate::yabridge::types::{PluginOptions, SyncOptions};
use crate::yabridge::{commands, config};

/// Main application state
pub struct App {
    /// Is the application running?
    pub running: bool,
    /// Currently active tab
    pub active_tab: Tab,
    /// Is a sync operation in progress?
    pub syncing: bool,
    /// Status message to display
    pub status_message: Option<String>,
    /// Ticks remaining before status_message is cleared
    status_message_ttl: u8,
    /// Show help overlay
    pub show_help: bool,
    /// Help overlay scroll offset
    pub help_scroll: u16,

    // ── Components ──────────────────────────
    pub plugins_tab: PluginsTab,
    pub paths_tab: PathsTab,
    pub config_tab: ConfigTab,
    pub log_tab: LogTab,
    pub popup: Popup,

    // ── Async channels ─────────────────────
    action_tx: mpsc::UnboundedSender<Action>,
    action_rx: mpsc::UnboundedReceiver<Action>,
}

impl App {
    pub fn new() -> Self {
        let (action_tx, action_rx) = mpsc::unbounded_channel();

        Self {
            running: true,
            active_tab: Tab::Plugins,
            syncing: false,
            status_message: None,
            status_message_ttl: 0,
            show_help: false,
            help_scroll: 0,
            plugins_tab: PluginsTab::new(),
            paths_tab: PathsTab::new(),
            config_tab: ConfigTab::new(),
            log_tab: LogTab::new(),
            popup: Popup::new(),
            action_tx,
            action_rx,
        }
    }

    /// Await the next action from background tasks
    pub async fn recv_action(&mut self) -> Option<Action> {
        self.action_rx.recv().await
    }

    /// Set a status message that auto-clears after ~5 seconds
    fn set_status(&mut self, msg: impl Into<String>) {
        self.status_message = Some(msg.into());
        self.status_message_ttl = 20; // 20 ticks × 250ms = 5s
    }

    /// Initial data load
    pub async fn init(&mut self) {
        self.log_tab
            .add_line("yabridge-tui starting...".to_string());

        // Check if yabridgectl is available
        if !commands::is_yabridgectl_available().await {
            self.log_tab
                .add_line("ERROR: yabridgectl not found in PATH!".to_string());
            self.set_status("yabridgectl not found!");
            self.popup.show_message(
                "yabridgectl not found".to_string(),
                "yabridgectl is not installed or not in PATH.\n\nInstall yabridge first:\n  https://github.com/robbert-vdh/yabridge".to_string(),
                true,
            );
            return;
        }

        self.load_config();
        self.refresh_plugins().await;
        self.refresh_paths().await;
        self.log_tab.add_line("Ready.".to_string());
    }

    /// Load yabridgectl config
    fn load_config(&mut self) {
        let config_path = config::config_path();
        self.config_tab.config_path = config_path.display().to_string();

        match config::load_config() {
            Ok(cfg) => {
                self.log_tab.add_line(format!(
                    "Config loaded: {} plugin dirs",
                    cfg.plugin_dirs.len()
                ));
                self.config_tab.set_config(cfg);
            }
            Err(e) => {
                self.log_tab.add_line(format!("Config load error: {}", e));
            }
        }

        // Try to load yabridge.toml from common locations
        let Some(home) = dirs::home_dir() else { return };
        for dir in &[".vst/yabridge", ".vst3/yabridge", ".clap/yabridge"] {
            let toml_path = home.join(dir).join("yabridge.toml");
            if toml_path.exists() {
                self.config_tab.plugin_config_path = toml_path.display().to_string();
                match config::load_plugin_options(&toml_path) {
                    Ok(opts) => {
                        if !opts.is_empty() {
                            self.log_tab.add_line(format!(
                                "Loaded {} plugin options from {}",
                                opts.len(),
                                toml_path.display()
                            ));
                            self.config_tab.set_plugin_options(opts);
                            break;
                        }
                    }
                    Err(e) => {
                        self.log_tab.add_line(format!(
                            "Warning: failed to parse {}: {}",
                            toml_path.display(),
                            e
                        ));
                    }
                }
            }
        }
    }

    /// Refresh plugin list from yabridgectl status
    async fn refresh_plugins(&mut self) {
        self.plugins_tab.loading = true;
        self.log_tab.add_command("yabridgectl status");

        match commands::get_status().await {
            Ok(plugins) => {
                self.log_tab
                    .add_line(format!("Found {} plugins", plugins.len()));
                self.plugins_tab.set_plugins(plugins);
            }
            Err(e) => {
                let msg = format!("Failed to get status: {}", e);
                self.log_tab.add_line(msg.clone());
                self.set_status(&msg);
                self.plugins_tab.loading = false;
            }
        }
    }

    /// Refresh paths list
    async fn refresh_paths(&mut self) {
        self.paths_tab.loading = true;
        match commands::list_paths().await {
            Ok(paths) => {
                self.paths_tab.set_paths(paths);
            }
            Err(e) => {
                self.log_tab
                    .add_line(format!("Failed to list paths: {}", e));
                self.paths_tab.loading = false;
            }
        }
    }

    /// Handle an incoming event and dispatch actions
    pub fn handle_event(&mut self, event: Event) -> Vec<Action> {
        let mut actions = Vec::new();

        match event {
            Event::Tick => {
                actions.push(Action::Tick);
            }
            Event::Key(key) => {
                // Popup gets priority
                if self.popup.is_open() {
                    let action = self.popup.handle_key(key);
                    actions.push(action);
                    return actions;
                }

                // Help overlay — scrollable
                if self.show_help {
                    match key.code {
                        KeyCode::Esc | KeyCode::Char('?') | KeyCode::Char('q') => {
                            self.show_help = false;
                            self.help_scroll = 0;
                        }
                        KeyCode::Char('j') | KeyCode::Down => {
                            self.help_scroll = self.help_scroll.saturating_add(1);
                        }
                        KeyCode::Char('k') | KeyCode::Up => {
                            self.help_scroll = self.help_scroll.saturating_sub(1);
                        }
                        KeyCode::Char('d') | KeyCode::PageDown => {
                            self.help_scroll = self.help_scroll.saturating_add(10);
                        }
                        KeyCode::Char('u') | KeyCode::PageUp => {
                            self.help_scroll = self.help_scroll.saturating_sub(10);
                        }
                        KeyCode::Char('g') | KeyCode::Home => {
                            self.help_scroll = 0;
                        }
                        KeyCode::Char('G') | KeyCode::End => {
                            self.help_scroll = u16::MAX; // clamped at render
                        }
                        _ => {}
                    }
                    return actions;
                }

                // If a tab filter is active or config is in edit mode,
                // delegate everything to the tab component
                // (except Ctrl+C which always quits)
                let filter_active = match self.active_tab {
                    Tab::Plugins => self.plugins_tab.filter_active,
                    Tab::Paths => self.paths_tab.filter_active,
                    Tab::Log => self.log_tab.filter_active,
                    Tab::Config => self.config_tab.is_editing(),
                };
                if filter_active {
                    if let KeyEvent {
                        code: KeyCode::Char('c'),
                        modifiers: KeyModifiers::CONTROL,
                        ..
                    } = key
                    {
                        actions.push(Action::Quit);
                        return actions;
                    }
                    let action = match self.active_tab {
                        Tab::Plugins => self.plugins_tab.handle_key(key),
                        Tab::Paths => self.paths_tab.handle_key(key),
                        Tab::Config => self.config_tab.handle_key(key),
                        Tab::Log => self.log_tab.handle_key(key),
                    };
                    actions.push(action);
                    return actions;
                }

                // Global keys
                if let KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                    ..
                } = key
                {
                    actions.push(Action::Quit);
                    return actions;
                }

                match key.code {
                    KeyCode::Char('q') => actions.push(Action::Quit),
                    KeyCode::Tab | KeyCode::Char('l') | KeyCode::Right => {
                        actions.push(Action::NextTab)
                    }
                    KeyCode::BackTab | KeyCode::Char('h') | KeyCode::Left => {
                        actions.push(Action::PrevTab)
                    }
                    KeyCode::Char('?') => actions.push(Action::ToggleHelp),
                    KeyCode::Char('T') => actions.push(Action::CycleTheme),
                    KeyCode::Char('1') => {
                        self.active_tab = Tab::Plugins;
                    }
                    KeyCode::Char('2') => {
                        self.active_tab = Tab::Paths;
                    }
                    KeyCode::Char('3') => {
                        self.active_tab = Tab::Config;
                    }
                    KeyCode::Char('4') => {
                        self.active_tab = Tab::Log;
                    }
                    _ => {
                        // Delegate to active tab component
                        let action = match self.active_tab {
                            Tab::Plugins => self.plugins_tab.handle_key(key),
                            Tab::Paths => self.paths_tab.handle_key(key),
                            Tab::Config => self.config_tab.handle_key(key),
                            Tab::Log => self.log_tab.handle_key(key),
                        };
                        actions.push(action);
                    }
                }
            }
            Event::Resize(_, _) => {}
            Event::Mouse(_) => {}
        }

        actions
    }

    /// Process an action, potentially spawning background tasks
    pub fn process_action(&mut self, action: Action) {
        match action {
            Action::Noop => {}
            Action::Tick => {
                if self.status_message.is_some() {
                    if self.status_message_ttl == 0 {
                        self.status_message = None;
                    } else {
                        self.status_message_ttl = self.status_message_ttl.saturating_sub(1);
                    }
                }
            }
            Action::Quit => {
                self.running = false;
            }
            Action::NextTab => {
                self.active_tab = self.active_tab.next();
            }
            Action::PrevTab => {
                self.active_tab = self.active_tab.prev();
            }
            Action::ToggleHelp => {
                self.show_help = !self.show_help;
            }
            Action::CycleTheme => {
                let name = crate::theme::cycle_theme();
                crate::theme::save_theme_preference(name);
                self.set_status(format!("Theme: {}", name.display()));
            }
            Action::SyncPlugins(options) => {
                self.start_sync(options);
            }
            Action::SyncOutputLine(line) => {
                self.log_tab.add_line(line);
            }
            Action::SyncComplete(result) => {
                self.syncing = false;
                match result {
                    Ok(msg) => {
                        self.log_tab.add_line(msg);
                        self.set_status("Sync completed!");
                        // Refresh plugin list after sync
                        self.process_action(Action::RefreshPlugins);
                    }
                    Err(msg) => {
                        self.log_tab.add_line(format!("Sync error: {}", msg));
                        self.set_status(format!("Sync failed: {}", msg));
                        self.popup.show_message("Sync Error".to_string(), msg, true);
                    }
                }
            }
            Action::RefreshPlugins => {
                self.plugins_tab.loading = true;
                self.log_tab.add_command("yabridgectl status");
                let tx = self.action_tx.clone();
                tokio::spawn(async move {
                    match commands::get_status().await {
                        Ok(plugins) => {
                            let _ = tx.send(Action::PluginsLoaded(plugins));
                        }
                        Err(e) => {
                            let _ = tx.send(Action::PluginsError(e.to_string()));
                        }
                    }
                });
            }
            Action::PluginsLoaded(plugins) => {
                self.log_tab
                    .add_line(format!("Found {} plugins", plugins.len()));
                self.plugins_tab.set_plugins(plugins);
            }
            Action::PluginsError(msg) => {
                self.log_tab
                    .add_line(format!("Failed to get status: {}", msg));
                self.set_status(format!("Refresh failed: {}", msg));
                self.plugins_tab.loading = false;
            }
            Action::RefreshPaths => {
                self.paths_tab.loading = true;
                let tx = self.action_tx.clone();
                tokio::spawn(async move {
                    match commands::list_paths().await {
                        Ok(paths) => {
                            let _ = tx.send(Action::PathsLoaded(paths));
                        }
                        Err(e) => {
                            let _ = tx.send(Action::PathsError(e.to_string()));
                        }
                    }
                });
            }
            Action::PathsLoaded(paths) => {
                self.log_tab
                    .add_line(format!("Loaded {} paths", paths.len()));
                self.paths_tab.set_paths(paths);
            }
            Action::PathsError(msg) => {
                self.log_tab
                    .add_line(format!("Failed to list paths: {}", msg));
                self.paths_tab.loading = false;
            }
            Action::AddPath(path) => {
                let tx = self.action_tx.clone();
                self.log_tab
                    .add_command(&format!("yabridgectl add {}", path.display()));
                tokio::spawn(async move {
                    match commands::add_path(&path).await {
                        Ok(msg) => {
                            let _ = tx.send(Action::PathAdded(Ok(msg)));
                        }
                        Err(e) => {
                            let _ = tx.send(Action::PathAdded(Err(e.to_string())));
                        }
                    }
                });
            }
            Action::PathAdded(result) => match result {
                Ok(msg) => {
                    self.log_tab.add_line(msg);
                    self.set_status("Path added!");
                    self.load_config();
                    self.process_action(Action::RefreshPaths);
                }
                Err(msg) => {
                    self.log_tab.add_line(format!("Error: {}", msg));
                    self.popup.show_message("Error".to_string(), msg, true);
                }
            },
            Action::RemovePath(path) => {
                let tx = self.action_tx.clone();
                self.log_tab
                    .add_command(&format!("yabridgectl rm {}", path.display()));
                tokio::spawn(async move {
                    match commands::remove_path(&path).await {
                        Ok(msg) => {
                            let _ = tx.send(Action::PathRemoved(Ok(msg)));
                        }
                        Err(e) => {
                            let _ = tx.send(Action::PathRemoved(Err(e.to_string())));
                        }
                    }
                });
            }
            Action::PathRemoved(result) => match result {
                Ok(msg) => {
                    self.log_tab.add_line(msg);
                    self.set_status("Path removed!");
                    self.load_config();
                    self.process_action(Action::RefreshPaths);
                }
                Err(msg) => {
                    self.log_tab.add_line(format!("Error: {}", msg));
                    self.popup.show_message("Error".to_string(), msg, true);
                }
            },
            // ── Config actions ────────────────────────
            Action::RefreshConfig => {
                self.load_config();
                self.log_tab.add_line("Config reloaded.".to_string());
                self.set_status("Config reloaded");
            }
            Action::SaveConfig(path, options) => {
                let path_clone = path.clone();
                let options_clone = options.clone();
                let tx = self.action_tx.clone();
                self.log_tab
                    .add_line(format!("Saving config to {}...", path));
                tokio::spawn(async move {
                    let path_buf = PathBuf::from(&path_clone);
                    match config::save_plugin_options(&path_buf, &options_clone) {
                        Ok(()) => {
                            let _ = tx.send(Action::ConfigSaved(Ok(format!(
                                "Config saved to {}",
                                path_clone
                            ))));
                        }
                        Err(e) => {
                            let _ = tx.send(Action::ConfigSaved(Err(e.to_string())));
                        }
                    }
                });
            }
            Action::ConfigSaved(result) => match result {
                Ok(msg) => {
                    self.log_tab.add_line(msg.clone());
                    self.set_status("Config saved!");
                    self.config_tab.dirty = false;
                }
                Err(msg) => {
                    self.log_tab.add_line(format!("Config save error: {}", msg));
                    self.popup.show_message("Save Error".to_string(), msg, true);
                }
            },
            Action::AddConfigRule(pattern) => {
                let pattern = pattern.trim().to_string();
                if pattern.is_empty() {
                    return;
                }
                // Check for duplicate pattern
                if self
                    .config_tab
                    .plugin_options
                    .iter()
                    .any(|o| o.pattern == pattern)
                {
                    self.popup.show_message(
                        "Duplicate".to_string(),
                        format!("A rule for pattern '{}' already exists.", pattern),
                        true,
                    );
                    return;
                }
                let new_opt = PluginOptions {
                    pattern: pattern.clone(),
                    ..Default::default()
                };
                self.config_tab.plugin_options.push(new_opt);
                // Select the newly added rule
                let new_idx = self.config_tab.plugin_options.len() - 1;
                self.config_tab.options_state.select(Some(new_idx));
                self.config_tab.dirty = true;
                self.log_tab.add_line(format!(
                    "Added config rule: [{}]",
                    self.config_tab.plugin_options[new_idx].pattern
                ));
            }
            Action::DeleteConfigRule(idx) => {
                if idx < self.config_tab.plugin_options.len() {
                    let pattern = self.config_tab.plugin_options[idx].pattern.clone();
                    self.config_tab.plugin_options.remove(idx);
                    self.config_tab.dirty = true;
                    // Fix selection
                    if self.config_tab.plugin_options.is_empty() {
                        self.config_tab.options_state.select(None);
                    } else if idx >= self.config_tab.plugin_options.len() {
                        self.config_tab
                            .options_state
                            .select(Some(self.config_tab.plugin_options.len() - 1));
                    }
                    self.log_tab
                        .add_line(format!("Deleted config rule: [{}]", pattern));
                }
            }

            Action::ShowConfirm {
                message,
                on_confirm,
            } => {
                self.popup.show_confirm(message, *on_confirm);
            }
            Action::ShowInput(prompt) => {
                if self.active_tab == Tab::Config {
                    if self.config_tab.is_editing() {
                        // Editing a field value — use plain input
                        if let Some(rule_idx) = self.config_tab.options_state.selected() {
                            let field_idx = self.config_tab.field_index();
                            let current = self.config_tab.plugin_options[rule_idx]
                                .get_field_display(field_idx);
                            let initial = if current == "-" {
                                String::new()
                            } else {
                                current
                            };
                            self.popup.show_input_plain(
                                prompt,
                                initial,
                                InputTarget::EditConfigField(rule_idx, field_idx),
                            );
                        }
                    } else if prompt.starts_with("Edit pattern") {
                        // Editing an existing rule's pattern
                        if let Some(rule_idx) = self.config_tab.options_state.selected() {
                            let current = self.config_tab.plugin_options[rule_idx].pattern.clone();
                            let candidates = self.plugins_tab.plugin_filenames();
                            self.popup.show_input_with_candidates(
                                prompt,
                                current,
                                InputTarget::EditConfigPattern(rule_idx),
                                candidates,
                            );
                        }
                    } else {
                        // Adding a new rule — autocomplete with plugin filenames
                        let candidates = self.plugins_tab.plugin_filenames();
                        self.popup.show_input_with_candidates(
                            prompt,
                            String::new(),
                            InputTarget::AddConfigRule,
                            candidates,
                        );
                    }
                } else if self.active_tab == Tab::Paths {
                    // Open file browser for directory selection
                    self.popup.show_file_browser();
                } else {
                    self.popup.show_input(prompt, InputTarget::AddPath);
                }
            }
            Action::EditConfigPattern(rule_idx, new_pattern) => {
                let new_pattern = new_pattern.trim().to_string();
                if new_pattern.is_empty() {
                    self.popup.show_message(
                        "Invalid".to_string(),
                        "Pattern cannot be empty.".to_string(),
                        true,
                    );
                    return;
                }
                if rule_idx < self.config_tab.plugin_options.len() {
                    // Check for duplicates (but allow keeping the same pattern)
                    let old_pattern = &self.config_tab.plugin_options[rule_idx].pattern;
                    if new_pattern != *old_pattern
                        && self
                            .config_tab
                            .plugin_options
                            .iter()
                            .any(|o| o.pattern == new_pattern)
                    {
                        self.popup.show_message(
                            "Duplicate".to_string(),
                            format!("A rule for pattern '{}' already exists.", new_pattern),
                            true,
                        );
                        return;
                    }
                    let old = self.config_tab.plugin_options[rule_idx].pattern.clone();
                    self.config_tab.plugin_options[rule_idx].pattern = new_pattern.clone();
                    self.config_tab.dirty = true;
                    self.log_tab
                        .add_line(format!("Renamed rule [{}] -> [{}]", old, new_pattern));
                }
            }
            Action::EditConfigField(rule_idx, field_idx, value) => {
                if rule_idx < self.config_tab.plugin_options.len() {
                    let opt = &mut self.config_tab.plugin_options[rule_idx];
                    if PluginOptions::is_string_field(field_idx) {
                        opt.set_string(field_idx, value);
                        self.config_tab.dirty = true;
                    } else if PluginOptions::is_numeric_field(field_idx) {
                        if value.trim().is_empty() {
                            opt.set_numeric(field_idx, None);
                        } else if let Ok(num) = value.trim().parse::<u32>() {
                            opt.set_numeric(field_idx, Some(num));
                        } else {
                            self.popup.show_message(
                                "Invalid".to_string(),
                                format!("'{}' is not a valid number.", value),
                                true,
                            );
                            return;
                        }
                        self.config_tab.dirty = true;
                    }
                }
            }
        }
    }

    /// Start a sync operation in the background
    fn start_sync(&mut self, options: SyncOptions) {
        if self.syncing {
            self.set_status("Sync already in progress...");
            return;
        }

        self.syncing = true;
        self.set_status("Syncing...");

        let mut cmd_parts = vec!["yabridgectl sync".to_string()];
        if options.force {
            cmd_parts.push("--force".to_string());
        }
        if options.prune {
            cmd_parts.push("--prune".to_string());
        }
        if options.verbose {
            cmd_parts.push("--verbose".to_string());
        }
        self.log_tab.add_command(&cmd_parts.join(" "));

        // Switch to log tab to show progress
        self.active_tab = Tab::Log;

        let tx = self.action_tx.clone();
        let (output_tx, mut output_rx) = mpsc::unbounded_channel::<String>();

        // Forward output lines to app actions
        let tx2 = tx.clone();
        tokio::spawn(async move {
            while let Some(line) = output_rx.recv().await {
                let _ = tx2.send(Action::SyncOutputLine(line));
            }
        });

        // Run sync in background
        tokio::spawn(async move {
            let result = commands::sync_plugins(options, output_tx).await;
            match result {
                Ok(()) => {
                    let _ = tx.send(Action::SyncComplete(Ok(
                        "Sync completed successfully.".to_string()
                    )));
                }
                Err(e) => {
                    let _ = tx.send(Action::SyncComplete(Err(e.to_string())));
                }
            }
        });
    }

    /// Render the entire application
    pub fn render(&mut self, frame: &mut Frame) {
        let area = frame.area();

        // Apply theme background to the entire terminal area
        let t = crate::theme::theme();
        let bg_block = ratatui::widgets::Block::default()
            .style(ratatui::style::Style::default().bg(t.bg).fg(t.fg));
        frame.render_widget(bg_block, area);

        // Main layout: tab bar + content + status bar
        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(3), // Tab bar
                Constraint::Min(1),    // Content
                Constraint::Length(1), // Status bar
            ])
            .split(area);

        // Tab bar
        render_tab_bar(frame, chunks[0], self.active_tab);

        // Content area - delegate to active tab
        match self.active_tab {
            Tab::Plugins => self.plugins_tab.render(frame, chunks[1]),
            Tab::Paths => self.paths_tab.render(frame, chunks[1]),
            Tab::Config => self.config_tab.render(frame, chunks[1]),
            Tab::Log => self.log_tab.render(frame, chunks[1]),
        }

        // Status bar
        render_status_bar(
            frame,
            chunks[2],
            self.active_tab,
            self.syncing,
            self.status_message.as_deref(),
            self.config_tab.is_editing(),
            self.config_tab.dirty,
        );

        // Popup overlay (renders on top of everything)
        self.popup.render(frame, area);

        // Help overlay
        if self.show_help {
            render_help(frame, area, self.help_scroll);
        }
    }
}
