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

use crate::state::{App, Focus, Mode, ThemeName};

pub fn handle(app: &mut App, key: KeyEvent) {
    if app.show_help {
        match key.code {
            KeyCode::Esc | KeyCode::Char('?') | KeyCode::Char('q') => {
                app.show_help = false;
            }
            _ => {}
        }
        return;
    }
    if app.show_comments_list {
        handle_comments_list(app, key);
        return;
    }
    if app.preview_mode {
        match key.code {
            KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('r') => {
                app.preview_mode = false;
            }
            _ => {}
        }
        return;
    }
    if app.show_revision_selector {
        handle_revision_selector(app, key);
        return;
    }
    match app.mode {
        Mode::Normal => handle_normal(app, key),
        Mode::Insert => handle_insert(app, key),
        Mode::Search => handle_search(app, key),
        Mode::FileFilter => handle_file_filter(app, key),
        Mode::Visual => handle_visual(app, key),
    }
}

fn handle_revision_selector(app: &mut App, key: KeyEvent) {
    match (key.code, key.modifiers) {
        (KeyCode::Down, _) | (KeyCode::Char('n'), KeyModifiers::CONTROL) => {
            app.revision_selector_next();
            return;
        }
        (KeyCode::Up, _) | (KeyCode::Char('p'), KeyModifiers::CONTROL) => {
            app.revision_selector_prev();
            return;
        }
        _ => {}
    }
    handle_line_input(
        app,
        key,
        App::close_revision_selector,
        App::confirm_revision_selector,
        Some(App::on_revision_filter_change),
    );
}

fn handle_comments_list(app: &mut App, key: KeyEvent) {
    if app.pending_d {
        app.pending_d = false;
        if matches!(key.code, KeyCode::Char('d')) {
            app.delete_selected_comment_in_list();
            return;
        }
    }
    match (key.code, key.modifiers) {
        (KeyCode::Esc, _)
        | (KeyCode::Char('q'), _)
        | (KeyCode::Char('C'), _)
        | (KeyCode::Char('c'), KeyModifiers::CONTROL) => app.close_comments_list(),
        (KeyCode::Char('j'), _) | (KeyCode::Down, _) => app.comments_list_next(),
        (KeyCode::Char('k'), _) | (KeyCode::Up, _) => app.comments_list_prev(),
        (KeyCode::Enter, _) => app.jump_to_selected_comment(),
        (KeyCode::Char('d'), KeyModifiers::NONE) => app.pending_d = true,
        (KeyCode::Char('y'), KeyModifiers::NONE) => copy_selected_comment_in_list(app),
        (KeyCode::Char('Y'), _) => copy_all_comments(app),
        _ => {}
    }
}

fn copy_selected_comment_in_list(app: &mut App) {
    let Some(c) = app.selected_comment_in_list().cloned() else {
        return;
    };
    let text = app.format_comment_full(&c);
    if let Ok(mut clipboard) = arboard::Clipboard::new() {
        let _ = clipboard.set_text(&text);
        app.set_flash("Copied comment".to_string());
    }
}

/// Shared single-line text input handler. Calls `on_cancel` for Esc, `on_submit` for Enter,
/// and `on_change` after any mutation (backspace, clear, char input).
fn handle_line_input(
    app: &mut App,
    key: KeyEvent,
    on_cancel: fn(&mut App),
    on_submit: fn(&mut App),
    on_change: Option<fn(&mut App)>,
) {
    match (key.code, key.modifiers) {
        (KeyCode::Esc, _) | (KeyCode::Char('c'), KeyModifiers::CONTROL) => on_cancel(app),
        (KeyCode::Enter, _) => on_submit(app),
        (KeyCode::Backspace, _) | (KeyCode::Char('h'), KeyModifiers::CONTROL) => {
            app.input_delete_backward();
            if let Some(f) = on_change {
                f(app);
            }
        }
        (KeyCode::Char('u'), KeyModifiers::CONTROL) => {
            app.input_clear();
            if let Some(f) = on_change {
                f(app);
            }
        }
        (KeyCode::Char('b'), KeyModifiers::CONTROL) | (KeyCode::Left, KeyModifiers::NONE) => {
            app.input_move_left();
        }
        (KeyCode::Char('f'), KeyModifiers::CONTROL) | (KeyCode::Right, KeyModifiers::NONE) => {
            app.input_move_right();
        }
        (KeyCode::Char('a'), KeyModifiers::CONTROL) => app.input_move_line_start(),
        (KeyCode::Char('e'), KeyModifiers::CONTROL) => app.input_move_line_end(),
        (KeyCode::Char(c), m)
            if !m.contains(KeyModifiers::CONTROL) && !m.contains(KeyModifiers::ALT) =>
        {
            app.input_insert_char(c);
            if let Some(f) = on_change {
                f(app);
            }
        }
        _ => {}
    }
}

