use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};

use super::{InputContext, InputResult};
use crate::commands::AppCommand;
use crate::engine::SeqCommand;
use crate::model::{FollowUp, PatternSpeed};
use crate::state::{
    ConfirmAction, EditorTarget, EuclideanField, Modal, PatternField,
    PatternPropsField, RenameTarget, ScriptField,
};

pub(super) fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
    match &mut ctx.app.ui.modal {
        Modal::Confirm { action, selected } => {
            let confirmed = *selected;
            match key.code {
                KeyCode::Char('y') | KeyCode::Char('Y') => {
                    let action = action.clone();
                    return execute_confirm(ctx, &action);
                }
                KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => {
                    ctx.dispatch(AppCommand::CloseModal);
                }
                KeyCode::Left | KeyCode::Right => {
                    if let Modal::Confirm { selected, .. } = &mut ctx.app.ui.modal {
                        *selected = !*selected;
                    }
                }
                KeyCode::Enter => {
                    if confirmed {
                        let action = action.clone();
                        return execute_confirm(ctx, &action);
                    }
                    ctx.dispatch(AppCommand::CloseModal);
                }
                _ => {}
            }
        }
        Modal::FileBrowser(state) => match key.code {
            KeyCode::Enter => {
                use crate::state::file_browser::FileBrowserMode;
                let is_save = matches!(state.mode, FileBrowserMode::Save);
                if let Some(path) = state.confirm() {
                    ctx.dispatch(AppCommand::CloseModal);
                    if is_save {
                        ctx.dispatch(AppCommand::Save(path));
                    } else {
                        let _ = ctx.seq_cmd_tx.send(SeqCommand::StopAll);
                        let _ = ctx.seq_cmd_tx.send(SeqCommand::ResetScriptState);
                        ctx.dispatch(AppCommand::Load(path));
                        super::load_project_samples(ctx);
                    }
                }
            }
            KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
            KeyCode::Tab => state.autocomplete(),
            KeyCode::Left => state.go_up(),
            KeyCode::Right => state.enter_selected(),
            KeyCode::Up => state.select_prev(12),
            KeyCode::Down => state.select_next(12),
            KeyCode::Backspace => state.backspace(),
            KeyCode::Char(c) => {
                state.input.push(c);
                state.refresh_entries();
            }
            _ => {}
        },
        Modal::Rename { target, name } => {
            match key.code {
                KeyCode::Enter => {
                    let new_name = if name.trim().is_empty() {
                        None
                    } else {
                        Some(name.clone())
                    };
                    let target = target.clone();
                    ctx.dispatch(rename_command(&target, new_name));
                    ctx.dispatch(AppCommand::CloseModal);
                }
                KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
                KeyCode::Backspace => {
                    if let Modal::Rename { name, .. } = &mut ctx.app.ui.modal {
                        name.pop();
                    }
                }
                KeyCode::Char(c) => {
                    if let Modal::Rename { name, .. } = &mut ctx.app.ui.modal {
                        name.push(c);
                    }
                }
                _ => {}
            }
        }
        Modal::SetPattern { field, input } => match key.code {
            KeyCode::Enter => {
                let field = *field;
                let (bank, pattern) = (ctx.app.editor_ctx.bank, ctx.app.editor_ctx.pattern);
                match field {
                    PatternField::Length => {
                        if let Ok(len) = input.parse::<usize>() {
                            ctx.dispatch(AppCommand::SetLength {
                                bank,
                                pattern,
                                length: len,
                            });
                            let new_len = ctx
                                .app
                                .project_state
                                .project
                                .pattern_at(bank, pattern)
                                .length;
                            ctx.dispatch(AppCommand::SetStatus(format!("Length set to {new_len}")));
                        } else {
                            ctx.dispatch(AppCommand::SetStatus("Invalid length".to_string()));
                        }
                    }
                    PatternField::Speed => {
                        if let Some(speed) = PatternSpeed::from_label(input) {
                            ctx.dispatch(AppCommand::SetSpeed {
                                bank,
                                pattern,
                                speed,
                            });
                            ctx.dispatch(AppCommand::SetStatus(format!(
                                "Speed set to {}",
                                speed.label()
                            )));
                        } else {
                            ctx.dispatch(AppCommand::SetStatus(
                                "Invalid speed (try 1/3, 2/5, 1x, 2x)".to_string(),
                            ));
                        }
                    }
                }
                ctx.dispatch(AppCommand::CloseModal);
            }
            KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
            KeyCode::Backspace => {
                input.pop();
            }
            KeyCode::Char(c) => input.push(c),
            _ => {}
        },
        Modal::SetScript { field, input } => match key.code {
            KeyCode::Enter => {
                let field = *field;
                match field {
                    ScriptField::Length => {
                        if let Ok(len) = input.parse::<usize>() {
                            ctx.dispatch(AppCommand::SetScriptLength(len));
                            let new_len = ctx.app.project_state.project.script_length;
                            ctx.dispatch(AppCommand::SetStatus(format!(
                                "Script length set to {new_len}"
                            )));
                        } else {
                            ctx.dispatch(AppCommand::SetStatus("Invalid length".to_string()));
                        }
                    }
                    ScriptField::Speed => {
                        if let Some(speed) = PatternSpeed::from_label(input) {
                            ctx.dispatch(AppCommand::SetScriptSpeed(speed));
                            ctx.dispatch(AppCommand::SetStatus(format!(
                                "Script speed set to {}",
                                speed.label()
                            )));
                        } else {
                            ctx.dispatch(AppCommand::SetStatus(
                                "Invalid speed (try 1/3, 2/5, 1x, 2x)".to_string(),
                            ));
                        }
                    }
                }
                ctx.dispatch(AppCommand::CloseModal);
            }
            KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
            KeyCode::Backspace => {
                input.pop();
            }
            KeyCode::Char(c) => input.push(c),
            _ => {}
        },
        Modal::JumpToStep(input) => match key.code {
            KeyCode::Enter => {
                if let Ok(step) = input.parse::<usize>() {
                    if step > 0 {
                        ctx.dispatch(AppCommand::GoToStep(step - 1));
                    }
                }
                ctx.dispatch(AppCommand::CloseModal);
            }
            KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
            KeyCode::Backspace => {
                input.pop();
            }
            KeyCode::Char(c) if c.is_ascii_digit() => input.push(c),
            _ => {}
        },
        Modal::SetTempo(input) => match key.code {
            KeyCode::Enter => {
                if let Ok(tempo) = input.parse::<f64>() {
                    let tempo = tempo.clamp(20.0, 300.0);
                    ctx.link.set_tempo(tempo);
                    ctx.dispatch(AppCommand::SetStatus(format!(
                        "Tempo set to {tempo:.1} BPM"
                    )));
                } else {
                    ctx.dispatch(AppCommand::SetStatus("Invalid tempo".to_string()));
                }
                ctx.dispatch(AppCommand::CloseModal);
            }
            KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
            KeyCode::Backspace => {
                input.pop();
            }
            KeyCode::Char(c) if c.is_ascii_digit() || c == '.' => input.push(c),
            _ => {}
        },
        Modal::AddSamplePath(state) => match key.code {
            KeyCode::Enter => {
                let sample_path = if let Some(entry) = state.entries.get(state.selected) {
                    if entry.is_dir && entry.name != ".." {
                        Some(state.current_dir().join(&entry.name))
                    } else if entry.is_dir {
                        state.enter_selected();
                        None
                    } else {
                        None
                    }
                } else {
                    let dir = state.current_dir();
                    if dir.is_dir() {
                        Some(dir)
                    } else {
                        None
                    }
                };
                if let Some(path) = sample_path {
                    let index = doux::sampling::scan_samples_dir(&path);
                    let count = index.len();
                    let preload_entries: Vec<(String, std::path::PathBuf)> = index
                        .iter()
                        .map(|e| (e.name.clone(), e.path.clone()))
                        .collect();
                    let _ = ctx.audio_tx.load().send(crate::engine::AudioCommand::LoadSamples(index));
                    if let Some(sf2_path) = doux::soundfont::find_sf2_file(&path) {
                        let _ = ctx.audio_tx.load().send(crate::engine::AudioCommand::LoadSoundfont(sf2_path));
                    }
                    ctx.app.audio.add_sample_path(path, count);
                    if let Some(registry) = ctx.app.audio.sample_registry.clone() {
                        let sr = ctx.app.audio.config.sample_rate;
                        std::thread::Builder::new()
                            .name("sample-preload".into())
                            .spawn(move || {
                                crate::engine::preload_sample_heads(preload_entries, sr, &registry);
                            })
                            .expect("failed to spawn preload thread");
                    }
                    ctx.app.save_settings(ctx.link);
                    ctx.dispatch(AppCommand::SetStatus(format!("Added {count} samples")));
                    ctx.dispatch(AppCommand::CloseModal);
                }
            }
            KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
            KeyCode::Tab => state.autocomplete(),
            KeyCode::Left => state.go_up(),
            KeyCode::Right => state.enter_selected(),
            KeyCode::Up => state.select_prev(14),
            KeyCode::Down => state.select_next(14),
            KeyCode::Backspace => state.backspace(),
            KeyCode::Char(c) => {
                state.input.push(c);
                state.refresh_entries();
            }
            _ => {}
        },
        Modal::Editor => {
            let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
            let shift = key.modifiers.contains(KeyModifiers::SHIFT);
            let editor = &mut ctx.app.editor_ctx.editor;

            if editor.search_active() {
                match key.code {
                    KeyCode::Esc => editor.search_clear(),
                    KeyCode::Enter => editor.search_confirm(),
                    KeyCode::Backspace => editor.search_backspace(),
                    KeyCode::Char(c) if !ctrl => editor.search_input(c),
                    _ => {}
                }
                return InputResult::Continue;
            }

            if editor.sample_finder_active() {
                match key.code {
                    KeyCode::Esc => editor.dismiss_sample_finder(),
                    KeyCode::Tab | KeyCode::Enter => editor.accept_sample_finder(),
                    KeyCode::Backspace => editor.sample_finder_backspace(),
                    KeyCode::Char('n') if ctrl => editor.sample_finder_next(),
                    KeyCode::Char('p') if ctrl => editor.sample_finder_prev(),
                    KeyCode::Char(c) if !ctrl => editor.sample_finder_input(c),
                    _ => {}
                }
                return InputResult::Continue;
            }

            match key.code {
                KeyCode::Esc => {
                    if editor.is_selecting() {
                        editor.cancel_selection();
                    } else if editor.completion_active() {
                        editor.dismiss_completion();
                    } else {
                        match ctx.app.editor_ctx.target {
                            EditorTarget::Step => {
                                ctx.dispatch(AppCommand::SaveEditorToStep);
                                ctx.dispatch(AppCommand::CompileCurrentStep);
                            }
                            EditorTarget::Prelude => {
                                ctx.dispatch(AppCommand::SavePrelude);
                                ctx.dispatch(AppCommand::EvaluatePrelude);
                                ctx.dispatch(AppCommand::ClosePreludeEditor);
                            }
                        }
                        ctx.dispatch(AppCommand::CloseModal);
                    }
                }
                KeyCode::Char('e') if ctrl => match ctx.app.editor_ctx.target {
                    EditorTarget::Step => {
                        ctx.dispatch(AppCommand::SaveEditorToStep);
                        ctx.dispatch(AppCommand::CompileCurrentStep);
                    }
                    EditorTarget::Prelude => {
                        ctx.dispatch(AppCommand::SavePrelude);
                        ctx.dispatch(AppCommand::EvaluatePrelude);
                    }
                },
                KeyCode::Char('b') if ctrl => {
                    editor.activate_sample_finder();
                }
                KeyCode::Char('f') if ctrl => {
                    editor.activate_search();
                }
                KeyCode::Char('n') if ctrl => {
                    if editor.completion_active() {
                        editor.completion_next();
                    } else if editor.sample_finder_active() {
                        editor.sample_finder_next();
                    } else {
                        editor.search_next();
                    }
                }
                KeyCode::Char('p') if ctrl => {
                    if editor.completion_active() {
                        editor.completion_prev();
                    } else if editor.sample_finder_active() {
                        editor.sample_finder_prev();
                    } else {
                        editor.search_prev();
                    }
                }
                KeyCode::Char('s') if ctrl => {
                    ctx.dispatch(AppCommand::ToggleEditorStack);
                }
                KeyCode::Char('r') if ctrl => {
                    let script = ctx.app.editor_ctx.editor.lines().join("\n");
                    match ctx
                        .app
                        .execute_script_oneshot(&script, ctx.link, ctx.audio_tx)
                    {
                        Ok(()) => ctx
                            .app
                            .ui
                            .flash("Executed", 100, crate::state::FlashKind::Info),
                        Err(e) => ctx.app.ui.flash(
                            &format!("Error: {e}"),
                            200,
                            crate::state::FlashKind::Error,
                        ),
                    }
                }
                KeyCode::Char('a') if ctrl => {
                    editor.select_all();
                }
                KeyCode::Char('c') if ctrl => {
                    ctx.app.editor_ctx.editor.copy();
                    let text = ctx.app.editor_ctx.editor.yank_text();
                    if let Ok(mut clip) = arboard::Clipboard::new() {
                        let _ = clip.set_text(text);
                    }
                }
                KeyCode::Char('x') if ctrl => {
                    ctx.app.editor_ctx.editor.cut();
                    let text = ctx.app.editor_ctx.editor.yank_text();
                    if let Ok(mut clip) = arboard::Clipboard::new() {
                        let _ = clip.set_text(text);
                    }
                }
                KeyCode::Char('v') if ctrl => {
                    if let Ok(mut clip) = arboard::Clipboard::new() {
                        if let Ok(text) = clip.get_text() {
                            ctx.app.editor_ctx.editor.set_yank_text(text);
                        }
                    }
                    ctx.app.editor_ctx.editor.paste();
                }
                KeyCode::Left | KeyCode::Right | KeyCode::Up | KeyCode::Down if shift => {
                    if !editor.is_selecting() {
                        editor.start_selection();
                    }
                    editor.input(Event::Key(key));
                }
                _ => {
                    editor.input(Event::Key(key));
                }
            }

            if ctx.app.editor_ctx.show_stack {
                crate::services::stack_preview::update_cache(&ctx.app.editor_ctx);
            }
        }
        Modal::PatternProps {
            bank,
            pattern,
            field,
            name,
            description,
            length,
            speed,
            quantization,
            sync_mode,
            follow_up,
        } => {
            let (bank, pattern) = (*bank, *pattern);
            let is_chain = matches!(follow_up, FollowUp::Chain { .. });
            match key.code {
                KeyCode::Up => *field = field.prev(is_chain),
                KeyCode::Down | KeyCode::Tab => *field = field.next(is_chain),
                KeyCode::Left => match field {
                    PatternPropsField::Speed => *speed = speed.prev(),
                    PatternPropsField::Quantization => *quantization = quantization.prev(),
                    PatternPropsField::SyncMode => *sync_mode = sync_mode.toggle(),
                    PatternPropsField::FollowUp => *follow_up = follow_up.prev_mode(),
                    PatternPropsField::ChainBank => {
                        if let FollowUp::Chain { bank: b, .. } = follow_up {
                            *b = b.saturating_sub(1);
                        }
                    }
                    PatternPropsField::ChainPattern => {
                        if let FollowUp::Chain { pattern: p, .. } = follow_up {
                            *p = p.saturating_sub(1);
                        }
                    }
                    _ => {}
                },
                KeyCode::Right => match field {
                    PatternPropsField::Speed => *speed = speed.next(),
                    PatternPropsField::Quantization => *quantization = quantization.next(),
                    PatternPropsField::SyncMode => *sync_mode = sync_mode.toggle(),
                    PatternPropsField::FollowUp => *follow_up = follow_up.next_mode(),
                    PatternPropsField::ChainBank => {
                        if let FollowUp::Chain { bank: b, .. } = follow_up {
                            *b = (*b + 1).min(31);
                        }
                    }
                    PatternPropsField::ChainPattern => {
                        if let FollowUp::Chain { pattern: p, .. } = follow_up {
                            *p = (*p + 1).min(31);
                        }
                    }
                    _ => {}
                },
                KeyCode::Char(c) => match field {
                    PatternPropsField::Name => name.push(c),
                    PatternPropsField::Description => description.push(c),
                    PatternPropsField::Length if c.is_ascii_digit() => length.push(c),
                    _ => {}
                },
                KeyCode::Backspace => match field {
                    PatternPropsField::Name => {
                        name.pop();
                    }
                    PatternPropsField::Description => {
                        description.pop();
                    }
                    PatternPropsField::Length => {
                        length.pop();
                    }
                    _ => {}
                },
                KeyCode::Enter => {
                    let name_val = if name.is_empty() {
                        None
                    } else {
                        Some(name.clone())
                    };
                    let desc_val = if description.is_empty() {
                        None
                    } else {
                        Some(description.clone())
                    };
                    let length_val = length.parse().ok();
                    let speed_val = *speed;
                    let quant_val = *quantization;
                    let sync_val = *sync_mode;
                    let follow_up_val = *follow_up;
                    ctx.dispatch(AppCommand::StagePatternProps {
                        bank,
                        pattern,
                        name: name_val,
                        description: desc_val,
                        length: length_val,
                        speed: speed_val,
                        quantization: quant_val,
                        sync_mode: sync_val,
                        follow_up: follow_up_val,
                    });
                    ctx.dispatch(AppCommand::CloseModal);
                }
                KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
                _ => {}
            }
        }
        Modal::KeybindingsHelp { scroll } => {
            let bindings_count = crate::views::keybindings::bindings_for(ctx.app.page, ctx.app.plugin_mode).len();
            match key.code {
                KeyCode::Esc | KeyCode::Char('?') => ctx.dispatch(AppCommand::CloseModal),
                KeyCode::Up | KeyCode::Char('k') => {
                    *scroll = scroll.saturating_sub(1);
                }
                KeyCode::Down | KeyCode::Char('j') => {
                    *scroll = (*scroll + 1).min(bindings_count.saturating_sub(1));
                }
                KeyCode::PageUp => {
                    *scroll = scroll.saturating_sub(10);
                }
                KeyCode::PageDown => {
                    *scroll = (*scroll + 10).min(bindings_count.saturating_sub(1));
                }
                _ => {}
            }
        }
        Modal::EuclideanDistribution {
            bank,
            pattern,
            source_step,
            field,
            pulses,
            steps,
            rotation,
        } => {
            let (bank_val, pattern_val, source_step_val) = (*bank, *pattern, *source_step);
            match key.code {
                KeyCode::Up => *field = field.prev(),
                KeyCode::Down | KeyCode::Tab => *field = field.next(),
                KeyCode::Left => {
                    let target = match field {
                        EuclideanField::Pulses => pulses,
                        EuclideanField::Steps => steps,
                        EuclideanField::Rotation => rotation,
                    };
                    if let Ok(val) = target.parse::<usize>() {
                        *target = val.saturating_sub(1).to_string();
                    }
                }
                KeyCode::Right => {
                    let target = match field {
                        EuclideanField::Pulses => pulses,
                        EuclideanField::Steps => steps,
                        EuclideanField::Rotation => rotation,
                    };
                    if let Ok(val) = target.parse::<usize>() {
                        *target = (val + 1).min(1024).to_string();
                    }
                }
                KeyCode::Char(c) if c.is_ascii_digit() => match field {
                    EuclideanField::Pulses => pulses.push(c),
                    EuclideanField::Steps => steps.push(c),
                    EuclideanField::Rotation => rotation.push(c),
                },
                KeyCode::Backspace => match field {
                    EuclideanField::Pulses => {
                        pulses.pop();
                    }
                    EuclideanField::Steps => {
                        steps.pop();
                    }
                    EuclideanField::Rotation => {
                        rotation.pop();
                    }
                },
                KeyCode::Enter => {
                    let pulses_val: usize = pulses.parse().unwrap_or(0);
                    let steps_val: usize = steps.parse().unwrap_or(0);
                    let rotation_val: usize = rotation.parse().unwrap_or(0);
                    if pulses_val > 0 && steps_val > 0 && pulses_val <= steps_val {
                        ctx.dispatch(AppCommand::ApplyEuclideanDistribution {
                            bank: bank_val,
                            pattern: pattern_val,
                            source_step: source_step_val,
                            pulses: pulses_val,
                            steps: steps_val,
                            rotation: rotation_val,
                        });
                        ctx.dispatch(AppCommand::CloseModal);
                    } else {
                        ctx.dispatch(AppCommand::SetStatus(
                            "Invalid: pulses must be > 0 and <= steps".to_string(),
                        ));
                    }
                }
                KeyCode::Esc => ctx.dispatch(AppCommand::CloseModal),
                _ => {}
            }
        }
        Modal::Onboarding { .. } => {
            let pages = crate::model::onboarding::for_page(ctx.app.page);
            let page_count = pages.len();
            match key.code {
                KeyCode::Right | KeyCode::Char('l') if page_count > 1 => {
                    if let Modal::Onboarding { page } = &mut ctx.app.ui.modal {
                        if *page + 1 < page_count {
                            *page += 1;
                        }
                    }
                }
                KeyCode::Left | KeyCode::Char('h') if page_count > 1 => {
                    if let Modal::Onboarding { page } = &mut ctx.app.ui.modal {
                        *page = page.saturating_sub(1);
                    }
                }
                KeyCode::Char('?') | KeyCode::F(1) => {
                    if let Some(topic) = ctx.app.page.help_topic_index() {
                        ctx.dispatch(AppCommand::GoToHelpTopic(topic));
                    } else {
                        ctx.dispatch(AppCommand::CloseModal);
                    }
                }
                KeyCode::Enter => {
                    ctx.dispatch(AppCommand::DismissOnboarding);
                    ctx.dispatch(AppCommand::CloseModal);
                    ctx.app.save_settings(ctx.link);
                }
                _ => ctx.dispatch(AppCommand::CloseModal),
            }
        }
        Modal::None => unreachable!(),
    }
    InputResult::Continue
}

