use super::{MainScreenMode, Panel};
use crate::action::{AppAction, DeleteKind, ReorderKind};
use crate::models::AppData;
use crate::ui::main_screen::MainScreenState;
use crate::ui::widget::TextInput;
use crate::ui::widget::text_input::handle_text_input;
use crossterm::event::KeyEvent;

impl MainScreenState {
    fn reorder_active_panel(&self, data: &AppData, dir: isize) -> AppAction {
        match self.active_panel {
            Panel::Groups if let Some(gi) = self.selected_group_idx(data) => {
                AppAction::Reorder(ReorderKind::Group(gi), dir)
            }
            Panel::Sets if let Some((gi, si)) = self.selected_set_idx(data) => {
                AppAction::Reorder(ReorderKind::Set(gi, si), dir)
            }
            _ => AppAction::None,
        }
    }

    fn navigate_list(&mut self, data: &AppData, forward: bool) {
        match self.active_panel {
            Panel::Groups => {
                if forward {
                    self.group_list.select_next(data.groups.len());
                } else {
                    self.group_list.select_previous();
                }
            }
            Panel::Sets => {
                let n = self.visible_sets(data).len();
                if n == 0 {
                    self.active_panel = Panel::Groups;
                } else if forward {
                    self.set_list.select_next(n);
                } else {
                    self.set_list.select_previous();
                }
            }
        }
    }