fn handle_search(app: &mut App, key: KeyEvent) {
    handle_line_input(app, key, App::cancel_search, App::submit_search, None);
}

fn handle_file_filter(app: &mut App, key: KeyEvent) {
    handle_line_input(
        app,
        key,
        App::cancel_file_filter,
        App::submit_file_filter,
        Some(App::update_file_filter_live),
    );
}

fn handle_visual(app: &mut App, key: KeyEvent) {
    match (key.code, key.modifiers) {
        (KeyCode::Esc, _)
        | (KeyCode::Char('c'), KeyModifiers::CONTROL)
        | (KeyCode::Char('v'), KeyModifiers::NONE) => {
            app.cancel_visual();
        }
        (KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _) => {
            app.move_cursor_down(1);
        }
        (KeyCode::Char('k'), KeyModifiers::NONE) | (KeyCode::Up, _) => {
            app.move_cursor_up(1);
        }
        (KeyCode::Char('i'), KeyModifiers::NONE) => {
            app.submit_visual_comment();
        }
        _ => {}
    }
}

fn handle_insert(app: &mut App, key: KeyEvent) {
    match (key.code, key.modifiers) {
        (KeyCode::Esc, _) | (KeyCode::Char('c'), KeyModifiers::CONTROL) => app.cancel_insert(),
        (KeyCode::Enter, m)
            if m.contains(KeyModifiers::SHIFT) || m.contains(KeyModifiers::CONTROL) =>
        {
            app.input_insert_newline();
        }
        (KeyCode::Enter, _) => app.submit_comment(),
        (KeyCode::Char('s'), KeyModifiers::CONTROL) => app.submit_comment(),
        (KeyCode::Char('j'), KeyModifiers::CONTROL) => app.input_insert_newline(),
        // delete
        (KeyCode::Backspace, _) | (KeyCode::Char('h'), KeyModifiers::CONTROL) => {
            app.input_delete_backward();
        }
        (KeyCode::Char('w'), KeyModifiers::CONTROL) => app.input_delete_word_backward(),
        (KeyCode::Char('k'), KeyModifiers::CONTROL) => app.input_kill_to_end_of_line(),
        (KeyCode::Char('u'), KeyModifiers::CONTROL) => app.input_clear(),
        // cursor movement
        (KeyCode::Char('b'), KeyModifiers::CONTROL) | (KeyCode::Left, KeyModifiers::NONE) => {
            app.input_move_left();
        }
        (KeyCode::Char('f'), KeyModifiers::CONTROL) | (KeyCode::Right, KeyModifiers::NONE) => {
            app.input_move_right();
        }
        (KeyCode::Char('p'), KeyModifiers::CONTROL) | (KeyCode::Up, _) => {
            app.input_move_up();
        }
        (KeyCode::Char('n'), KeyModifiers::CONTROL) | (KeyCode::Down, _) => {
            app.input_move_down();
        }
        (KeyCode::Char('a'), KeyModifiers::CONTROL) => app.input_move_line_start(),
        (KeyCode::Char('e'), KeyModifiers::CONTROL) => app.input_move_line_end(),
        (KeyCode::Char('b'), KeyModifiers::ALT) | (KeyCode::Left, KeyModifiers::ALT) => {
            app.input_move_word_left();
        }
        (KeyCode::Char('f'), KeyModifiers::ALT) | (KeyCode::Right, KeyModifiers::ALT) => {
            app.input_move_word_right();
        }
        // character input
        (KeyCode::Char(c), m)
            if !m.contains(KeyModifiers::CONTROL) && !m.contains(KeyModifiers::ALT) =>
        {
            app.input_insert_char(c);
        }
        _ => {}
    }
}

