pub mod cell;
pub mod commit;
pub mod core;
pub mod effect_cmds;
pub mod grid;
pub mod mode;
pub mod navigation;
pub mod panel;
pub mod registry;
pub mod search;
pub mod text_buffer;
pub mod tile;

// Re-export items used by external code
pub use self::core::{Cmd, CmdContext, CmdRegistry};
pub use registry::default_registry;

#[cfg(test)]
pub(super) mod test_helpers {
    use std::collections::HashMap;

    use crossterm::event::KeyCode;

    use crate::model::Model;
    use crate::ui::app::AppMode;
    use crate::ui::effect::Effect;
    use crate::view::GridLayout;

    use super::core::CmdContext;
    use super::registry::default_registry;

    pub type CmdRegistry = super::core::CmdRegistry;

    pub static EMPTY_BUFFERS: std::sync::LazyLock<HashMap<String, String>> =
        std::sync::LazyLock::new(HashMap::new);
    pub static EMPTY_EXPANDED: std::sync::LazyLock<std::collections::HashSet<String>> =
        std::sync::LazyLock::new(std::collections::HashSet::new);

    pub fn make_layout(model: &Model) -> GridLayout {
        GridLayout::new(model, model.active_view())
    }

    pub fn make_ctx<'a>(
        model: &'a Model,
        layout: &'a GridLayout,
        registry: &'a CmdRegistry,
    ) -> CmdContext<'a> {
        let view = model.active_view();
        let (sr, sc) = view.selected;
        CmdContext {
            model,
            layout,
            registry,
            mode: &AppMode::Normal,
            selected: view.selected,
            row_offset: view.row_offset,
            col_offset: view.col_offset,
            search_query: "",
            yanked: &None,
            dirty: false,
            search_mode: false,
            formula_panel_open: false,
            category_panel_open: false,
            view_panel_open: false,
            formula_cursor: 0,
            cat_panel_cursor: 0,
            view_panel_cursor: 0,
            tile_cat_idx: 0,
            buffers: &EMPTY_BUFFERS,
            view_back_stack: &[],
            view_forward_stack: &[],
            has_drill_state: false,
            display_value: {
                let key = layout.cell_key(sr, sc);
                key.as_ref()
                    .and_then(|k| model.get_cell(k).cloned())
                    .map(|v| v.to_string())
                    .unwrap_or_default()
            },
            visible_rows: 20,
            visible_cols: 8,
            expanded_cats: &EMPTY_EXPANDED,
            key_code: KeyCode::Null,
        }
    }

    pub fn two_cat_model() -> Model {
        let mut m = Model::new("Test");
        m.add_category("Type").unwrap();
        m.add_category("Month").unwrap();
        m.category_mut("Type").unwrap().add_item("Food");
        m.category_mut("Type").unwrap().add_item("Clothing");
        m.category_mut("Month").unwrap().add_item("Jan");
        m.category_mut("Month").unwrap().add_item("Feb");
        m
    }

    pub fn three_cat_model_with_page() -> Model {
        let mut m = Model::new("Test");
        m.add_category("Type").unwrap();
        m.add_category("Month").unwrap();
        m.add_category("Region").unwrap();
        m.category_mut("Type").unwrap().add_item("Food");
        m.category_mut("Type").unwrap().add_item("Clothing");
        m.category_mut("Month").unwrap().add_item("Jan");
        m.category_mut("Month").unwrap().add_item("Feb");
        m.category_mut("Region").unwrap().add_item("North");
        m.category_mut("Region").unwrap().add_item("South");
        m.category_mut("Region").unwrap().add_item("East");
        let view = m.active_view_mut();
        view.set_axis("Region", crate::view::Axis::Page);
        m
    }

    pub fn effects_debug(effects: &[Box<dyn Effect>]) -> String {
        format!("{:?}", effects)
    }

    pub fn make_registry() -> CmdRegistry {
        default_registry()
    }
}