    /// Handle a key event, returning an action.
    pub fn handle_key(&mut self, key: KeyEvent, data: &AppData) -> AppAction {
        use crossterm::event::KeyCode;

        // Rename mode (takes priority over search)
        if let MainScreenMode::Renaming(ref mut input) = self.mode {
            return match key.code {
                KeyCode::Enter => {
                    let name = input.content.clone();
                    let gi = self.group_list.selected;
                    self.mode = MainScreenMode::Normal;
                    AppAction::RenameGroup(gi, name)
                }
                KeyCode::Esc => {
                    self.mode = MainScreenMode::Normal;
                    AppAction::None
                }
                _ => {
                    handle_text_input(input, key);
                    AppAction::None
                }
            };
        }

        // Search mode
        if let MainScreenMode::Searching(ref mut input) = self.mode {
            return match key.code {
                KeyCode::Esc => {
                    self.mode = MainScreenMode::Normal;
                    self.set_list.reset();
                    self.active_panel = Panel::Groups;
                    AppAction::None
                }
                KeyCode::Enter => {
                    let results = data.filter_sets(&input.content);
                    if let Some(result) = results.get(self.set_list.selected) {
                        self.group_list.selected = result.group_index;
                        self.set_list.selected = result.set_index;
                        self.active_panel = Panel::Sets;
                    }
                    self.mode = MainScreenMode::Normal;
                    AppAction::None
                }
                KeyCode::Up | KeyCode::Char('k') | KeyCode::Char('K') => {
                    self.set_list.select_previous();
                    AppAction::None
                }
                KeyCode::Down | KeyCode::Char('j') | KeyCode::Char('J') => {
                    let n = data.filter_sets(&input.content).len();
                    self.set_list.select_next(n);
                    AppAction::None
                }
                _ => {
                    handle_text_input(input, key);
                    self.active_panel = Panel::Sets;
                    self.set_list.reset();
                    AppAction::None
                }
            };
        }

        match key.code {
            KeyCode::Up
                if key
                    .modifiers
                    .contains(crossterm::event::KeyModifiers::CONTROL) =>
            {
                self.reorder_active_panel(data, -1)
            }
            KeyCode::Down
                if key
                    .modifiers
                    .contains(crossterm::event::KeyModifiers::CONTROL) =>
            {
                self.reorder_active_panel(data, 1)
            }
            KeyCode::Up | KeyCode::Char('k') | KeyCode::Char('K') => {
                self.navigate_list(data, false);
                AppAction::None
            }
            KeyCode::Down | KeyCode::Char('j') | KeyCode::Char('J') => {
                self.navigate_list(data, true);
                AppAction::None
            }
            KeyCode::Left => {
                match self.active_panel {
                    Panel::Sets => self.active_panel = Panel::Groups,
                    Panel::Groups => { /* already on the leftmost panel */ }
                }
                AppAction::None
            }
            KeyCode::Right => {
                match self.active_panel {
                    Panel::Groups => {
                        let has_sets = self
                            .selected_group_idx(data)
                            .map(|gi| !data.groups[gi].sets.is_empty())
                            .unwrap_or(false);
                        if has_sets {
                            self.active_panel = Panel::Sets;
                        }
                    }
                    Panel::Sets => { /* already on the rightmost panel */ }
                }
                AppAction::None
            }
            KeyCode::Enter => {
                if self.active_panel == Panel::Sets
                    && let Some((gi, si)) = self.selected_set_idx(data)
                {
                    return AppAction::ExecuteSet(gi, si);
                }
                AppAction::None
            }
            KeyCode::Char('e') | KeyCode::Char('E') => {
                if self.active_panel == Panel::Sets
                    && let Some((gi, si)) = self.selected_set_idx(data)
                {
                    return AppAction::EditSet(gi, si);
                }
                AppAction::None
            }
            KeyCode::Char('n') | KeyCode::Char('N') => {
                if let Some(gi) = self.selected_group_idx(data) {
                    AppAction::NewSet(gi)
                } else {
                    AppAction::None
                }
            }
            KeyCode::Char('d') => {
                if self.active_panel == Panel::Sets
                    && let Some((gi, si)) = self.selected_set_idx(data)
                {
                    let set_name = data.groups[gi].sets[si].name.clone();
                    return AppAction::RequestDelete(DeleteKind::Set {
                        group_index: gi,
                        set_index: si,
                        set_name,
                    });
                }
                AppAction::None
            }
            KeyCode::Char('D') => {
                if self.active_panel == Panel::Groups
                    && let Some(gi) = self.selected_group_idx(data)
                {
                    let group = &data.groups[gi];
                    return AppAction::RequestDelete(DeleteKind::Group {
                        group_index: gi,
                        group_name: group.name.clone(),
                        set_count: group.sets.len(),
                    });
                }
                AppAction::None
            }
            KeyCode::Char('g') => AppAction::NewGroup,
            KeyCode::Char('R') => {
                if self.active_panel == Panel::Groups
                    && let Some(gi) = self.selected_group_idx(data)
                {
                    let current = data.groups[gi].name.clone();
                    self.mode = MainScreenMode::Renaming(TextInput::new(current));
                }
                AppAction::None
            }
            KeyCode::Char('/') => {
                self.mode = MainScreenMode::Searching(TextInput::new(String::new()));
                self.set_list.reset();
                self.active_panel = Panel::Sets;
                AppAction::None
            }
            KeyCode::Char('h') | KeyCode::Char('H')
                if key
                    .modifiers
                    .contains(crossterm::event::KeyModifiers::CONTROL) =>
            {
                AppAction::Help
            }
            KeyCode::Char('h') | KeyCode::Char('H') => AppAction::None,
            KeyCode::Esc | KeyCode::Char('q') => {
                if key.code == KeyCode::Esc {
                    AppAction::None
                } else {
                    AppAction::Quit
                }
            }
            _ => AppAction::None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::action::{AppAction, DeleteKind, ReorderKind};
    use crate::models::{AppData, CommandSet, Group};
    use crate::test_utils::make_key;
    use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

    fn make_data() -> AppData {
        let mut g = Group::new("Test Group".to_string());
        let set = CommandSet::new("Test Set".to_string(), g.id);
        g.sets.push(set);
        AppData { groups: vec![g] }
    }

    #[test]
    fn test_nav_down_returns_none() {
        let mut state = MainScreenState::new();
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Down), &data);
        assert!(matches!(action, AppAction::None));
        assert_eq!(state.group_list.selected, 0); // stays at first
    }

