import path from 'path';
import * as fs from 'fs';
import { AI_TOOLS } from './config.js';
import { ALL_WORKFLOWS } from './profiles.js';
import { CommandAdapterRegistry } from './command-generation/index.js';
import { getConfiguredTools } from './shared/index.js';
import { shouldGenerateCommandsForTool, shouldGenerateSkillsForTool, shouldReconcileCommandFilesForTool, shouldRemoveSkillsForTool, } from './command-surface.js';
import { readSharedSkillTarget } from './shared-skill-target.js';
import { FileSystemUtils } from '../utils/file-system.js';
import { isLegacyCodexSkillEquivalentToCurrent } from './shared/skill-content-equivalence.js';
import { hasGlobalSkillTarget, resolveToolSkillsDir, toolSupportsSkills, } from './shared/skill-paths.js';
/**
 * Maps workflow IDs to their skill directory names.
 */
export const WORKFLOW_TO_SKILL_DIR = {
    'explore': 'openspec-explore',
    'new': 'openspec-new-change',
    'continue': 'openspec-continue-change',
    'apply': 'openspec-apply-change',
    'update': 'openspec-update-change',
    'ff': 'openspec-ff-change',
    'sync': 'openspec-sync-specs',
    'archive': 'openspec-archive-change',
    'bulk-archive': 'openspec-bulk-archive-change',
    'verify': 'openspec-verify-change',
    'onboard': 'openspec-onboard',
    'propose': 'openspec-propose',
};
function toKnownWorkflows(workflows) {
    return workflows.filter((workflow) => ALL_WORKFLOWS.includes(workflow));
}
/**
 * Returns tools that are configured via either skills or commands.
 */
export function getConfiguredToolsForProfileSync(projectPath) {
    return getConfiguredTools(projectPath);
}
/**
 * Detects if a single tool has profile/delivery drift against the desired state.
 *
 * This function covers:
 * - required artifacts missing for selected workflows
 * - artifacts that should not exist for the selected delivery mode
 * - artifacts for workflows that were deselected from the current profile
 */
export function hasToolProfileOrDeliveryDrift(projectPath, toolId, desiredWorkflows, delivery) {
    const tool = AI_TOOLS.find((t) => t.value === toolId);
    if (!tool || !toolSupportsSkills(tool))
        return false;
    const knownDesiredWorkflows = toKnownWorkflows(desiredWorkflows);
    const desiredWorkflowSet = new Set(knownDesiredWorkflows);
    const skillsDir = resolveToolSkillsDir(projectPath, tool);
    const adapter = CommandAdapterRegistry.get(toolId);
    const shouldGenerateSkills = shouldGenerateSkillsForTool(toolId, delivery);
    const shouldGenerateCommands = shouldGenerateCommandsForTool(toolId, delivery);
    const sharedTarget = tool.skillsDir
        ? readSharedSkillTarget(projectPath, tool.skillsDir)
        : undefined;
    for (const root of tool.legacySkillsDirs ?? []) {
        for (const workflow of knownDesiredWorkflows) {
            const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
            const legacySkill = path.join(projectPath, root, 'skills', dirName, 'SKILL.md');
            if (!fs.existsSync(legacySkill))
                continue;
            const currentSkill = path.join(skillsDir, dirName, 'SKILL.md');
            if (!fs.existsSync(currentSkill) || sharedTarget !== toolId) {
                return true;
            }
            try {
                if (FileSystemUtils.canonicalizeExistingPath(legacySkill) ===
                    FileSystemUtils.canonicalizeExistingPath(currentSkill)) {
                    continue;
                }
                // Equivalent generated copies are actionable: migration can safely
                // remove the redundant legacy file even when version, line endings,
                // or supported invocation syntax changed. Materially divergent copies
                // stay in place without forcing an update on every run.
                if (isLegacyCodexSkillEquivalentToCurrent(fs.readFileSync(legacySkill, 'utf-8'), fs.readFileSync(currentSkill, 'utf-8'))) {
                    return true;
                }
            }
            catch {
                return true;
            }
        }
    }
    if (shouldGenerateSkills) {
        for (const workflow of knownDesiredWorkflows) {
            const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
            const skillFile = path.join(skillsDir, dirName, 'SKILL.md');
            if (!fs.existsSync(skillFile)) {
                return true;
            }
        }
        // Deselecting workflows in a profile should trigger sync.
        for (const workflow of ALL_WORKFLOWS) {
            if (desiredWorkflowSet.has(workflow))
                continue;
            const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
            const skillDir = path.join(skillsDir, dirName);
            if (fs.existsSync(skillDir)) {
                return true;
            }
        }
    }
    else if (shouldRemoveSkillsForTool(toolId, delivery) && !hasGlobalSkillTarget(tool)) {
        for (const workflow of ALL_WORKFLOWS) {
            const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
            const skillDir = path.join(skillsDir, dirName);
            if (fs.existsSync(skillDir)) {
                return true;
            }
        }
    }
    if (shouldGenerateCommands && adapter) {
        for (const workflow of knownDesiredWorkflows) {
            const cmdPath = adapter.getFilePath(workflow);
            const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
            if (!fs.existsSync(fullPath)) {
                return true;
            }
        }
        // Deselecting workflows in a profile should trigger sync.
        for (const workflow of ALL_WORKFLOWS) {
            if (desiredWorkflowSet.has(workflow))
                continue;
            const cmdPath = adapter.getFilePath(workflow);
            const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
            if (fs.existsSync(fullPath)) {
                return true;
            }
        }
    }
    else if (shouldReconcileCommandFilesForTool(toolId, delivery) && adapter) {
        for (const workflow of ALL_WORKFLOWS) {
            const cmdPath = adapter.getFilePath(workflow);
            const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
            if (fs.existsSync(fullPath)) {
                return true;
            }
        }
    }
    return false;
}
/**
 * Returns configured tools that currently need a profile/delivery sync.
 */
