//! Handler for the `help` command.

use super::{CommandContext, commands};

pub(super) fn handle_help(ctx: &CommandContext) -> Option<String> {
    let mut output = format!("**{} Commands**\n\n", ctx.services.bot_name);
    let mut sorted: Vec<_> = commands().iter().collect();
    sorted.sort_by_key(|c| c.name);
    for cmd in sorted {
        output.push_str(&format!("- **{}** — {}\n", cmd.name, cmd.description));
    }
    Some(output)
}

#[cfg(test)]
mod tests {
    use super::super::tests::{commands, try_cmd_addressed};

    #[test]
    fn help_command_matches() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        assert!(result.is_some(), "help command should match");
    }

    #[test]
    fn help_command_case_insensitive() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy HELP");
        assert!(result.is_some(), "HELP should match case-insensitively");
    }

    #[test]
    fn help_output_contains_all_commands() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        let output = result.unwrap();
        for cmd in commands() {
            assert!(
                output.contains(cmd.name),
                "help output must include command '{}'",
                cmd.name
            );
            assert!(
                output.contains(cmd.description),
                "help output must include description for '{}'",
                cmd.name
            );
        }
    }

    #[test]
    fn help_output_uses_bot_name() {
        let result = try_cmd_addressed("HAL", "@hal:example.com", "@hal help");
        let output = result.unwrap();
        assert!(
            output.contains("HAL Commands"),
            "help output should use bot name: {output}"
        );
    }

    #[test]
    fn help_output_formatted_as_markdown() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        let output = result.unwrap();
        assert!(
            output.contains("**help**"),
            "command name should be bold: {output}"
        );
        assert!(
            output.contains("- **"),
            "commands should be in a list: {output}"
        );
    }

    #[test]
    fn help_output_includes_status() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        let output = result.unwrap();
        assert!(
            output.contains("status"),
            "help should list status command: {output}"
        );
    }

    #[test]
    fn help_output_is_alphabetical() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        let output = result.unwrap();
        // Search for **name** (bold markdown) to avoid substring matches in descriptions.
        let mut positions: Vec<(usize, &str)> = commands()
            .iter()
            .map(|c| {
                let marker = format!("**{}**", c.name);
                let pos = output
                    .find(&marker)
                    .expect("command must appear in help as **name**");
                (pos, c.name)
            })
            .collect();
        positions.sort_by_key(|(pos, _)| *pos);
        let names_in_order: Vec<&str> = positions.iter().map(|(_, n)| *n).collect();
        let mut sorted = names_in_order.clone();
        sorted.sort();
        assert_eq!(
            names_in_order, sorted,
            "commands must appear in alphabetical order"
        );
    }

    #[test]
    fn help_output_includes_ambient() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        let output = result.unwrap();
        assert!(
            output.contains("ambient"),
            "help should list ambient command: {output}"
        );
    }

    #[test]
    fn help_output_includes_htop() {
        let result = try_cmd_addressed("Timmy", "@timmy:homeserver.local", "@timmy help");
        let output = result.unwrap();
        assert!(
            output.contains("htop"),
            "help should list htop command: {output}"
        );
    }
}
