/**
 * ServiceContainer - Dependency injection container for Bash services.
 *
 * Replaces the singleton pattern used in v2.x with explicit instance
 * ownership. Each Bash instance creates its own ServiceContainer,
 * ensuring full isolation between shell instances.
 *
 * v5.0.0: Lazy initialization for all services except astCache and sharedBus.
 * Services are only instantiated on first access, reducing startup cost.
 *
 * v6.0.0: Descriptor-registry model. Each lazy service is described by a
 * {@link ServiceDescriptor} carrying metadata (bus-aware, disposable). The
 * registry centralizes bus wiring and ensures dispose() iterates EVERY
 * instantiated disposable instead of a hardcoded subset. AgentMemory is
 * hydrated from / persisted to the VFS via an optional fs accessor.
 */
import { Orchestrator } from "../agentic/Orchestrator.js";
import { LSPManager } from "../lsp/LSPManager.js";
import { ASTCache } from "../parser/ASTCache.js";
import { TreeSitterParser } from "../parser/tree-sitter-parser.js";
import { AgentManager } from "./AgentManager.js";
import { AgentMemory } from "./AgentMemory.js";
import { loadMemoryFromFs, saveMemoryToFs, } from "./AgentMemorySync.js";
import { CronScheduler } from "./CronScheduler.js";
import { GitTracker } from "./GitTracker.js";
import { McpClient } from "./McpClient.js";
import { SessionManager } from "./SessionManager.js";
import { SharedStateBus } from "./SharedStateBus.js";
import { TaskManager } from "./TaskManager.js";
import { TeamManager } from "./TeamManager.js";
import { WorktreeManager } from "./WorktreeManager.js";
export function createDefaultServices(overrides, fsAccessor) {
    // Eager services - universally needed
    const astCache = overrides?.astCache ?? new ASTCache();
    const sharedBus = overrides?.sharedBus ?? new SharedStateBus();
    // Lazy backing fields
    let _sessionManager = overrides?.sessionManager ?? null;
    let _agentManager = overrides?.agentManager ?? null;
    let _mcpClient = overrides?.mcpClient ?? null;
    let _orchestrator = overrides?.orchestrator ?? null;
    let _lspManager = overrides?.lspManager ?? null;
    let _taskManager = overrides?.taskManager ?? null;
    let _teamManager = overrides?.teamManager ?? null;
    let _agentMemory = overrides?.agentMemory ?? null;
    let _gitTracker = overrides?.gitTracker ?? null;
    let _cronScheduler = overrides?.cronScheduler ?? null;
    let _worktreeManager = overrides?.worktreeManager ?? null;
    let _parser = overrides?.parser ?? null;
    // Memoized AgentMemory hydration promise. Created on first agentMemory
    // access (when an fsAccessor is present) and awaited by
    // ensureAgentMemoryHydrated().
    let _agentMemoryHydration = null;
    let disposed = false;
    /**
     * Centralized lazy-construction helper for bus-aware services. Wires the
     * shared bus exactly once, on construction, replacing the previous five
     * ad-hoc `setBus` call sites.
     */
    function wireBus(instance) {
        instance.setBus(sharedBus);
        return instance;
    }
    function ensureAgentMemoryInstance() {
        if (_agentMemory)
            return _agentMemory;
        _agentMemory = new AgentMemory();
        // Kick off (memoized) hydration in the background when an fs accessor is
        // available. Failures degrade gracefully - loadMemoryFromFs already
        // tolerates missing scope dirs and corrupt files.
        if (fsAccessor && !_agentMemoryHydration) {
            const memory = _agentMemory;
            _agentMemoryHydration = (async () => {
                try {
                    await loadMemoryFromFs(memory, fsAccessor());
                }
                catch {
                    // Hydration is best-effort; never block on a broken VFS.
                }
            })();
        }
        return _agentMemory;
    }
    // The descriptor registry. Order matters: dispose() iterates in reverse to
    // tear down in reverse construction order (mirrors the prior behavior).
    const registry = [
        {
            key: "sessionManager",
            isBusAware: false,
            isDisposable: true,
            getInstance: () => _sessionManager,
        },
        {
            key: "agentManager",
            isBusAware: false,
            isDisposable: false,
            getInstance: () => _agentManager,
        },
        {
            key: "mcpClient",
            isBusAware: false,
            isDisposable: true,
            getInstance: () => _mcpClient,
        },
        {
            key: "orchestrator",
            isBusAware: false,
            isDisposable: false,
            getInstance: () => _orchestrator,
        },
        {
            key: "lspManager",
            isBusAware: false,
            isDisposable: false,
            getInstance: () => _lspManager,
        },
        {
            key: "taskManager",
            isBusAware: true,
            isDisposable: false,
            getInstance: () => _taskManager,
        },
        {
            key: "teamManager",
            isBusAware: true,
            isDisposable: false,
            getInstance: () => _teamManager,
        },
        {
            key: "agentMemory",
            isBusAware: false,
            isDisposable: false,
            getInstance: () => _agentMemory,
        },
        {
            key: "gitTracker",
            isBusAware: true,
            isDisposable: true,
            getInstance: () => _gitTracker,
        },
        {
            key: "cronScheduler",
            isBusAware: true,
            isDisposable: true,
            getInstance: () => _cronScheduler,
        },
        {
            key: "worktreeManager",
            isBusAware: true,
            isDisposable: false,
            getInstance: () => _worktreeManager,
        },
    ];
    return {
        astCache,
        sharedBus,
        get sessionManager() {
            _sessionManager ??= new SessionManager();
            return _sessionManager;
        },
        get agentManager() {
            _agentManager ??= new AgentManager();
            return _agentManager;
        },
        get mcpClient() {
            _mcpClient ??= new McpClient();
            return _mcpClient;
        },
        get orchestrator() {
            _orchestrator ??= new Orchestrator();
            return _orchestrator;
        },
        get lspManager() {
            _lspManager ??= new LSPManager();
            return _lspManager;
        },
        get taskManager() {
            _taskManager ??= wireBus(new TaskManager());
            return _taskManager;
        },
        get teamManager() {
            _teamManager ??= wireBus(new TeamManager());
            return _teamManager;
        },
        get agentMemory() {
            return ensureAgentMemoryInstance();
        },
        get gitTracker() {
            _gitTracker ??= wireBus(new GitTracker());
            return _gitTracker;
        },
        get cronScheduler() {
            _cronScheduler ??= wireBus(new CronScheduler());
            return _cronScheduler;
        },
        get worktreeManager() {
            _worktreeManager ??= wireBus(new WorktreeManager());
            return _worktreeManager;
        },
        get parser() {
            _parser ??= TreeSitterParser;
            return _parser;
        },
        async ensureAgentMemoryHydrated() {
            // Constructing the instance also schedules hydration (when an fs
            // accessor is present), so ensure it exists first.
            ensureAgentMemoryInstance();
            if (_agentMemoryHydration) {
                await _agentMemoryHydration;
            }
        },
        async dispose() {
            if (disposed)
                return;
            disposed = true;
            const errors = [];
            // Persist AgentMemory before tearing down, if it was instantiated and an
            // fs accessor is available. Wait for any in-flight hydration first so we
            // never save a partially-loaded store over good data.
            if (_agentMemory && fsAccessor) {
                try {
                    if (_agentMemoryHydration) {
                        await _agentMemoryHydration;
                    }
                    await saveMemoryToFs(_agentMemory, fsAccessor());
                }
                catch (e) {
                    errors.push(e instanceof Error ? e : new Error(String(e)));
                }
            }
            // Dispose every instantiated disposable, in reverse construction order.
            for (const descriptor of [...registry].reverse()) {
                if (!descriptor.isDisposable)
                    continue;
                const instance = descriptor.getInstance();
                if (!instance)
                    continue;
                try {
                    await instance.dispose();
                }
                catch (e) {
                    errors.push(e instanceof Error ? e : new Error(String(e)));
                }
            }
            sharedBus.destroy();
            if (errors.length > 0) {
                throw new AggregateError(errors, `Dispose failed for ${errors.length} service(s)`);
            }
        },
    };
}
