// engine/plugins/lifecycle.ts — Plugin lifecycle management (SPEC-085 AC-03)
import { cp, rm, readFile, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { getPluginsDir, loadPluginModule } from './loader.js';
import { validateManifestStructure } from './validator.js';
import { withPluginTimeout } from './sandbox.js';
import { registerPluginTools, unregisterPluginTools } from './extensions/tool-extension.js';
import { registerPluginWorkers, unregisterPluginWorkers } from './extensions/worker-extension.js';
import { registerPluginAnalyzers, unregisterPluginAnalyzers, } from './extensions/analyzer-extension.js';
import { registerPluginTemplates, unregisterPluginTemplates, } from './extensions/template-extension.js';
const STATE_FILE = 'plugins-state.json';
function stateFilePath(dataDir) {
    return join(dataDir, 'global', STATE_FILE);
}
/**
 * Reads plugin state from disk.
 */
export async function readPluginState(dataDir) {
    try {
        const raw = await readFile(stateFilePath(dataDir), 'utf8');
        return JSON.parse(raw);
    }
    catch {
        return { plugins: [], lastUpdated: new Date().toISOString() };
    }
}
/**
 * Writes plugin state to disk.
 */
export async function writePluginState(dataDir, state) {
    const dir = join(dataDir, 'global');
    await mkdir(dir, { recursive: true });
    await writeFile(stateFilePath(dataDir), JSON.stringify(state, null, 2), 'utf8');
}
/**
 * Installs a plugin by copying it to the plugins directory.
 */
export async function installPlugin(sourcePath, projectPath, dataDir) {
    const pluginsDir = getPluginsDir(projectPath);
    await mkdir(pluginsDir, { recursive: true });
    // Read and validate manifest from source
    const manifestRaw = await readFile(join(sourcePath, 'plugin.json'), 'utf8');
    const manifest = JSON.parse(manifestRaw);
    const validation = validateManifestStructure(manifest);
    if (!validation.valid) {
        throw new Error(`Invalid plugin manifest: ${validation.errors.join('; ')}`);
    }
    const typedManifest = manifest;
    const targetDir = join(pluginsDir, typedManifest.id);
    // Copy plugin to plugins directory
    await cp(sourcePath, targetDir, { recursive: true });
    const registration = {
        manifest: manifest,
        state: 'installed',
        loadedAt: new Date().toISOString(),
    };
    // Persist state
    const state = await readPluginState(dataDir);
    const existing = state.plugins.findIndex((p) => p.manifest.id === typedManifest.id);
    if (existing >= 0) {
        state.plugins[existing] = registration;
    }
    else {
        state.plugins.push(registration);
    }
    state.plugins = [...state.plugins]; // ensure fresh array
    await writePluginState(dataDir, {
        plugins: state.plugins,
        lastUpdated: new Date().toISOString(),
    });
    return registration;
}
/**
 * Activates a plugin: loads module, registers extensions, runs onActivate hook.
 */
export async function activatePlugin(pluginId, projectPath, dataDir, builtInTools) {
    const state = await readPluginState(dataDir);
    const regIndex = state.plugins.findIndex((p) => p.manifest.id === pluginId);
    if (regIndex < 0) {
        throw new Error(`Plugin "${pluginId}" is not installed`);
    }
    const reg = state.plugins[regIndex];
    /* v8 ignore next 3 -- unreachable: findIndex already validated index */
    if (!reg) {
        throw new Error(`Plugin "${pluginId}" registration not found`);
    }
    if (reg.state === 'active') {
        return reg;
    }
    let mod;
    try {
        mod = await loadPluginModule(projectPath, reg.manifest);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        const errorReg = {
            ...reg,
            state: 'error',
            error: message,
        };
        state.plugins[regIndex] = errorReg;
        await writePluginState(dataDir, {
            plugins: state.plugins,
            lastUpdated: new Date().toISOString(),
        });
        return errorReg;
    }
    // Register extensions
    try {
        if (mod.tools) {
            registerPluginTools(pluginId, mod.tools, builtInTools);
        }
        if (mod.workers) {
            registerPluginWorkers(pluginId, mod.workers);
        }
        if (mod.analyzers) {
            registerPluginAnalyzers(pluginId, mod.analyzers);
        }
        if (mod.templates) {
            registerPluginTemplates(pluginId, mod.templates);
        }
        // Run onActivate hook
        if (mod.hooks?.onActivate) {
            await withPluginTimeout(pluginId, 'onActivate', mod.hooks.onActivate);
        }
    }
    catch (err) {
        // Rollback: unregister everything
        unregisterPluginTools(pluginId);
        unregisterPluginWorkers(pluginId);
        unregisterPluginAnalyzers(pluginId);
        unregisterPluginTemplates(pluginId);
        const message = err instanceof Error ? err.message : String(err);
        const errorReg = {
            ...reg,
            state: 'error',
            error: message,
        };
        state.plugins[regIndex] = errorReg;
        await writePluginState(dataDir, {
            plugins: state.plugins,
            lastUpdated: new Date().toISOString(),
        });
        return errorReg;
    }
    const activeReg = { ...reg, state: 'active' };
    delete activeReg.error;
    state.plugins[regIndex] = activeReg;
    await writePluginState(dataDir, {
        plugins: state.plugins,
        lastUpdated: new Date().toISOString(),
    });
    return activeReg;
}
/**
 * Deactivates a plugin: unregisters extensions, runs onDeactivate hook.
 */
export async function deactivatePlugin(pluginId, dataDir) {
    const state = await readPluginState(dataDir);
    const regIndex = state.plugins.findIndex((p) => p.manifest.id === pluginId);
    if (regIndex < 0) {
        throw new Error(`Plugin "${pluginId}" is not installed`);
    }
    const reg = state.plugins[regIndex];
    /* v8 ignore next 3 -- unreachable: findIndex already validated index */
    if (!reg) {
        throw new Error(`Plugin "${pluginId}" registration not found`);
    }
    // Unregister all extensions
    unregisterPluginTools(pluginId);
    unregisterPluginWorkers(pluginId);
    unregisterPluginAnalyzers(pluginId);
    unregisterPluginTemplates(pluginId);
    const inactiveReg = { ...reg, state: 'inactive' };
    delete inactiveReg.error;
    state.plugins[regIndex] = inactiveReg;
    await writePluginState(dataDir, {
        plugins: state.plugins,
        lastUpdated: new Date().toISOString(),
    });
    return inactiveReg;
}
/**
 * Uninstalls a plugin: deactivates if active, removes directory and state.
 */
export async function uninstallPlugin(pluginId, projectPath, dataDir) {
    const state = await readPluginState(dataDir);
    const regIndex = state.plugins.findIndex((p) => p.manifest.id === pluginId);
    if (regIndex < 0) {
        throw new Error(`Plugin "${pluginId}" is not installed`);
    }
    const reg = state.plugins[regIndex];
    if (reg?.state === 'active') {
        await deactivatePlugin(pluginId, dataDir);
    }
    // Remove plugin directory
    const pluginsDir = getPluginsDir(projectPath);
    const pluginDir = join(pluginsDir, pluginId);
    await rm(pluginDir, { recursive: true, force: true });
    // Remove from state
    const updatedState = await readPluginState(dataDir);
    updatedState.plugins = updatedState.plugins.filter((p) => p.manifest.id !== pluginId);
    await writePluginState(dataDir, {
        plugins: updatedState.plugins,
        lastUpdated: new Date().toISOString(),
    });
}
//# sourceMappingURL=lifecycle.js.map