export function getToolsNeedingProfileSync(projectPath, desiredWorkflows, delivery, configuredTools) {
    const tools = configuredTools ? [...new Set(configuredTools)] : getConfiguredToolsForProfileSync(projectPath);
    return tools.filter((toolId) => hasToolProfileOrDeliveryDrift(projectPath, toolId, desiredWorkflows, delivery));
}
function getInstalledWorkflowsForTool(projectPath, toolId, options) {
    const tool = AI_TOOLS.find((t) => t.value === toolId);
    if (!tool || !toolSupportsSkills(tool))
        return [];
    const installed = new Set();
    const skillsDir = resolveToolSkillsDir(projectPath, tool);
    if (options.includeSkills) {
        for (const workflow of ALL_WORKFLOWS) {
            const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
            const skillFile = path.join(skillsDir, dirName, 'SKILL.md');
            if (fs.existsSync(skillFile)) {
                installed.add(workflow);
            }
        }
    }
    if (options.includeCommands) {
        const adapter = CommandAdapterRegistry.get(toolId);
        if (adapter) {
            for (const workflow of ALL_WORKFLOWS) {
                const cmdPath = adapter.getFilePath(workflow);
                const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
                if (fs.existsSync(fullPath)) {
                    installed.add(workflow);
                }
            }
        }
    }
    return [...installed];
}
/**
 * Detects whether the current project has any profile/delivery drift.
 */
export function hasProjectConfigDrift(projectPath, desiredWorkflows, delivery) {
    const configuredTools = getConfiguredToolsForProfileSync(projectPath);
    if (getToolsNeedingProfileSync(projectPath, desiredWorkflows, delivery, configuredTools).length > 0) {
        return true;
    }
    const desiredSet = new Set(toKnownWorkflows(desiredWorkflows));
    for (const toolId of configuredTools) {
        const includeSkills = shouldGenerateSkillsForTool(toolId, delivery);
        const includeCommands = shouldGenerateCommandsForTool(toolId, delivery);
        const installed = getInstalledWorkflowsForTool(projectPath, toolId, { includeSkills, includeCommands });
        if (installed.some((workflow) => !desiredSet.has(workflow))) {
            return true;
        }
    }
    return false;
}
//# sourceMappingURL=profile-sync-drift.js.map