// engine/config-health/ts-checker-scripts.ts — SPEC-042
// Checks for package.json scripts and husky hooks referencing missing tools/scripts.
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { glob } from 'glob';
async function readFileSafe(filePath) {
    try {
        return await readFile(filePath, 'utf-8');
    }
    catch {
        return '';
    }
}
async function fileExists(filePath) {
    const content = await readFileSafe(filePath);
    return content !== '';
}
// ---------------------------------------------------------------------------
// Check: package.json scripts referencing non-installed commands
// ---------------------------------------------------------------------------
/**
 * Detect package.json scripts that reference commands that are not installed
 * in node_modules/.bin (e.g., "test": "vitest run" when vitest is not in deps).
 */
export async function checkPackageScripts(projectPath) {
    const findings = [];
    const pkgContent = await readFileSafe(join(projectPath, 'package.json'));
    if (!pkgContent) {
        return findings;
    }
    let pkg;
    try {
        pkg = JSON.parse(pkgContent);
    }
    catch {
        findings.push({
            severity: 'error',
            checker: 'package-scripts',
            file: 'package.json',
            message: 'package.json is not valid JSON',
            fix: 'Fix the JSON syntax in package.json',
        });
        return findings;
    }
    /* v8 ignore start -- CLI binary detection has many ecosystem-specific branches */
    const scripts = pkg.scripts ?? {};
    const allDeps = {
        ...pkg.dependencies,
        ...pkg.devDependencies,
    };
    // Commands to check — map CLI binary name to package name
    const CLI_TO_PACKAGE = {
        vitest: 'vitest',
        jest: 'jest',
        mocha: 'mocha',
        eslint: 'eslint',
        prettier: 'prettier',
        tsc: 'typescript',
        ts_node: 'ts-node',
        'ts-node': 'ts-node',
        tsx: 'tsx',
        webpack: 'webpack',
        vite: 'vite',
        rollup: 'rollup',
        esbuild: 'esbuild',
    };
    for (const [scriptName, scriptCmd] of Object.entries(scripts)) {
        // Extract the first word (the command binary)
        const firstWord = scriptCmd.trim().split(/\s+/)[0] ?? '';
        const pkg_name = CLI_TO_PACKAGE[firstWord];
        if (pkg_name && !(pkg_name in allDeps)) {
            // Also check if the binary exists in node_modules/.bin
            const binPath = join(projectPath, 'node_modules', '.bin', firstWord);
            const binExists = await fileExists(binPath);
            if (!binExists) {
                findings.push({
                    severity: 'error',
                    checker: 'package-scripts',
                    file: 'package.json',
                    message: `Script '${scriptName}' uses '${firstWord}' but '${pkg_name}' is not in dependencies or node_modules/.bin`,
                    fix: `Install the missing package: pnpm add -D ${pkg_name}`,
                });
            }
        }
    }
    /* v8 ignore stop */
    return findings;
}
// ---------------------------------------------------------------------------
// Check: Husky hooks referencing missing scripts
// ---------------------------------------------------------------------------
/**
 * Detect husky hooks (.husky/pre-commit, etc.) that reference scripts or
 * files that don't exist in the project.
 */
export async function checkHuskyScripts(projectPath) {
    const findings = [];
    const huskyDir = join(projectPath, '.husky');
    let hookFiles;
    try {
        hookFiles = await glob('*', {
            cwd: huskyDir,
            nodir: true,
        });
    }
    catch {
        return findings; // No .husky directory
    }
    for (const hookFile of hookFiles) {
        const hookPath = join(huskyDir, hookFile);
        const content = await readFileSafe(hookPath);
        if (!content) {
            continue;
        }
        // Extract script references: lines that run shell scripts
        const lines = content.split('\n');
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i]?.trim() ?? '';
            if (!line || line.startsWith('#')) {
                continue;
            }
            // Check for references like: bash scripts/check.sh or ./scripts/check.sh
            const scriptMatch = /(?:bash|sh)\s+([\w./-]+\.sh)/.exec(line);
            if (scriptMatch) {
                const scriptRef = scriptMatch[1] ?? '';
                const scriptPath = join(projectPath, scriptRef);
                const exists = await fileExists(scriptPath);
                if (!exists) {
                    findings.push({
                        severity: 'error',
                        checker: 'husky-scripts',
                        file: `.husky/${hookFile}`,
                        line: i + 1,
                        message: `Husky hook references script '${scriptRef}' that does not exist`,
                        fix: `Create the script at '${scriptRef}' or remove the reference from .husky/${hookFile}`,
                    });
                }
            }
        }
    }
    return findings;
}
//# sourceMappingURL=ts-checker-scripts.js.map