use crate::agents::AgentPool;
use crate::chat::timer::TimerStore;
use crate::chat::ChatTransport;
use crate::http::context::{PermissionDecision, PermissionForward};
use matrix_sdk::ruma::{OwnedEventId, OwnedRoomId, OwnedUserId};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
use tokio::sync::{mpsc, oneshot};

use super::history::ConversationHistory;

/// Shared context injected into Matrix event handlers.
#[derive(Clone)]
pub struct BotContext {
    pub bot_user_id: OwnedUserId,
    /// All room IDs the bot listens in.
    pub target_room_ids: Vec<OwnedRoomId>,
    pub project_root: PathBuf,
    pub allowed_users: Vec<String>,
    /// Shared, per-room rolling conversation history.
    pub history: ConversationHistory,
    /// Maximum number of entries to keep per room before trimming the oldest.
    pub history_size: usize,
    /// Event IDs of messages the bot has sent.  Used to detect replies to the
    /// bot so it can continue a conversation thread without requiring an
    /// explicit `@mention` on every follow-up.
    pub bot_sent_event_ids: Arc<TokioMutex<HashSet<OwnedEventId>>>,
    /// Receiver for permission requests from the MCP `prompt_permission` tool.
    /// During an active chat the bot locks this to poll for incoming requests.
    pub perm_rx: Arc<TokioMutex<mpsc::UnboundedReceiver<PermissionForward>>>,
    /// Per-room pending permission reply senders. When a permission prompt is
    /// posted to a room the oneshot sender is stored here; when the user
    /// replies (yes/no) the event handler resolves it.
    pub pending_perm_replies:
        Arc<TokioMutex<HashMap<OwnedRoomId, oneshot::Sender<PermissionDecision>>>>,
    /// How long to wait for a user to respond to a permission prompt before
    /// denying (fail-closed).
    pub permission_timeout_secs: u64,
    /// The name the bot uses to refer to itself. Derived from `display_name`
    /// in bot.toml; defaults to "Assistant" when unset.
    pub bot_name: String,
    /// Set of room IDs where ambient mode is active.  In ambient mode the bot
    /// responds to all messages rather than only addressed ones.
    /// Uses a sync mutex since locks are never held across await points.
    /// Room IDs are stored as plain strings (platform-agnostic).
    pub ambient_rooms: Arc<std::sync::Mutex<HashSet<String>>>,
    /// Agent pool for checking agent availability.
    pub agents: Arc<AgentPool>,
    /// Per-room htop monitoring sessions.  Keyed by room ID; each entry holds
    /// a stop-signal sender that the background task watches.
    pub htop_sessions: super::super::htop::HtopSessions,
    /// Chat transport used for sending and editing messages.
    ///
    /// All message I/O goes through this abstraction so the bot logic works
    /// with any platform, not just Matrix.
    pub transport: Arc<dyn ChatTransport>,
    /// Persistent store for pending deferred-start timers.
    pub timer_store: Arc<TimerStore>,
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;
    use tokio::sync::mpsc;

    fn make_user_id(s: &str) -> OwnedUserId {
        s.parse().unwrap()
    }

    #[test]
    fn bot_context_is_clone() {
        // BotContext must be Clone for the Matrix event handler injection.
        fn assert_clone<T: Clone>() {}
        assert_clone::<BotContext>();
    }

    #[test]
    fn bot_context_has_no_require_verified_devices_field() {
        // Verification is always on — BotContext no longer has a toggle field.
        // This test verifies the struct can be constructed and cloned without it.
        let (_perm_tx, perm_rx) = mpsc::unbounded_channel();
        let ctx = BotContext {
            bot_user_id: make_user_id("@bot:example.com"),
            target_room_ids: vec![],
            project_root: PathBuf::from("/tmp"),
            allowed_users: vec![],
            history: Arc::new(TokioMutex::new(HashMap::new())),
            history_size: 20,
            bot_sent_event_ids: Arc::new(TokioMutex::new(HashSet::new())),
            perm_rx: Arc::new(TokioMutex::new(perm_rx)),
            pending_perm_replies: Arc::new(TokioMutex::new(HashMap::new())),
            permission_timeout_secs: 120,
            bot_name: "Assistant".to_string(),
            ambient_rooms: Arc::new(std::sync::Mutex::new(HashSet::new())),
            agents: Arc::new(crate::agents::AgentPool::new_test(3000)),
            htop_sessions: Arc::new(TokioMutex::new(HashMap::new())),
            transport: Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new(
                "test-phone".to_string(),
                "test-token".to_string(),
                "pipeline_notification".to_string(),
            )),
            timer_store: Arc::new(crate::chat::timer::TimerStore::load(
                std::path::PathBuf::from("/tmp/timers.json"),
            )),
        };
        // Clone must work (required by Matrix SDK event handler injection).
        let _cloned = ctx.clone();
    }
}
