//! Handler for the `ambient` command.

use super::CommandContext;
use crate::chat::transport::matrix::config::save_ambient_rooms;

/// Toggle ambient mode for this room.
///
/// Works whether or not the message directly addressed the bot — the user can
/// say "timmy ambient on", "@timmy ambient on", or just "ambient on" in an
/// ambient-mode room.  The command is specific enough (must be the first word
/// after any bot-mention prefix) that accidental triggering is very unlikely.
pub(super) fn handle_ambient(ctx: &CommandContext) -> Option<String> {
    let enable = match ctx.args {
        "on" => true,
        "off" => false,
        _ => return Some("Usage: `ambient on` or `ambient off`".to_string()),
    };
    let room_ids: Vec<String> = {
        let mut ambient = ctx.services.ambient_rooms.lock().unwrap();
        if enable {
            ambient.insert(ctx.room_id.to_string());
        } else {
            ambient.remove(ctx.room_id);
        }
        ambient.iter().cloned().collect()
    };
    save_ambient_rooms(ctx.effective_root(), &room_ids);
    let msg = if enable {
        "Ambient mode on. I'll respond to all messages in this room."
    } else {
        "Ambient mode off. I'll only respond when mentioned."
    };
    Some(msg.to_string())
}

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

    // Bug 352: ambient commands were being forwarded to LLM after refactors
    // 328/330 because handle_ambient required is_addressed=true, but
    // mentions_bot() only matches @-prefixed mentions, not bare bot names.
    // "timmy ambient off" sets is_addressed=false even though it names the bot.
    #[test]
    fn ambient_on_works_when_unaddressed() {
        let services = crate::services::Services::new_test(
            std::path::PathBuf::from("/tmp"),
            "Timmy".to_string(),
        );
        let room_id = "!myroom:example.com".to_string();
        let dispatch = CommandDispatch {
            services: &services,
            project_root: &services.project_root,
            bot_user_id: "@timmy:homeserver.local",
            room_id: &room_id,
        };
        // "timmy ambient on" — bot name mentioned but not @-prefixed, so
        // is_addressed is false; strip_bot_mention still strips "timmy ".
        let result = try_handle_command(&dispatch, "timmy ambient on");
        assert!(
            result.is_some(),
            "ambient on should fire even when is_addressed=false"
        );
        assert!(
            services.ambient_rooms.lock().unwrap().contains(&room_id),
            "room should be in ambient_rooms after ambient on"
        );
    }

    #[test]
    fn ambient_off_works_bare_in_ambient_room() {
        let services = crate::services::Services::new_test(
            std::path::PathBuf::from("/tmp"),
            "Timmy".to_string(),
        );
        let room_id = "!myroom:example.com".to_string();
        services
            .ambient_rooms
            .lock()
            .unwrap()
            .insert(room_id.clone());
        let dispatch = CommandDispatch {
            services: &services,
            project_root: &services.project_root,
            bot_user_id: "@timmy:homeserver.local",
            room_id: &room_id,
        };
        // Bare "ambient off" in an ambient room (is_addressed=false).
        let result = try_handle_command(&dispatch, "ambient off");
        assert!(
            result.is_some(),
            "bare ambient off should be handled without LLM"
        );
        let output = result.unwrap();
        assert!(
            output.contains("Ambient mode off"),
            "response should confirm ambient off: {output}"
        );
        assert!(
            !services.ambient_rooms.lock().unwrap().contains(&room_id),
            "room should be removed from ambient_rooms after ambient off"
        );
    }

    #[test]
    fn ambient_on_enables_ambient_mode() {
        let services = crate::services::Services::new_test(
            std::path::PathBuf::from("/tmp"),
            "Timmy".to_string(),
        );
        let room_id = "!myroom:example.com".to_string();
        let dispatch = CommandDispatch {
            services: &services,
            project_root: &services.project_root,
            bot_user_id: "@timmy:homeserver.local",
            room_id: &room_id,
        };
        let result = try_handle_command(&dispatch, "@timmy ambient on");
        assert!(result.is_some(), "ambient on should produce a response");
        let output = result.unwrap();
        assert!(
            output.contains("Ambient mode on"),
            "response should confirm ambient on: {output}"
        );
        assert!(
            services.ambient_rooms.lock().unwrap().contains(&room_id),
            "room should be in ambient_rooms after ambient on"
        );
    }

    #[test]
    fn ambient_off_disables_ambient_mode() {
        let services = crate::services::Services::new_test(
            std::path::PathBuf::from("/tmp"),
            "Timmy".to_string(),
        );
        let room_id = "!myroom:example.com".to_string();
        // Pre-insert the room
        services
            .ambient_rooms
            .lock()
            .unwrap()
            .insert(room_id.clone());

        let dispatch = CommandDispatch {
            services: &services,
            project_root: &services.project_root,
            bot_user_id: "@timmy:homeserver.local",
            room_id: &room_id,
        };
        let result = try_handle_command(&dispatch, "@timmy ambient off");
        assert!(result.is_some(), "ambient off should produce a response");
        let output = result.unwrap();
        assert!(
            output.contains("Ambient mode off"),
            "response should confirm ambient off: {output}"
        );
        assert!(
            !services.ambient_rooms.lock().unwrap().contains(&room_id),
            "room should be removed from ambient_rooms after ambient off"
        );
    }

    #[test]
    fn ambient_invalid_args_returns_usage() {
        let result = super::super::tests::try_cmd_addressed(
            "Timmy",
            "@timmy:homeserver.local",
            "@timmy ambient",
        );
        let output = result.unwrap();
        assert!(
            output.contains("Usage"),
            "invalid ambient args should show usage: {output}"
        );
    }
}