fn execute_confirm(ctx: &mut InputContext, action: &ConfirmAction) -> InputResult {
    match action {
        ConfirmAction::Quit => return InputResult::Quit,
        ConfirmAction::DeleteStep { bank, pattern, step } => {
            ctx.dispatch(AppCommand::DeleteStep { bank: *bank, pattern: *pattern, step: *step });
        }
        ConfirmAction::DeleteSteps { bank, pattern, steps } => {
            ctx.dispatch(AppCommand::DeleteSteps { bank: *bank, pattern: *pattern, steps: steps.clone() });
        }
        ConfirmAction::ResetPattern { bank, pattern } => {
            ctx.dispatch(AppCommand::ResetPattern { bank: *bank, pattern: *pattern });
        }
        ConfirmAction::ResetBank { bank } => {
            ctx.dispatch(AppCommand::ResetBank { bank: *bank });
        }
        ConfirmAction::ResetPatterns { bank, patterns } => {
            ctx.dispatch(AppCommand::ResetPatterns { bank: *bank, patterns: patterns.clone() });
        }
        ConfirmAction::ResetBanks { banks } => {
            ctx.dispatch(AppCommand::ResetBanks { banks: banks.clone() });
        }
        ConfirmAction::ImportBank { bank } => {
            ctx.dispatch(AppCommand::ImportBank { bank: *bank });
        }
    }
    ctx.dispatch(AppCommand::CloseModal);
    InputResult::Continue
}

fn rename_command(target: &RenameTarget, name: Option<String>) -> AppCommand {
    match target {
        RenameTarget::Bank { bank } => AppCommand::RenameBank { bank: *bank, name },
        RenameTarget::Pattern { bank, pattern } => AppCommand::RenamePattern {
            bank: *bank, pattern: *pattern, name,
        },
        RenameTarget::Step { bank, pattern, step } => AppCommand::RenameStep {
            bank: *bank, pattern: *pattern, step: *step, name,
        },
        RenameTarget::DescribePattern { bank, pattern } => AppCommand::DescribePattern {
            bank: *bank, pattern: *pattern, description: name,
        },
    }
}