fn copy_current_comment(app: &mut App) {
    let Some(file) = app.current() else { return };
    let comments = app.comments_on(file, app.cursor_line);
    let picked = app
        .comment_focus
        .and_then(|cf| comments.get(cf))
        .or(comments.first());
    let Some(c) = picked.cloned() else { return };
    let text = app.format_comment_full(c);
    if let Ok(mut clipboard) = arboard::Clipboard::new() {
        let _ = clipboard.set_text(&text);
        app.set_flash("Copied comment".to_string());
    }
}

fn copy_all_comments(app: &mut App) {
    if app.comments.is_empty() {
        return;
    }
    let text = app.format_all_comments();
    if let Ok(mut clipboard) = arboard::Clipboard::new() {
        let _ = clipboard.set_text(&text);
        app.set_flash(format!("Copied {} comments", app.comments.len()));
    }
}

fn handle_normal(app: &mut App, key: KeyEvent) {
    let half = (app.viewport_height / 2).max(1);
    let full = app.viewport_height.max(1);

    if app.pending_g {
        app.pending_g = false;
        if matches!(key.code, KeyCode::Char('g')) {
            app.cursor_top();
            return;
        }
    }

    if app.pending_d {
        app.pending_d = false;
        if matches!(key.code, KeyCode::Char('d')) {
            if app.focus == Focus::Diff {
                app.delete_focused_comment();
            }
            return;
        }
    }

    match (key.code, key.modifiers) {
        (KeyCode::Char('q'), _) | (KeyCode::Char('c'), KeyModifiers::CONTROL) => {
            app.should_quit = true;
        }
        // design.md §7: `c` (comment) is the canonical binding; `i` is kept
        // as a legacy alias so existing diftty muscle memory survives.
        (KeyCode::Char('i'), KeyModifiers::NONE) | (KeyCode::Char('c'), KeyModifiers::NONE)
            if app.focus == Focus::Diff =>
        {
            app.enter_insert();
        }
        // Search
        (KeyCode::Char('/'), KeyModifiers::NONE) => {
            if app.focus == Focus::Diff {
                app.enter_search();
            } else if app.focus == Focus::Files {
                app.enter_file_filter();
            }
        }
        // Search navigation (when search is active)
        (KeyCode::Char('n'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            if app.search_query.is_some() {
                app.next_search_match();
            } else {
                app.next_hunk();
            }
        }
        (KeyCode::Char('p'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            if app.search_query.is_some() {
                app.prev_search_match();
            } else {
                app.prev_hunk();
            }
        }
        (KeyCode::Char('N'), _) if app.focus == Focus::Diff => {
            app.next_comment();
        }
        (KeyCode::Char('P'), _) if app.focus == Focus::Diff => {
            app.prev_comment();
        }
        // Clear search / file filter with Esc in normal mode
        (KeyCode::Esc, _) => {
            if app.search_query.is_some() {
                app.clear_search();
            }
            if app.file_filter.is_some() {
                app.file_filter = None;
            }
        }
        // Visual mode
        (KeyCode::Char('V'), _) if app.focus == Focus::Diff => {
            app.enter_visual();
        }
        // File list resize
        (KeyCode::Char('<'), _) => {
            app.file_list_width = app.file_list_width.saturating_sub(2).max(20);
        }
        (KeyCode::Char('>'), _) => {
            app.file_list_width = (app.file_list_width + 2).min(60);
        }
        // Theme toggle
        (KeyCode::Char('T'), _) => {
            app.theme_name = app.theme_name.next();
        }
        (KeyCode::Char('1'), KeyModifiers::NONE) => {
            app.theme_name = ThemeName::Classic;
        }
        (KeyCode::Char('2'), KeyModifiers::NONE) => {
            app.theme_name = ThemeName::Washi;
        }
        (KeyCode::Char('3'), KeyModifiers::NONE) => {
            app.theme_name = ThemeName::Sumi;
        }
        // Copy prompt (comment at cursor)
        (KeyCode::Char('y'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            copy_current_comment(app);
        }
        // Copy all comments
        (KeyCode::Char('Y'), _) => {
            copy_all_comments(app);
        }
        // Open in editor
        (KeyCode::Char('e'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            app.pending_open_editor = true;
        }
        // Reload diff
        (KeyCode::Char('R'), _) => {
            app.pending_reload = true;
        }
        (KeyCode::Tab, _) => app.select_next_file(),
        (KeyCode::BackTab, _) => app.select_prev_file(),
        (KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _) => match app.focus {
            Focus::Diff => app.move_cursor_down(1),
            Focus::Files => app.file_tree_cursor_down(),
        },
        (KeyCode::Char('k'), KeyModifiers::NONE) | (KeyCode::Up, _) => match app.focus {
            Focus::Diff => app.move_cursor_up(1),
            Focus::Files => app.file_tree_cursor_up(),
        },
        (KeyCode::Char('d' | 'D'), KeyModifiers::CONTROL) => app.move_cursor_down(half),
        (KeyCode::Char('u' | 'U'), KeyModifiers::CONTROL) => app.move_cursor_up(half),
        (KeyCode::Char('f' | 'F'), KeyModifiers::CONTROL) => app.move_cursor_down(full),
        (KeyCode::Char('b' | 'B'), KeyModifiers::CONTROL) => app.move_cursor_up(full),
        (KeyCode::Char('g'), KeyModifiers::NONE) => {
            app.pending_g = true;
        }
        (KeyCode::Char('G'), _) => app.cursor_bottom(),
        (KeyCode::Char('o'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            if let Some(line) = app.current().and_then(|f| f.lines.get(app.cursor_line)) {
                if line.kind.is_fold() {
                    app.pending_expand = Some(20);
                }
            }
        }
        (KeyCode::Char('O'), _) if app.focus == Focus::Diff => {
            if let Some(line) = app.current().and_then(|f| f.lines.get(app.cursor_line)) {
                if line.kind.is_fold() {
                    app.pending_expand = Some(usize::MAX);
                }
            }
        }
        (KeyCode::Enter, _) => match app.focus {
            Focus::Diff => {
                if let Some(line) = app.current().and_then(|f| f.lines.get(app.cursor_line)) {
                    if line.kind.is_fold() {
                        app.pending_expand = Some(20);
                    }
                }
            }
            Focus::Files => {
                app.toggle_file_tree_dir_at_cursor();
            }
        },
        (KeyCode::Char('s'), KeyModifiers::NONE) => {
            app.split_view = !app.split_view;
        }
        (KeyCode::Char('r'), KeyModifiers::NONE) => {
            app.preview_mode = !app.preview_mode;
        }
        (KeyCode::Char('C'), _) => app.toggle_comments_list(),
        (KeyCode::Char('F'), _) => {
            app.show_file_list = !app.show_file_list;
            if !app.show_file_list && app.focus == Focus::Files {
                app.focus = Focus::Diff;
            }
        }
        (KeyCode::Char('h'), _) => {
            app.focus = Focus::Files;
        }
        (KeyCode::Char('l'), _) => {
            app.focus = Focus::Diff;
        }
        (KeyCode::Char(']'), KeyModifiers::NONE) => app.select_next_file(),
        (KeyCode::Char('['), KeyModifiers::NONE) => app.select_prev_file(),
        (KeyCode::Char('}'), _) => app.select_last_file(),
        (KeyCode::Char('{'), _) => app.select_first_file(),
        (KeyCode::Char('.'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            app.center_cursor();
        }
        (KeyCode::Char('v'), KeyModifiers::NONE) => app.toggle_viewed(),
        (KeyCode::Char('d'), KeyModifiers::NONE) if app.focus == Focus::Diff => {
            app.pending_d = true;
        }
        (KeyCode::Char('?'), _) => {
            app.show_help = true;
        }
        (KeyCode::Char(':'), _) => {
            app.open_revision_selector();
        }
        (KeyCode::Char(' '), KeyModifiers::NONE) if app.focus == Focus::Files => {
            app.toggle_file_tree_dir_at_cursor();
        }
        _ => {}
    }
}
