import fs from 'node:fs/promises';
import path from 'node:path';
async function ensureDir(dir) {
    await fs.mkdir(dir, { recursive: true });
}
async function pathExists(target) {
    try {
        await fs.access(target);
        return true;
    }
    catch (error) {
        if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
            return false;
        }
        return false;
    }
}
async function removeIfExists(target) {
    await fs.rm(target, { recursive: true, force: true });
}
async function copyDir(source, target) {
    await ensureDir(target);
    const entries = await fs.readdir(source, { withFileTypes: true });
    for (const entry of entries) {
        const srcPath = path.join(source, entry.name);
        const tgtPath = path.join(target, entry.name);
        if (entry.isDirectory()) {
            await copyDir(srcPath, tgtPath);
            continue;
        }
        if (entry.isSymbolicLink()) {
            const linkTarget = await fs.readlink(srcPath);
            await fs.symlink(linkTarget, tgtPath, 'junction');
            continue;
        }
        if (entry.isFile()) {
            await fs.copyFile(srcPath, tgtPath);
        }
    }
}
function parseYamlStringValue(raw) {
    const trimmed = raw.trim();
    if (!trimmed) {
        return undefined;
    }
    if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
        (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
        return trimmed.slice(1, -1).trim();
    }
    return trimmed;
}
function fallbackNameFromDirectory(directory) {
    return path.basename(directory).replace(/\.skill$/, '').replace(/[-_]/g, ' ');
}
async function parseSkillMetadata(directory) {
    const skillFile = path.join(directory, 'SKILL.md');
    if (!(await pathExists(skillFile))) {
        return null;
    }
    const raw = (await fs.readFile(skillFile, 'utf-8')).replace(/^\uFEFF/, '');
    const metadata = {};
    if (raw.startsWith('---')) {
        const end = raw.indexOf('\n---', 3);
        if (end >= 0) {
            const frontMatter = raw.slice(3, end).split(/\r?\n/);
            for (const line of frontMatter) {
                const match = line.match(/^\s*(name|description)\s*:\s*(.*)$/);
                if (!match) {
                    continue;
                }
                const value = parseYamlStringValue(match[2]);
                if (value) {
                    metadata[match[1]] = value;
                }
            }
        }
    }
    return {
        name: metadata.name || fallbackNameFromDirectory(directory),
        description: metadata.description,
    };
}
async function materializeSkillDir(sourceDir, targetDir, method) {
    await removeIfExists(targetDir);
    await ensureDir(path.dirname(targetDir));
    if (method === 'copy') {
        await copyDir(sourceDir, targetDir);
        return;
    }
    if (method === 'symlink') {
        await fs.symlink(sourceDir, targetDir, 'junction');
        return;
    }
    try {
        await fs.symlink(sourceDir, targetDir, 'junction');
    }
    catch {
        await copyDir(sourceDir, targetDir);
    }
}
export class DirectorySkillAdapter {
    constructor(appId, skillsDir) {
        this.appId = appId;
        this.skillsDir = skillsDir;
    }
    async importSkills() {
        await ensureDir(this.skillsDir);
        const entries = await fs.readdir(this.skillsDir, { withFileTypes: true });
        const skills = [];
        for (const entry of entries) {
            if (entry.name.startsWith('.') ||
                (!entry.isDirectory() && !entry.isSymbolicLink())) {
                continue;
            }
            const directory = path.join(this.skillsDir, entry.name);
            const metadata = await parseSkillMetadata(directory);
            if (!metadata) {
                continue;
            }
            skills.push({
                id: `skill_import_${this.appId}_${entry.name}`,
                name: metadata.name,
                directory,
                description: metadata.description,
            });
        }
        return skills;
    }
    async syncSkill(skill, sourceDir, method) {
        const targetDir = path.join(this.skillsDir, skill.name);
        await materializeSkillDir(sourceDir, targetDir, method);
    }
    async removeSkill(skill) {
        const targetDir = path.join(this.skillsDir, skill.name);
        await removeIfExists(targetDir);
    }
}
