use std::sync::Arc;

use crate::api::Client as ApiClient;
use crate::app::domain::session::EventStore;
use crate::app::validation::ValidatorRegistry;
use crate::model_registry::ModelRegistry;
use crate::workspace::{RepoManager, Workspace, WorkspaceManager};

use super::BackendRegistry;
use super::agent_spawner_impl::DefaultAgentSpawner;
use super::builtin_tools::register_builtin_tools;
use super::executor::ToolExecutor;
use super::model_caller_impl::DefaultModelCaller;
use super::registry::ToolRegistry;
use super::services::ToolServices;

pub struct ToolSystemBuilder {
    workspace: Arc<dyn Workspace>,
    event_store: Arc<dyn EventStore>,
    api_client: Arc<ApiClient>,
    model_registry: Arc<ModelRegistry>,
    backend_registry: Arc<BackendRegistry>,
    validators: Arc<ValidatorRegistry>,
    workspace_manager: Option<Arc<dyn WorkspaceManager>>,
    repo_manager: Option<Arc<dyn RepoManager>>,
}

impl ToolSystemBuilder {
    pub fn new(
        workspace: Arc<dyn Workspace>,
        event_store: Arc<dyn EventStore>,
        api_client: Arc<ApiClient>,
        model_registry: Arc<ModelRegistry>,
    ) -> Self {
        Self {
            workspace,
            event_store,
            api_client,
            model_registry,
            backend_registry: Arc::new(BackendRegistry::new()),
            validators: Arc::new(ValidatorRegistry::new()),
            workspace_manager: None,
            repo_manager: None,
        }
    }

    pub fn with_backend_registry(mut self, registry: Arc<BackendRegistry>) -> Self {
        self.backend_registry = registry;
        self
    }

    pub fn with_validators(mut self, validators: Arc<ValidatorRegistry>) -> Self {
        self.validators = validators;
        self
    }

    pub fn with_workspace_manager(mut self, manager: Arc<dyn WorkspaceManager>) -> Self {
        self.workspace_manager = Some(manager);
        self
    }

    pub fn with_repo_manager(mut self, manager: Arc<dyn RepoManager>) -> Self {
        self.repo_manager = Some(manager);
        self
    }

    pub fn build(self) -> Arc<ToolExecutor> {
        let base_executor = ToolExecutor::with_components(self.backend_registry, self.validators);

        let agent_spawner = Arc::new(DefaultAgentSpawner::new(
            self.event_store.clone(),
            self.api_client.clone(),
            self.workspace.clone(),
            self.model_registry,
            self.workspace_manager.clone(),
            self.repo_manager.clone(),
        ));

        let model_caller = Arc::new(DefaultModelCaller::new(self.api_client.clone()));

        let mut services =
            ToolServices::new(self.workspace.clone(), self.event_store, self.api_client)
                .with_agent_spawner(agent_spawner)
                .with_model_caller(model_caller)
                .with_network();
        if let Some(manager) = self.workspace_manager {
            services = services.with_workspace_manager(manager);
        }
        if let Some(manager) = self.repo_manager {
            services = services.with_repo_manager(manager);
        }
        let services = Arc::new(services);

        let mut registry = ToolRegistry::new();
        register_builtin_tools(&mut registry);

        Arc::new(base_executor.with_builtin_tools(Arc::new(registry), services))
    }
}
