//! Cleanup-worktrees command: list or remove orphaned worktrees from chat.
//!
//! `{bot_name} cleanup_worktrees` lists orphaned worktrees (dry run).
//! `{bot_name} cleanup_worktrees --confirm` removes them.
//!
//! Both paths share the [`crate::worktree::run_cleanup`] handler.

use crate::chat::util::strip_bot_mention;
use crate::config::ProjectConfig;
use std::path::Path;

/// A parsed cleanup_worktrees command from a Matrix message body.
#[derive(Debug, PartialEq)]
pub enum CleanupWorktreesCommand {
    /// List orphaned worktrees without removing (dry run).
    DryRun,
    /// Remove all orphaned worktrees.
    Confirm,
}

/// Parse a cleanup_worktrees command from a raw Matrix message body.
///
/// Strips the bot mention prefix and checks whether the first word is
/// `cleanup_worktrees`. Returns `None` when the message is not this command.
pub fn extract_cleanup_worktrees_command(
    message: &str,
    bot_name: &str,
    bot_user_id: &str,
) -> Option<CleanupWorktreesCommand> {
    let stripped = strip_bot_mention(message, bot_name, bot_user_id);
    let trimmed = stripped
        .trim()
        .trim_start_matches(|c: char| !c.is_alphanumeric());

    let (cmd, args) = match trimmed.split_once(char::is_whitespace) {
        Some((c, a)) => (c, a.trim()),
        None => (trimmed, ""),
    };

    if !cmd.eq_ignore_ascii_case("cleanup_worktrees") {
        return None;
    }

    if args == "--confirm" {
        Some(CleanupWorktreesCommand::Confirm)
    } else {
        Some(CleanupWorktreesCommand::DryRun)
    }
}

/// Handle a cleanup_worktrees command asynchronously.
///
/// Delegates to [`crate::worktree::run_cleanup`] and formats the result as
/// a Markdown string suitable for posting to the chat room.
pub async fn handle_cleanup_worktrees(project_root: &Path, confirm: bool) -> String {
    let config = match ProjectConfig::load(project_root) {
        Ok(c) => c,
        Err(e) => return format!("Failed to load project config: {e}"),
    };

    let report = crate::worktree::run_cleanup(project_root, &config, confirm).await;
    crate::worktree::format_report(&report, confirm)
}

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

#[cfg(test)]
mod tests {
    use super::*;

    // -- extract_cleanup_worktrees_command -----------------------------------

    #[test]
    fn extract_dry_run_with_full_user_id() {
        let cmd = extract_cleanup_worktrees_command(
            "@timmy:home.local cleanup_worktrees",
            "Timmy",
            "@timmy:home.local",
        );
        assert_eq!(cmd, Some(CleanupWorktreesCommand::DryRun));
    }

    #[test]
    fn extract_confirm_with_display_name() {
        let cmd = extract_cleanup_worktrees_command(
            "Timmy cleanup_worktrees --confirm",
            "Timmy",
            "@timmy:home.local",
        );
        assert_eq!(cmd, Some(CleanupWorktreesCommand::Confirm));
    }

    #[test]
    fn extract_case_insensitive() {
        let cmd = extract_cleanup_worktrees_command(
            "Timmy CLEANUP_WORKTREES",
            "Timmy",
            "@timmy:home.local",
        );
        assert_eq!(cmd, Some(CleanupWorktreesCommand::DryRun));
    }

    #[test]
    fn extract_with_localpart() {
        let cmd = extract_cleanup_worktrees_command(
            "@timmy cleanup_worktrees --confirm",
            "Timmy",
            "@timmy:home.local",
        );
        assert_eq!(cmd, Some(CleanupWorktreesCommand::Confirm));
    }

    #[test]
    fn extract_other_command_returns_none() {
        let cmd =
            extract_cleanup_worktrees_command("Timmy rmtree 42", "Timmy", "@timmy:home.local");
        assert_eq!(cmd, None);
    }

    #[test]
    fn extract_unrelated_text_returns_none() {
        let cmd = extract_cleanup_worktrees_command("Timmy help", "Timmy", "@timmy:home.local");
        assert_eq!(cmd, None);
    }

    // -- handle_cleanup_worktrees integration --------------------------------

    #[tokio::test]
    async fn handle_dry_run_no_worktrees() {
        let tmp = tempfile::tempdir().unwrap();
        std::fs::create_dir_all(tmp.path().join(".huskies").join("worktrees")).unwrap();
        let response = handle_cleanup_worktrees(tmp.path(), false).await;
        assert!(
            response.contains("No orphaned"),
            "unexpected response: {response}"
        );
    }

    #[tokio::test]
    async fn handle_confirm_no_worktrees() {
        let tmp = tempfile::tempdir().unwrap();
        std::fs::create_dir_all(tmp.path().join(".huskies").join("worktrees")).unwrap();
        let response = handle_cleanup_worktrees(tmp.path(), true).await;
        assert!(
            response.contains("No orphaned"),
            "unexpected response: {response}"
        );
    }
}
