#![allow(dead_code)] // Phase 1: consumed by LoreApp in bd-6pmy

//! Command registry — single source of truth for all TUI actions.
//!
//! Every keybinding, palette entry, help text, CLI equivalent, and
//! status hint is generated from [`CommandRegistry`]. No hardcoded
//! duplicate maps exist in view/state modules.
//!
//! Supports single-key and two-key sequences (g-prefix vim bindings).

mod defs;
mod registry;

// Re-export public API — preserves `crate::commands::{CommandRegistry, build_registry, ...}`.
pub use defs::{CommandDef, CommandId, KeyCombo, ScreenFilter};
pub use registry::{CommandRegistry, build_registry};

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::Utc;
    use ftui::{KeyCode, Modifiers};

    use crate::message::{InputMode, Screen};

    #[test]
    fn test_registry_builds_successfully() {
        let reg = build_registry();
        assert!(!reg.is_empty());
        assert!(reg.len() >= 15);
    }

    #[test]
    fn test_registry_lookup_quit() {
        let reg = build_registry();
        let cmd = reg.lookup_key(
            &KeyCode::Char('q'),
            &Modifiers::NONE,
            &Screen::Dashboard,
            &InputMode::Normal,
        );
        assert!(cmd.is_some());
        assert_eq!(cmd.unwrap().id, "quit");
    }

    #[test]
    fn test_registry_lookup_quit_blocked_in_text_mode() {
        let reg = build_registry();
        let cmd = reg.lookup_key(
            &KeyCode::Char('q'),
            &Modifiers::NONE,
            &Screen::Dashboard,
            &InputMode::Text,
        );
        assert!(cmd.is_none());
    }

    #[test]
    fn test_registry_esc_works_in_text_mode() {
        let reg = build_registry();
        let cmd = reg.lookup_key(
            &KeyCode::Escape,
            &Modifiers::NONE,
            &Screen::IssueList,
            &InputMode::Text,
        );
        assert!(cmd.is_some());
        assert_eq!(cmd.unwrap().id, "go_back");
    }

    #[test]
    fn test_registry_ctrl_p_works_in_text_mode() {
        let reg = build_registry();
        let cmd = reg.lookup_key(
            &KeyCode::Char('p'),
            &Modifiers::CTRL,
            &Screen::Search,
            &InputMode::Text,
        );
        assert!(cmd.is_some());
        assert_eq!(cmd.unwrap().id, "command_palette");
    }

    #[test]
    fn test_g_is_sequence_starter() {
        let reg = build_registry();
        assert!(reg.is_sequence_starter(&KeyCode::Char('g'), &Modifiers::NONE));
        assert!(!reg.is_sequence_starter(&KeyCode::Char('x'), &Modifiers::NONE));
    }

    #[test]
    fn test_complete_sequence_gi() {
        let reg = build_registry();
        let cmd = reg.complete_sequence(
            &KeyCode::Char('g'),
            &Modifiers::NONE,
            &KeyCode::Char('i'),
            &Modifiers::NONE,
            &Screen::Dashboard,
        );
        assert!(cmd.is_some());
        assert_eq!(cmd.unwrap().id, "go_issues");
    }

    #[test]
    fn test_complete_sequence_invalid_second_key() {
        let reg = build_registry();
        let cmd = reg.complete_sequence(
            &KeyCode::Char('g'),
            &Modifiers::NONE,
            &KeyCode::Char('z'),
            &Modifiers::NONE,
            &Screen::Dashboard,
        );
        assert!(cmd.is_none());
    }

    #[test]
    fn test_screen_specific_command() {
        let reg = build_registry();
        // 'j' (move_down) should work on IssueList
        let cmd = reg.lookup_key(
            &KeyCode::Char('j'),
            &Modifiers::NONE,
            &Screen::IssueList,
            &InputMode::Normal,
        );
        assert!(cmd.is_some());
        assert_eq!(cmd.unwrap().id, "move_down");

        // 'j' should NOT match on Dashboard (move_down is list-only).
        let cmd = reg.lookup_key(
            &KeyCode::Char('j'),
            &Modifiers::NONE,
            &Screen::Dashboard,
            &InputMode::Normal,
        );
        assert!(cmd.is_none());
    }

    #[test]
    fn test_palette_entries_sorted_by_label() {
        let reg = build_registry();
        let entries = reg.palette_entries(&Screen::Dashboard);
        let labels: Vec<&str> = entries.iter().map(|c| c.label).collect();
        let mut sorted = labels.clone();
        sorted.sort();
        assert_eq!(labels, sorted);
    }

    #[test]
    fn test_help_entries_only_include_keybindings() {
        let reg = build_registry();
        let entries = reg.help_entries(&Screen::Dashboard);
        for entry in &entries {
            assert!(
                entry.keybinding.is_some(),
                "help entry without keybinding: {}",
                entry.id
            );
        }
    }

    #[test]
    fn test_status_hints_non_empty() {
        let reg = build_registry();
        let hints = reg.status_hints(&Screen::Dashboard);
        assert!(!hints.is_empty());
        // All returned hints should be non-empty strings.
        for hint in &hints {
            assert!(!hint.is_empty());
        }
    }

    #[test]
    fn test_cli_equivalents_populated() {
        let reg = build_registry();
        let with_cli: Vec<&CommandDef> = reg
            .commands
            .iter()
            .filter(|c| c.cli_equivalent.is_some())
            .collect();
        assert!(
            with_cli.len() >= 5,
            "expected at least 5 commands with cli_equivalent, got {}",
            with_cli.len()
        );
    }

    #[test]
    fn test_go_prefix_timeout_detection() {
        let reg = build_registry();
        // Simulate GoPrefix mode entering: 'g' detected as sequence starter.
        assert!(reg.is_sequence_starter(&KeyCode::Char('g'), &Modifiers::NONE));

        // Simulate InputMode::GoPrefix with timeout check.
        let started = Utc::now();
        let mode = InputMode::GoPrefix {
            started_at: started,
        };
        // In GoPrefix mode, normal lookup should still work for non-sequence keys.
        let cmd = reg.lookup_key(
            &KeyCode::Char('q'),
            &Modifiers::NONE,
            &Screen::Dashboard,
            &mode,
        );
        assert!(cmd.is_some());
        assert_eq!(cmd.unwrap().id, "quit");
    }

    #[test]
    fn test_all_commands_have_nonempty_help() {
        let reg = build_registry();
        for cmd in &reg.commands {
            assert!(
                !cmd.help_text.is_empty(),
                "command {} has empty help_text",
                cmd.id
            );
        }
    }
}
