/**
 * Plugin discovery: scans bundled plugins/ directory and user ~/.openwriter/plugins/
 * for available plugins. Reads package.json metadata without importing plugin code.
 */
import { existsSync, readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import { fileURLToPath, pathToFileURL } from 'url';
import { dirname } from 'path';
import { homedir } from 'os';
import { createRequire } from 'module';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const USER_PLUGINS_DIR = join(homedir(), '.openwriter', 'plugins');
/**
 * Scan for bundled plugins. Checks two locations:
 * 1. dist/plugins/ — plugins copied into the npm package at publish time
 * 2. monorepo root plugins/ — local dev with workspace symlinks
 */
function discoverBundledPlugins() {
    // 1. Monorepo root: dist/server/ → ../../../../plugins/ (live dev code)
    const monoPluginsDir = join(__dirname, '..', '..', '..', '..', 'plugins');
    // 2. Bundled in dist: dist/server/ → ../plugins/ (npm install)
    const distPluginsDir = join(__dirname, '..', 'plugins');
    // Prefer monorepo path in dev (live code), fall back to bundled copy in npm install
    const pluginsDir = existsSync(monoPluginsDir) ? monoPluginsDir : distPluginsDir;
    if (!existsSync(pluginsDir))
        return [];
    const results = [];
    for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
        if (!entry.isDirectory())
            continue;
        const pluginDir = join(pluginsDir, entry.name);
        const pkgPath = join(pluginDir, 'package.json');
        if (!existsSync(pkgPath))
            continue;
        try {
            const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
            if (!pkg.name)
                continue;
            const manifest = pkg.openwriter;
            results.push({
                name: pkg.name,
                dirName: entry.name,
                version: pkg.version || '0.0.0',
                description: pkg.description || '',
                source: 'bundled',
                displayName: manifest?.displayName,
                category: manifest?.category,
                pluginDir,
            });
        }
        catch {
            // Skip malformed package.json
        }
    }
    return results;
}
/**
 * Scan ~/.openwriter/plugins/node_modules/ for user-installed plugins.
 * Matches packages with `openwriter` field in package.json or matching naming conventions.
 */
function discoverUserPlugins() {
    const nodeModules = join(USER_PLUGINS_DIR, 'node_modules');
    if (!existsSync(nodeModules))
        return [];
    const results = [];
    const scanDir = (dir) => {
        for (const entry of readdirSync(dir, { withFileTypes: true })) {
            if (!entry.isDirectory())
                continue;
            // Handle scoped packages (@scope/package-name)
            if (entry.name.startsWith('@')) {
                const scopeDir = join(dir, entry.name);
                for (const scoped of readdirSync(scopeDir, { withFileTypes: true })) {
                    if (!scoped.isDirectory())
                        continue;
                    tryAddPlugin(join(scopeDir, scoped.name), `${entry.name}/${scoped.name}`, results);
                }
            }
            else {
                tryAddPlugin(join(dir, entry.name), entry.name, results);
            }
        }
    };
    scanDir(nodeModules);
    return results;
}
function tryAddPlugin(pkgDir, fullName, results) {
    const pkgPath = join(pkgDir, 'package.json');
    if (!existsSync(pkgPath))
        return;
    try {
        const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
        if (!pkg.name)
            return;
        const manifest = pkg.openwriter;
        const isOpenWriterPlugin = manifest ||
            /^openwriter-plugin-/.test(pkg.name) ||
            /^@openwriter\/plugin-/.test(pkg.name) ||
            /^@[\w-]+\/openwriter-plugin-/.test(pkg.name);
        if (!isOpenWriterPlugin)
            return;
        results.push({
            name: pkg.name,
            dirName: fullName,
            version: pkg.version || '0.0.0',
            description: pkg.description || '',
            source: 'user',
            displayName: manifest?.displayName,
            category: manifest?.category,
            pluginDir: pkgDir,
        });
    }
    catch {
        // Skip malformed package.json
    }
}
/** Preferred display order for bundled plugins */
const BUNDLED_ORDER = [
    '@openwriter/plugin-authors-voice',
    '@openwriter/plugin-publish',
    '@openwriter/plugin-image-gen',
    '@openwriter/plugin-x-api',
];
/**
 * Discover all plugins from both bundled and user sources.
 * Deduplicates by name (bundled takes priority).
 */
export function discoverPlugins() {
    const bundled = discoverBundledPlugins();
    const user = discoverUserPlugins();
    // Sort bundled plugins by preferred order (unknown plugins go to end)
    bundled.sort((a, b) => {
        const ai = BUNDLED_ORDER.indexOf(a.name);
        const bi = BUNDLED_ORDER.indexOf(b.name);
        return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
    });
    // Deduplicate: bundled wins if same name exists in both
    const seen = new Set(bundled.map(p => p.name));
    const deduped = [...bundled];
    for (const p of user) {
        if (!seen.has(p.name)) {
            deduped.push(p);
            seen.add(p.name);
        }
    }
    return deduped;
}
/**
 * Import a plugin module and extract its metadata.
 * Uses pluginDir for path-based imports (works in both npm install and monorepo dev).
 */
export async function loadPluginModule(name, source = 'bundled', pluginDir) {
    try {
        let mod;
        if (source === 'user') {
            // ESM import from non-standard node_modules requires path resolution
            const userRequire = createRequire(join(USER_PLUGINS_DIR, 'package.json'));
            const resolved = userRequire.resolve(name);
            mod = await import(pathToFileURL(resolved).href);
        }
        else if (pluginDir) {
            // Path-based import — works for both bundled-in-dist and monorepo plugins
            const mainPath = join(pluginDir, 'dist', 'index.js');
            mod = await import(pathToFileURL(mainPath).href);
        }
        else {
            // Fallback: bare import (workspace symlinks in monorepo dev)
            mod = await import(name);
        }
        const plugin = mod.default || mod.plugin || mod;
        if (!plugin.name || !plugin.version)
            return null;
        return {
            plugin,
            configSchema: plugin.configSchema || {},
        };
    }
    catch (err) {
        console.error(`[PluginDiscovery] Failed to import "${name}" (${source}):`, err.message);
        return null;
    }
}
