// engine/detectors/library-detector-langs3b.ts — Systems lang library detection (part B)
// SPEC-004 tech-debt S4: Gleam, Crystal, Nim, Zig
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { glob } from 'glob';
// === Catalog constants ===
const GLEAM_WEB_LIBS = ['gleam_http', 'mist', 'wisp'];
const GLEAM_TESTING_LIBS = ['gleeunit', 'gleam_stdlib'];
const GLEAM_JSON_LIBS = ['gleam_json'];
const CRYSTAL_WEB_LIBS = ['kemal', 'lucky', 'amber'];
const CRYSTAL_DB_LIBS = ['db', 'crystal-pg', 'cql'];
const CRYSTAL_TESTING_LIBS = ['mocks'];
const NIM_WEB_LIBS = ['jester', 'prologue', 'httpbeast'];
const NIM_DB_LIBS = ['norm', 'ndb'];
const NIM_TESTING_LIBS = ['unittest'];
const ZIG_HTTP_LIBS = ['httpz', 'zap'];
const ZIG_TESTING_LIBS = ['zig-test'];
// === Helpers ===
function filterPresent(catalog, deps) {
    return catalog.filter((lib) => deps.has(lib));
}
// === Gleam parsers ===
/**
 * Parse gleam.toml [dependencies] section.
 * Matches `name = "version"` lines.
 */
export function parseGleamToml(content) {
    const deps = new Set();
    const sectionMatch = /\[dependencies\]([\s\S]*?)(?:\n\[|$)/s.exec(content);
    if (sectionMatch?.[1]) {
        const matches = sectionMatch[1].matchAll(/^([a-zA-Z0-9_-]+)\s*=/gm);
        for (const match of matches) {
            /* v8 ignore start */
            if (match[1]) {
                /* v8 ignore stop */
                deps.add(match[1]);
            }
        }
    }
    return deps;
}
async function collectGleamDeps(projectPath) {
    try {
        const content = await readFile(join(projectPath, 'gleam.toml'), 'utf-8');
        return parseGleamToml(content);
    }
    catch {
        return new Set();
    }
}
// === Crystal parsers ===
/**
 * Parse shard.yml dependencies section.
 * Matches top-level package names under `dependencies:` key.
 */
export function parseShardYml(content) {
    const deps = new Set();
    const sectionMatch = /^dependencies:\s*\n((?:[ \t]+[^\n]*\n?)*)/m.exec(content);
    if (sectionMatch?.[1]) {
        const lines = sectionMatch[1].split('\n');
        for (const line of lines) {
            // Shard names are indented with exactly 2 spaces and end with colon
            const nameMatch = /^ {2}([a-zA-Z0-9_-]+)\s*:/.exec(line);
            if (nameMatch?.[1]) {
                deps.add(nameMatch[1]);
            }
        }
    }
    return deps;
}
async function collectCrystalDeps(projectPath) {
    try {
        const content = await readFile(join(projectPath, 'shard.yml'), 'utf-8');
        return parseShardYml(content);
    }
    catch {
        return new Set();
    }
}
// === Nim parsers ===
/**
 * Parse *.nimble for `requires "package >= version"` lines.
 * Extracts the bare package name.
 */
export function parseNimble(content) {
    const deps = new Set();
    const matches = content.matchAll(/requires\s+"([a-zA-Z0-9_-]+)/g);
    for (const match of matches) {
        /* v8 ignore start */
        if (match[1]) {
            /* v8 ignore stop */
            deps.add(match[1]);
        }
    }
    return deps;
}
async function collectNimDeps(projectPath) {
    try {
        const files = await glob('**/*.nimble', {
            cwd: projectPath,
            absolute: false,
            ignore: ['node_modules/**'],
        });
        const sets = await Promise.all(files.map((f) => readFile(join(projectPath, f), 'utf-8')
            .then(parseNimble)
            .catch(() => new Set())));
        const merged = new Set();
        for (const s of sets) {
            for (const v of s) {
                merged.add(v);
            }
        }
        return merged;
    }
    catch {
        return new Set();
    }
}
// === Zig parsers ===
/**
 * Parse build.zig.zon for dependency names.
 * Dependencies are direct children of `.dependencies = .{ .depname = .{ ... } }`.
 * Uses brace-counting to extract the dependencies block content.
 */
export function parseBuildZigZon(content) {
    const deps = new Set();
    const depsIdx = content.indexOf('.dependencies');
    if (depsIdx === -1) {
        return deps;
    }
    const braceStart = content.indexOf('{', depsIdx);
    if (braceStart === -1) {
        return deps;
    }
    // Count braces to find the end of the .dependencies block
    let depth = 1;
    let pos = braceStart + 1;
    while (pos < content.length && depth > 0) {
        if (content[pos] === '{') {
            depth++;
        }
        else if (content[pos] === '}') {
            depth--;
        }
        pos++;
    }
    const block = content.slice(braceStart + 1, pos - 1);
    // Match dotted field names that open nested structs (.depname = .{)
    // Skip internal sub-fields: url, hash, path, lazy
    const SKIP = new Set(['url', 'hash', 'path', 'lazy', 'version']);
    const matches = block.matchAll(/\.([a-zA-Z0-9_-]+)\s*=\s*\.?\{/g);
    for (const match of matches) {
        /* v8 ignore start */
        if (match[1] && !SKIP.has(match[1])) {
            /* v8 ignore stop */
            deps.add(match[1]);
        }
    }
    return deps;
}
async function collectZigDeps(projectPath) {
    try {
        const content = await readFile(join(projectPath, 'build.zig.zon'), 'utf-8');
        return parseBuildZigZon(content);
    }
    catch {
        return new Set();
    }
}
// === Public API ===
/** Detect Gleam library dependencies from gleam.toml. */
export async function detectGleamLibraries(projectPath) {
    const deps = await collectGleamDeps(projectPath);
    return {
        web: filterPresent(GLEAM_WEB_LIBS, deps),
        testing: filterPresent(GLEAM_TESTING_LIBS, deps),
        json: filterPresent(GLEAM_JSON_LIBS, deps),
    };
}
/** Detect Crystal shard dependencies from shard.yml. */
export async function detectCrystalLibraries(projectPath) {
    const deps = await collectCrystalDeps(projectPath);
    return {
        web: filterPresent(CRYSTAL_WEB_LIBS, deps),
        db: filterPresent(CRYSTAL_DB_LIBS, deps),
        testing: filterPresent(CRYSTAL_TESTING_LIBS, deps),
    };
}
/** Detect Nim library dependencies from *.nimble files. */
export async function detectNimLibraries(projectPath) {
    const deps = await collectNimDeps(projectPath);
    return {
        web: filterPresent(NIM_WEB_LIBS, deps),
        db: filterPresent(NIM_DB_LIBS, deps),
        testing: filterPresent(NIM_TESTING_LIBS, deps),
    };
}
/** Detect Zig dependencies from build.zig.zon. */
export async function detectZigLibraries(projectPath) {
    const deps = await collectZigDeps(projectPath);
    return {
        http: filterPresent(ZIG_HTTP_LIBS, deps),
        testing: filterPresent(ZIG_TESTING_LIBS, deps),
    };
}
//# sourceMappingURL=library-detector-langs3b.js.map