    #[test]
    fn test_enter_on_set_returns_execute_set() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Sets;
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Enter), &data);
        assert!(matches!(action, AppAction::ExecuteSet(0, 0)));
    }

    #[test]
    fn test_e_returns_edit_set() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Sets;
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('e')), &data);
        assert!(matches!(action, AppAction::EditSet(0, 0)));
    }

    #[test]
    fn test_n_returns_new_set() {
        let mut state = MainScreenState::new();
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('n')), &data);
        assert!(matches!(action, AppAction::NewSet(0)));
    }

    #[test]
    fn test_d_returns_request_delete_set() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Sets;
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('d')), &data);
        assert!(matches!(
            action,
            AppAction::RequestDelete(DeleteKind::Set {
                group_index: 0,
                set_index: 0,
                ..
            })
        ));
    }

    #[test]
    fn test_d_on_groups_panel_does_nothing() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Groups;
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('d')), &data);
        assert!(matches!(action, AppAction::None));
    }

    #[test]
    fn test_big_d_returns_request_delete_group() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Groups;
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('D')), &data);
        assert!(matches!(
            action,
            AppAction::RequestDelete(DeleteKind::Group { group_index: 0, .. })
        ));
    }

    #[test]
    fn test_g_returns_new_group() {
        let mut state = MainScreenState::new();
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('g')), &data);
        assert!(matches!(action, AppAction::NewGroup));
    }

    #[test]
    fn test_q_returns_quit() {
        let mut state = MainScreenState::new();
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('q')), &data);
        assert!(matches!(action, AppAction::Quit));
    }

    #[test]
    fn test_question_mark_is_delegated_to_app() {
        let mut state = MainScreenState::new();
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('?')), &data);
        // '?' is now a global shortcut in App::handle_key, not in MainScreenState
        assert!(matches!(action, AppAction::None));
    }

    #[test]
    fn test_slash_enters_search_mode() {
        let mut state = MainScreenState::new();
        let data = make_data();
        let action = state.handle_key(make_key(KeyCode::Char('/')), &data);
        assert!(matches!(action, AppAction::None));
        assert!(matches!(state.mode, MainScreenMode::Searching(_)));
    }

    #[test]
    fn test_ctrl_up_returns_reorder_group() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Groups;
        state.group_list.selected = 1;
        let mut data = make_data();
        data.groups.push(Group::new("G2".to_string()));
        let ctrl_up = KeyEvent::new(KeyCode::Up, KeyModifiers::CONTROL);
        let action = state.handle_key(ctrl_up, &data);
        assert!(matches!(
            action,
            AppAction::Reorder(ReorderKind::Group(1), -1)
        ));
    }

    #[test]
    fn test_ctrl_down_returns_reorder_set() {
        let mut state = MainScreenState::new();
        state.active_panel = Panel::Sets;
        state.set_list.selected = 0;
        let mut data = make_data();
        let set2 = CommandSet::new("S2".to_string(), data.groups[0].id);
        data.groups[0].sets.push(set2);
        let ctrl_down = KeyEvent::new(KeyCode::Down, KeyModifiers::CONTROL);
        let action = state.handle_key(ctrl_down, &data);
        assert!(matches!(
            action,
            AppAction::Reorder(ReorderKind::Set(0, 0), 1)
        ));
    }

    #[test]
    fn test_ctrl_up_ignored_in_groups_when_no_group_selected() {
        let mut state = MainScreenState::new();
        let empty_data = AppData::empty();
        let ctrl_up = KeyEvent::new(KeyCode::Up, KeyModifiers::CONTROL);
        let action = state.handle_key(ctrl_up, &empty_data);
        assert!(matches!(action, AppAction::None));
    }
}
