/**
 * Skills detector — names of skill directories materialized in the version
 * home that match the central source content. Mirrors versions.ts:359-389.
 */
import * as fs from 'fs';
import * as path from 'path';
import { AGENTS, agentConfigDirName } from '../../agents.js';
import { capableAgents } from '../../capabilities.js';
import { resolveSkillSource } from '../writers/sources.js';
import { lazyAgentMap } from '../writers/lazy-map.js';
const SKILL_COPY_IGNORE = new Set(['.DS_Store', '.git', '.gitignore', '.venv', '__pycache__', 'node_modules']);
/** Exported for unit tests (RUSH-2320 #2). Production callers: list() below. */
export function skillDirsMatch(src, dest) {
    const entries = fs.readdirSync(src, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.isSymbolicLink())
            continue;
        if (SKILL_COPY_IGNORE.has(entry.name))
            continue;
        const srcPath = path.join(src, entry.name);
        const destPath = path.join(dest, entry.name);
        if (entry.isDirectory()) {
            if (!fs.existsSync(destPath))
                return false;
            if (!skillDirsMatch(srcPath, destPath))
                return false;
        }
        else {
            // Stat-first (RUSH-2320 #2): size mismatch is a definitive miss with no
            // content reads. Do NOT treat equal mtimes as equal content — src and
            // dest are different trees (version home vs source), and copyFileSync
            // does not preserve mtime, so mtime equality is accidental and unsafe.
            // Content compare only when sizes match.
            let srcStat;
            let destStat;
            try {
                srcStat = fs.statSync(srcPath);
                destStat = fs.statSync(destPath);
            }
            catch {
                return false;
            }
            if (srcStat.size !== destStat.size)
                return false;
            if (fs.readFileSync(srcPath, 'utf-8') !== fs.readFileSync(destPath, 'utf-8'))
                return false;
        }
    }
    return true;
}
function buildSkillsDetector(agent) {
    return {
        kind: 'skills',
        agent,
        list({ versionHome }) {
            const skillsDir = path.join(versionHome, agentConfigDirName(agent), 'skills');
            if (!fs.existsSync(skillsDir))
                return [];
            const installed = fs.readdirSync(skillsDir, { withFileTypes: true })
                .filter(d => d.isDirectory() && !d.name.startsWith('.'))
                .map(d => d.name);
            const synced = [];
            for (const name of installed) {
                const src = resolveSkillSource(name);
                if (!src) {
                    // True orphan — no source. Still count so cleanup knows it's accounted for.
                    synced.push(name);
                    continue;
                }
                if (skillDirsMatch(src, path.join(skillsDir, name))) {
                    synced.push(name);
                }
            }
            return synced;
        },
    };
}
export const skillsDetectors = lazyAgentMap(() => {
    const m = {};
    for (const agent of capableAgents('skills')) {
        if (AGENTS[agent].nativeAgentsSkillsDir)
            continue;
        m[agent] = buildSkillsDetector(agent);
    }
    return m;
});
