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

use crate::discord::AppCommand;
use crate::tui::keybindings::{ComposerAction, ComposerCompletionAction, SelectionAction};
use crate::tui::state::DashboardState;

pub(super) fn handle_composer_key(state: &mut DashboardState, key: KeyEvent) -> Option<AppCommand> {
    if state.composer_has_active_picker()
        && let Some(command) = handle_active_picker_key(state, key)
    {
        return command;
    }

    let action = state.key_bindings().composer_action(key);

    match action {
        ComposerAction::OpenInEditor => {
            state.request_open_composer_in_editor();
            None
        }
        ComposerAction::PasteClipboard => {
            state.request_paste_clipboard();
            None
        }
        ComposerAction::InsertNewline => {
            state.push_composer_char('\n');
            state.note_composer_typing()
        }
        ComposerAction::Submit => state.submit_composer(),
        ComposerAction::Close => {
            state.close_composer();
            None
        }
        ComposerAction::ClearInput => {
            state.clear_composer_input();
            None
        }
        ComposerAction::RemoveLastAttachment => {
            state.pop_pending_composer_attachment();
            None
        }
        ComposerAction::EditText(action) => {
            state.edit_composer_text_input(action);
            None
        }
        ComposerAction::ToggleReplyPing => {
            state.toggle_ping_on_reply();
            None
        }
        ComposerAction::InsertChar(value) => {
            if value != ':' || !state.open_composer_reaction_picker_from_plus_colon() {
                state.push_composer_char(value);
            }
            state.note_composer_typing()
        }
        ComposerAction::Ignore => None,
    }
}

/// Returns `Some(None)` to mean "the picker absorbed this key, don't fall
/// through to the regular composer handler", and `None` to mean "let the
/// composer handle this key normally."
fn handle_active_picker_key(
    state: &mut DashboardState,
    key: KeyEvent,
) -> Option<Option<AppCommand>> {
    if key.code == KeyCode::Enter
        && key.modifiers == KeyModifiers::NONE
        && state.active_composer_picker_is_command()
        && state.composer_command_can_submit()
        && !state.composer_command_selected_candidate_is_top_level()
    {
        return Some(state.submit_composer());
    }

    handle_composer_completion_picker_key(
        state,
        key,
        DashboardState::move_active_composer_picker_selection,
        DashboardState::confirm_active_composer_picker,
        DashboardState::cancel_active_composer_picker,
    )
}

fn handle_composer_completion_picker_key(
    state: &mut DashboardState,
    key: KeyEvent,
    mut move_selection: impl FnMut(&mut DashboardState, isize),
    mut confirm: impl FnMut(&mut DashboardState) -> bool,
    mut cancel: impl FnMut(&mut DashboardState),
) -> Option<Option<AppCommand>> {
    match state.key_bindings().composer_completion_action(key) {
        ComposerCompletionAction::Select(SelectionAction::Next) => {
            move_selection(state, 1);
            Some(None)
        }
        ComposerCompletionAction::Select(SelectionAction::Previous) => {
            move_selection(state, -1);
            Some(None)
        }
        ComposerCompletionAction::Confirm => {
            if confirm(state) {
                Some(None)
            } else {
                cancel(state);
                Some(None)
            }
        }
        ComposerCompletionAction::Cancel => {
            cancel(state);
            Some(None)
        }
        ComposerCompletionAction::FallThrough => None,
    }
}
