use crate::acp;
use crate::permissions::{AcpPermissionPrompter, DefaultPermissionPrompter};
use crate::tooling::AcpToolRegistry;
use hashbrown::HashMap;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc;
use tracing::warn;
use vtcode_core::config::ToolDocumentationMode;
use vtcode_core::config::types::{AgentConfig as CoreAgentConfig, CapabilityLevel};
use vtcode_core::config::{AgentClientProtocolZedConfig, CommandsConfig, ToolsConfig};
use vtcode_core::core::threads::ThreadManager;
use vtcode_core::tools::file_ops::FileOpsTool;
use vtcode_core::tools::grep_file::GrepSearchManager;
use vtcode_core::tools::handlers::{SessionSurface, SessionToolsConfig, ToolModelCapabilities};
use vtcode_core::tools::registry::ToolRegistry as CoreToolRegistry;

use super::types::{NotificationEnvelope, SessionHandle};

mod handlers;
mod prompt;
mod session_state;
mod tool_config;
#[cfg(test)]
mod tool_config_tests;
mod tool_execution;
mod tool_execution_local;
mod updates;

pub(crate) struct ZedAgent {
    config: CoreAgentConfig,
    system_prompt: String,
    sessions: Rc<RefCell<HashMap<acp::SessionId, SessionHandle>>>,
    next_session_id: Cell<u64>,
    session_update_tx: mpsc::UnboundedSender<NotificationEnvelope>,
    acp_tool_registry: Rc<AcpToolRegistry>,
    permission_prompter: Rc<dyn AcpPermissionPrompter>,
    local_tool_registry: CoreToolRegistry,
    file_ops_tool: Option<FileOpsTool>,
    thread_manager: ThreadManager,
    client_capabilities: Rc<RefCell<Option<acp::ClientCapabilities>>>,
    title: Option<String>,
    tool_loop_limit: usize,
    tool_call_delay: Option<Duration>,
}

impl ZedAgent {
    pub(crate) async fn new(
        config: CoreAgentConfig,
        zed_config: AgentClientProtocolZedConfig,
        tools_config: ToolsConfig,
        commands_config: CommandsConfig,
        system_prompt: String,
        session_update_tx: mpsc::UnboundedSender<NotificationEnvelope>,
        title: Option<String>,
    ) -> Self {
        let read_file_enabled = zed_config.tools.read_file;
        let workspace_root = config.workspace.clone();
        let tool_loop_limit = tools_config.max_tool_loops;
        let tool_call_delay = tools_config.tool_call_delay();
        let file_ops_tool = if zed_config.tools.list_files {
            let search_root = workspace_root.clone();
            Some(FileOpsTool::new(
                workspace_root.clone(),
                Arc::new(GrepSearchManager::new(search_root)),
            ))
        } else {
            None
        };
        let list_files_enabled = file_ops_tool.is_some();

        let core_tool_registry = CoreToolRegistry::new(config.workspace.clone()).await;
        if let Err(error) = core_tool_registry
            .apply_tool_runtime_config(&commands_config, &tools_config)
            .await
        {
            warn!(%error, "Failed to apply tools configuration to ACP tool registry");
        }
        let local_definitions = core_tool_registry
            .model_tools(SessionToolsConfig::full_public(
                SessionSurface::Acp,
                CapabilityLevel::CodeSearch,
                ToolDocumentationMode::default(),
                ToolModelCapabilities::default(),
            ))
            .await;
        let acp_tool_registry = Rc::new(AcpToolRegistry::new(
            workspace_root.as_path(),
            read_file_enabled,
            list_files_enabled,
            local_definitions,
        ));
        let permission_prompter: Rc<dyn AcpPermissionPrompter> = Rc::new(
            DefaultPermissionPrompter::new(Rc::clone(&acp_tool_registry)),
        );

        Self {
            config,
            system_prompt,
            sessions: Rc::new(RefCell::new(HashMap::with_capacity(10))),
            next_session_id: Cell::new(0),
            session_update_tx,
            acp_tool_registry,
            permission_prompter,
            local_tool_registry: core_tool_registry,
            file_ops_tool,
            thread_manager: ThreadManager::new(),
            client_capabilities: Rc::new(RefCell::new(None)),
            title,
            tool_loop_limit,
            tool_call_delay,
        }
    }
}
