import { jsx as _jsx } from "react/jsx-runtime";
import { useEffect } from 'react';
import { ConfigurationError, createLLMClient } from '../client-factory.js';
import { commandRegistry } from '../commands.js';
// Built-in commands are registered via a lazy registry so their modules
// aren't loaded at startup — each command's handler is imported on first
// invocation. See `source/commands/lazy-registry.ts`.
import { lazyCommands } from '../commands/lazy-registry.js';
import { ErrorMessage, InfoMessage, WarningMessage, } from '../components/message-box.js';
import { getAppConfig, reloadAppConfig } from '../config/index.js';
import { formatConfigLintIssue, lintProviderConfigs } from '../config/lint.js';
import { loadAllProviderConfigs } from '../config/mcp-config-loader.js';
import { getLastUsedModel, loadPreferences, updateLastUsed, } from '../config/preferences.js';
import { validateProjectConfigSecurity } from '../config/validation.js';
import { TIMEOUT_OUTPUT_FLUSH_MS } from '../constants.js';
import { CustomCommandExecutor } from '../custom-commands/executor.js';
import { CustomCommandLoader } from '../custom-commands/loader.js';
import { getLSPManager } from '../lsp/index.js';
import { setCommandLoaderGetter, setToolManagerGetter, setToolRegistryGetter, } from '../message-handler.js';
import { generateKey } from '../session/key-generator.js';
import { SubagentExecutor } from '../subagents/subagent-executor.js';
import { getSubagentLoader } from '../subagents/subagent-loader.js';
import { setAgentToolExecutor, setAvailableAgentNames } from '../tools/agent-tool.js';
import { clearAllTasks } from '../tools/tasks/index.js';
import { ToolManager } from '../tools/tool-manager.js';
import { setAvailableSubagents } from '../utils/prompt-processor.js';
import { getShutdownManager } from '../utils/shutdown/index.js';
import { checkForUpdates } from '../utils/update-checker.js';
export function useAppInitialization({ setClient, setCurrentModel, setCurrentProvider, setCurrentProviderConfig, setToolManager, setCustomCommandLoader, setCustomCommandExecutor, setCustomCommandCache: _setCustomCommandCache, setStartChat, setMcpInitialized, setUpdateInfo, setMcpServersStatus, setLspServersStatus, setPreferencesLoaded, setCustomCommandsCount, setSubagentsReady, addToChatQueue, customCommandCache, setActiveMode, cliProvider, cliModel, nonInteractiveMode = false, developmentModeRef, }) {
    // Initialize LLM client and model
    const initializeClient = async (preferredProvider, preferredModel) => {
        // Lint provider configs before instantiation so typos and misplaced
        // blocks surface as warnings in the chat queue, without a box —
        // matches the existing "no box" convention for inline diagnostics.
        const lintIssues = lintProviderConfigs(loadAllProviderConfigs());
        for (const issue of lintIssues) {
            addToChatQueue(_jsx(WarningMessage, { message: formatConfigLintIssue(issue), hideBox: true }, generateKey(`config-lint-${issue.provider}`)));
        }
        const { client, actualProvider } = await createLLMClient(preferredProvider, preferredModel);
        setClient(client);
        setCurrentProvider(actualProvider);
        setCurrentProviderConfig(client.getProviderConfig());
        // Use CLI model if provided (already set by createLLMClient), otherwise try last used model
        let finalModel;
        if (preferredModel) {
            finalModel = client.getCurrentModel();
        }
        else {
            // Try to use the last used model for this provider
            const lastUsedModel = getLastUsedModel(actualProvider);
            if (lastUsedModel) {
                const availableModels = await client.getAvailableModels();
                if (availableModels.includes(lastUsedModel)) {
                    client.setModel(lastUsedModel);
                    finalModel = lastUsedModel;
                }
                else {
                    finalModel = client.getCurrentModel();
                }
            }
            else {
                finalModel = client.getCurrentModel();
            }
        }
        setCurrentModel(finalModel);
        setCurrentProviderConfig(client.getProviderConfig());
        // Save the preference - use actualProvider and the model that was actually set
        updateLastUsed(actualProvider, finalModel);
        return client;
    };
    // Seed the autocomplete cache from a populated CustomCommandLoader. The
    // unified bootstrap is responsible for calling loader.loadCommands(); this
    // helper just reads the result back out for the picker UI.
    const refreshCustomCommandCache = (loader) => {
        const customCommands = loader.getAllCommands() || [];
        customCommandCache.clear();
        for (const command of customCommands) {
            customCommandCache.set(command.name, command);
            if (command.metadata?.aliases) {
                for (const alias of command.metadata.aliases) {
                    customCommandCache.set(alias, command);
                }
            }
        }
        setCustomCommandsCount(customCommands.length);
    };
    // Back-compat shim: callers that want to force a reload of the on-disk
    // command files (e.g. /commands refresh).
    const loadCustomCommands = (loader) => {
        loader.loadCommands();
        refreshCustomCommandCache(loader);
    };
    // Initialize MCP servers if configured
    const initializeMCPServers = async (toolManager) => {
        const config = getAppConfig();
        if (config.mcpServers && config.mcpServers.length > 0) {
            // Validate security for project-level configurations
            validateProjectConfigSecurity(config.mcpServers);
            // Initialize status array
            const mcpStatus = config.mcpServers.map(server => ({
                name: server.name,
                status: 'pending',
            }));
            // Define progress callback to update status silently
            const onProgress = (result) => {
                const statusIndex = mcpStatus.findIndex(s => s.name === result.serverName);
                if (statusIndex !== -1) {
                    if (result.success) {
                        mcpStatus[statusIndex] = {
                            name: result.serverName,
                            status: 'connected',
                        };
                    }
                    else {
                        mcpStatus[statusIndex] = {
                            name: result.serverName,
                            status: 'failed',
                            errorMessage: result.error,
                        };
                    }
                    // Update the state with current status
                    setMcpServersStatus([...mcpStatus]);
                }
            };
            try {
                await toolManager.initializeMCP(config.mcpServers, onProgress);
            }
            catch (error) {
                // Mark all pending servers as failed
                mcpStatus.forEach((status, index) => {
                    if (status.status === 'pending') {
                        mcpStatus[index] = {
                            ...status,
                            status: 'failed',
                            errorMessage: String(error),
                        };
                    }
                });
                setMcpServersStatus([...mcpStatus]);
            }
            // Mark MCP as initialized whether successful or not
            setMcpInitialized(true);
        }
        else {
            // No MCP servers configured, set empty status
            setMcpServersStatus([]);
            setMcpInitialized(true);
        }
    };
    // Initialize LSP servers with auto-discovery
    const initializeLSPServers = async () => {
        const lspConfig = getAppConfig();
        const lspManager = await getLSPManager({
            rootUri: `file://${process.cwd()}`,
            autoDiscover: true,
            // Use custom servers from config if provided
            servers: lspConfig.lspServers?.map(server => ({
                name: server.name,
                command: server.command,
                args: server.args,
                languages: server.languages,
                env: server.env,
            })),
        });
        // Initialize status array for configured servers
        const lspStatus = [];
        // Add configured servers to status
        if (lspConfig.lspServers) {
            for (const server of lspConfig.lspServers) {
                lspStatus.push({
                    name: server.name,
                    status: 'pending',
                });
            }
        }
        // Define progress callback to update status silently
        const onProgress = (result) => {
            const statusIndex = lspStatus.findIndex(s => s.name === result.serverName);
            if (statusIndex !== -1) {
                if (result.success) {
                    lspStatus[statusIndex] = {
                        name: result.serverName,
                        status: 'connected',
                    };
                }
                else {
                    // Don't mark auto-discovery failures as errors
                    lspStatus[statusIndex] = {
                        name: result.serverName,
                        status: 'failed',
                        errorMessage: result.error,
                    };
                }
                // Update the state with current status
                setLspServersStatus([...lspStatus]);
            }
            // For auto-discovered servers, add them if successful
            else if (result.success) {
                lspStatus.push({
                    name: result.serverName,
                    status: 'connected',
                });
                setLspServersStatus([...lspStatus]);
            }
        };
        try {
            await lspManager.initialize({
                autoDiscover: true,
                servers: lspConfig.lspServers?.map(server => ({
                    name: server.name,
                    command: server.command,
                    args: server.args,
                    languages: server.languages,
                    env: server.env,
                })),
                onProgress,
            });
            // Mark any remaining pending servers as failed
            lspStatus.forEach((status, index) => {
                if (status.status === 'pending') {
                    lspStatus[index] = {
                        ...status,
                        status: 'failed',
                        errorMessage: 'Connection timeout',
                    };
                }
            });
            setLspServersStatus([...lspStatus]);
        }
        catch (error) {
            // Mark all pending servers as failed
            lspStatus.forEach((status, index) => {
                if (status.status === 'pending') {
                    lspStatus[index] = {
                        ...status,
                        status: 'failed',
                        errorMessage: String(error),
                    };
                }
            });
            setLspServersStatus([...lspStatus]);
        }
    };
    const start = async (toolManager, newCustomCommandLoader, preferences) => {
        try {
            // Use CLI provider/model if provided, otherwise use preferences
            const provider = cliProvider || preferences.lastProvider;
            const model = cliModel || undefined;
            const client = await initializeClient(provider, model);
            // Create and initialize the SubagentExecutor if client was successfully created
            if (client) {
                const executor = new SubagentExecutor(toolManager, client);
                // Read the live development mode per tool call so subagents honor
                // the current mode (and mid-run switches), matching the main loop.
                if (developmentModeRef) {
                    executor.setModeResolver(() => developmentModeRef.current ?? 'normal');
                }
                setAgentToolExecutor(executor);
            }
        }
        catch (error) {
            // Check if it's a ConfigurationError
            if (error instanceof ConfigurationError) {
                // Only trigger wizard if config is empty/missing, not for invalid CLI args
                if (error.isEmptyConfig ||
                    error.message.includes('No providers configured')) {
                    if (nonInteractiveMode) {
                        // Wizard is interactive-only — exit cleanly under `run`.
                        addToChatQueue(_jsx(ErrorMessage, { message: "No providers configured. Run nanocoder interactively to set them up.", hideBox: true }, generateKey('config-error')));
                    }
                    else {
                        addToChatQueue(_jsx(InfoMessage, { message: "Configuration needed. Let's set up your providers...", hideBox: true }, generateKey('config-error')));
                        // Trigger wizard mode after showing UI
                        setTimeout(() => {
                            setActiveMode('configWizard');
                        }, 100);
                    }
                }
                else {
                    // Invalid CLI provider/model - show error and don't trigger wizard
                    addToChatQueue(_jsx(ErrorMessage, { message: error.message, hideBox: true }, generateKey('config-error')));
                }
            }
            else {
                // Regular error - show simple error message
                addToChatQueue(_jsx(ErrorMessage, { message: `No providers available: ${String(error)}`, hideBox: true }, generateKey('init-error')));
            }
            // In non-interactive mode there's no human to recover via /model —
            // keep init from blocking on a null client by exiting once the
            // error has had a chance to flush to stdout.
            if (nonInteractiveMode) {
                setTimeout(() => {
                    void getShutdownManager().gracefulShutdown(1);
                }, TIMEOUT_OUTPUT_FLUSH_MS);
            }
            // Leave client as null - the UI will handle this gracefully
        }
        // Unified skill boot: runs the legacy loaders (CustomCommandLoader,
        // SubagentLoader, ToolManager.initializeCustomTools), then layers
        // bundle-form skills on top via the registrar. /skills sees both
        // forms; the TUI itself does not host event sources (the daemon does)
        // so subscriptions register but do not fire here.
        try {
            const { EventRouter } = await import('../events/event-router.js');
            const { bootSkillPipeline } = await import('../skills/bootstrap.js');
            const { getSubagentLoader } = await import('../subagents/subagent-loader.js');
            const router = new EventRouter({ dispatch: () => { } });
            const subagentLoader = getSubagentLoader();
            const result = await bootSkillPipeline({
                projectRoot: process.cwd(),
                toolManager,
                commandLoader: newCustomCommandLoader,
                subagentLoader,
                eventRouter: router,
            });
            refreshCustomCommandCache(newCustomCommandLoader);
            // The critical-path branch above publishes the agent list *before*
            // bundle skills register their subagents. Re-publish now so the
            // `agent` tool's parameter description and the system prompt's
            // subagent block include bundle agents (e.g. k8s_agent).
            const refreshedAgents = await subagentLoader.listSubagents();
            const refreshedSummaries = refreshedAgents.map(a => ({
                name: a.name,
                description: a.description,
            }));
            setAvailableSubagents(refreshedSummaries);
            setAvailableAgentNames(refreshedSummaries);
            for (const err of result.loadErrors) {
                const where = err.filePath ?? err.bundlePath;
                addToChatQueue(_jsx(ErrorMessage, { message: `Skill load error (${where}): ${err.message}`, hideBox: true }, generateKey('skill-load-error')));
            }
            for (const c of result.registration.collisions) {
                addToChatQueue(_jsx(ErrorMessage, { message: `Skill collision (${c.skill} ${c.kind}:${c.name}): ${c.message}`, hideBox: true }, generateKey('skill-collision')));
            }
            for (const warning of result.deprecations) {
                addToChatQueue(_jsx(ErrorMessage, { message: warning, hideBox: true }, generateKey('skill-deprecation')));
            }
        }
        catch (error) {
            addToChatQueue(_jsx(ErrorMessage, { message: `Failed to load skill bundles: ${String(error)}`, hideBox: true }, generateKey('skill-init-error')));
        }
    };
    // biome-ignore lint/correctness/useExhaustiveDependencies: Initialization effect should only run once on mount
    useEffect(() => {
        const initializeApp = async () => {
            setClient(null);
            setCurrentModel('');
            setCurrentProviderConfig(null);
            // Clear task list — fire-and-forget, just deletes a JSON file
            void clearAllTasks();
            const newToolManager = new ToolManager();
            const newCustomCommandLoader = new CustomCommandLoader();
            const newCustomCommandExecutor = new CustomCommandExecutor();
            setToolManager(newToolManager);
            setCustomCommandLoader(newCustomCommandLoader);
            setCustomCommandExecutor(newCustomCommandExecutor);
            // Load preferences - we'll pass them directly to avoid state timing issues
            const preferences = loadPreferences();
            // Mark preferences as loaded for display in Status component
            setPreferencesLoaded(true);
            // Set up the tool registry getter for the message handler
            setToolRegistryGetter(() => newToolManager.getToolRegistry());
            // Set up the tool manager getter for commands that need it
            setToolManagerGetter(() => newToolManager);
            // Set up the command loader getter so /commands and friends
            // read from the same instance the bootstrap populates.
            setCommandLoaderGetter(() => newCustomCommandLoader);
            commandRegistry.registerLazy(lazyCommands);
            // === CRITICAL PATH ===
            // LLM client + subagents are independent — run in parallel.
            // Everything else (update check, MCP, LSP) runs in the background.
            const subagentLoader = getSubagentLoader();
            await Promise.all([
                start(newToolManager, newCustomCommandLoader, preferences),
                subagentLoader.initialize().then(async () => {
                    const availableAgents = await subagentLoader.listSubagents();
                    const agentSummaries = availableAgents.map(a => ({
                        name: a.name,
                        description: a.description,
                    }));
                    setAvailableSubagents(agentSummaries);
                    setAvailableAgentNames(agentSummaries);
                    setSubagentsReady(true);
                }),
            ]);
            // === SHOW CHAT UI ===
            // The Status box was removed from startup (it's now /status),
            // so nothing gates on MCP/LSP/update-check completing. Show
            // the prompt immediately after the LLM client + subagents are
            // ready. Everything else connects in the background.
            setMcpInitialized(true);
            setStartChat(true);
            // === BACKGROUND WORK ===
            // All three run concurrently after the chat is interactive.
            // MCP tools register dynamically as servers connect; LSP
            // diagnostics appear when ready; update banner shows when the
            // npm check resolves.
            void checkForUpdates()
                .then(info => setUpdateInfo(info))
                .catch(() => setUpdateInfo(null));
            void initializeMCPServers(newToolManager);
            void initializeLSPServers();
        };
        void initializeApp();
    }, []);
    return {
        initializeClient,
        loadCustomCommands,
        initializeMCPServers,
        reinitializeMCPServers: async (toolManager) => {
            // Reload app config to get latest MCP servers
            reloadAppConfig();
            // Reinitialize MCP servers with new configuration
            await initializeMCPServers(toolManager);
        },
        initializeLSPServers,
    };
}
//# sourceMappingURL=useAppInitialization.js.map