import { rm } from 'node:fs/promises';
import { join } from 'node:path';
import { buildCommandContext } from '../../../app/command-context.js';
import { withScopeOption } from '../../shared/scope-option.js';
import { readGlobalOptions, resolveConcreteScopes, } from '../../shared/shared.utils.js';
import { DEFAULT_SYNC_CONFIG, loadSyncConfig, } from '../../../config/sync-config.js';
import { dirExists, fileExists } from '../../../fs/io.js';
import { resolveProjectRoot, resolveScopeRoot } from '../../../fs/paths.js';
import { loadManifest, removeEntry, saveManifest, } from '../../../manifest/index.js';
import { claudeAdapter } from '../../../providers/claude/index.js';
import { codexAdapter } from '../../../providers/codex/index.js';
import { copilotAdapter } from '../../../providers/copilot/index.js';
import { cursorAdapter } from '../../../providers/cursor/index.js';
import { geminiAdapter } from '../../../providers/gemini/index.js';
import { getConfigAwareAdapters, getSyncMappings, } from '../../../providers/shared/index.js';
import { getAdoptionSources } from '../../../providers/shared/adapter.utils.js';
import { Command } from 'commander';
export function createDefaultRemoveSkillDependencies() {
    return {
        buildCommandContext,
        async resolveScopeRoot(scope, context) {
            if (scope === 'project') {
                return resolveProjectRoot(context.cwd);
            }
            return resolveScopeRoot(scope, context.cwd, context.home);
        },
        loadManifest,
        saveManifest,
        async loadSyncConfig(configPath) {
            return loadSyncConfig(configPath, DEFAULT_SYNC_CONFIG);
        },
        getAdapters() {
            return [
                claudeAdapter,
                cursorAdapter,
                codexAdapter,
                copilotAdapter,
                geminiAdapter,
            ];
        },
        getConfigAwareAdapters,
        getSyncMappings,
        getAdoptionSources,
        async pathExists(path) {
            return (await fileExists(path)) || (await dirExists(path));
        },
        async removeDirectory(path) {
            await rm(path, { recursive: true, force: true });
        },
    };
}
function dedupeViews(views) {
    const seen = new Set();
    return views.filter((view) => {
        const key = `${view.provider}::${view.relativePath}`;
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    });
}
async function buildScopePlan(scope, scopeRoot, skillName, dependencies) {
    const canonicalRelativePath = `.agents/skills/${skillName}`;
    const canonicalAbsolutePath = join(scopeRoot, '.agents', 'skills', skillName);
    const canonicalExists = await dependencies.pathExists(canonicalAbsolutePath);
    if (!canonicalExists) {
        return null;
    }
    const manifestPath = join(scopeRoot, '.oat', 'sync', 'manifest.json');
    const configPath = join(scopeRoot, '.oat', 'sync', 'config.json');
    const [manifest, config] = await Promise.all([
        dependencies.loadManifest(manifestPath),
        dependencies.loadSyncConfig(configPath),
    ]);
    const adapters = dependencies.getAdapters();
    const { activeAdapters } = await dependencies.getConfigAwareAdapters(adapters, scopeRoot, config);
    const managedProviderViews = [];
    const unmanagedProviderViews = [];
    for (const adapter of activeAdapters) {
        const syncMappings = dependencies
            .getSyncMappings(adapter, scope)
            .filter((mapping) => mapping.contentType === 'skill');
        for (const mapping of syncMappings) {
            const relativePath = join(mapping.providerDir, skillName);
            const absolutePath = join(scopeRoot, relativePath);
            const hasManifestEntry = manifest.entries.some((entry) => entry.canonicalPath === canonicalRelativePath &&
                entry.provider === adapter.name);
            const providerPathExists = await dependencies.pathExists(absolutePath);
            if (hasManifestEntry) {
                managedProviderViews.push({
                    provider: adapter.name,
                    absolutePath,
                    relativePath,
                });
                continue;
            }
            if (providerPathExists) {
                unmanagedProviderViews.push({
                    provider: adapter.name,
                    absolutePath,
                    relativePath,
                });
            }
        }
        const syncProviderDirs = new Set(syncMappings.map((mapping) => mapping.providerDir));
        const localAdoptionSources = dependencies
            .getAdoptionSources(adapter, scope)
            .filter((source) => source.contentType === 'skill' &&
            !syncProviderDirs.has(source.directory));
        for (const source of localAdoptionSources) {
            const relativePath = join(source.directory, skillName);
            const absolutePath = join(scopeRoot, relativePath);
            if (await dependencies.pathExists(absolutePath)) {
                unmanagedProviderViews.push({
                    provider: adapter.name,
                    absolutePath,
                    relativePath,
                });
            }
        }
    }
    return {
        scope,
        scopeRoot,
        manifestPath,
        manifest,
        canonicalRelativePath,
        canonicalAbsolutePath,
        managedProviderViews: dedupeViews(managedProviderViews),
        unmanagedProviderViews: dedupeViews(unmanagedProviderViews),
    };
}
function logDryRun(context, plan) {
    context.logger.info(`[dry-run][${plan.scope}] remove ${plan.canonicalRelativePath}`);
    if (plan.managedProviderViews.length > 0) {
        context.logger.info(`[dry-run][${plan.scope}] managed provider views:`);
        for (const view of plan.managedProviderViews) {
            context.logger.info(`  - ${view.provider}: ${view.relativePath}`);
        }
    }
    if (plan.unmanagedProviderViews.length > 0) {
        context.logger.warn(`[dry-run][${plan.scope}] unmanaged provider views (not deleted):`);
        for (const view of plan.unmanagedProviderViews) {
            context.logger.warn(`  - ${view.provider}: ${view.relativePath}`);
        }
    }
}
function toJsonScopeResults(plans) {
    return plans.map((plan) => ({
        scope: plan.scope,
        canonicalPath: plan.canonicalRelativePath,
        managedProviderViews: plan.managedProviderViews
            .map((view) => view.relativePath)
            .sort(),
        unmanagedProviderViews: plan.unmanagedProviderViews
            .map((view) => view.relativePath)
            .sort(),
    }));
}
async function applyPlan(context, plan, dependencies) {
    await dependencies.removeDirectory(plan.canonicalAbsolutePath);
    for (const view of plan.managedProviderViews) {
        await dependencies.removeDirectory(view.absolutePath);
    }
    let nextManifest = plan.manifest;
    for (const view of plan.managedProviderViews) {
        nextManifest = removeEntry(nextManifest, plan.canonicalRelativePath, view.provider);
    }
    await dependencies.saveManifest(plan.manifestPath, nextManifest);
    context.logger.info(`[apply][${plan.scope}] removed ${plan.canonicalRelativePath}`);
    if (plan.unmanagedProviderViews.length > 0) {
        context.logger.warn(`[apply][${plan.scope}] unmanaged provider views preserved:`);
        for (const view of plan.unmanagedProviderViews) {
            context.logger.warn(`  - ${view.provider}: ${view.relativePath}`);
        }
    }
}
export async function runRemoveSkill(context, skillName, dryRun, dependencies) {
    const scopes = resolveConcreteScopes(context.scope);
    const plans = [];
    for (const scope of scopes) {
        const scopeRoot = await dependencies.resolveScopeRoot(scope, context);
        const plan = await buildScopePlan(scope, scopeRoot, skillName, dependencies);
        if (plan) {
            plans.push(plan);
        }
    }
    if (plans.length === 0) {
        const message = `Skill not found in selected scope(s): ${skillName}`;
        if (context.json) {
            context.logger.json({ status: 'not_found', skill: skillName });
        }
        else {
            context.logger.warn(message);
        }
        return false;
    }
    if (dryRun) {
        for (const plan of plans) {
            logDryRun(context, plan);
        }
        if (context.json) {
            context.logger.json({
                status: 'dry_run',
                skill: skillName,
                scopes: toJsonScopeResults(plans),
            });
        }
        return true;
    }
    for (const plan of plans) {
        await applyPlan(context, plan, dependencies);
    }
    if (context.json) {
        context.logger.json({
            status: 'removed',
            skill: skillName,
            scopes: toJsonScopeResults(plans),
        });
    }
    return true;
}
export function createRemoveSkillCommand(overrides = {}) {
    const dependencies = {
        ...createDefaultRemoveSkillDependencies(),
        ...overrides,
    };
    return withScopeOption(new Command('skill'))
        .description('Remove a single installed skill by name')
        .argument('<name>', 'Skill name (e.g., oat-idea-scratchpad)')
        .option('--dry-run', 'Preview removal without applying')
        .action(async (skillName, options, command) => {
        const context = dependencies.buildCommandContext(readGlobalOptions(command));
        try {
            const removed = await runRemoveSkill(context, skillName, options.dryRun ?? false, dependencies);
            process.exitCode = removed ? 0 : 1;
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            if (context.json) {
                context.logger.json({ status: 'error', message });
            }
            else {
                context.logger.error(message);
            }
            process.exitCode = 2;
        }
    });
}
