/**
 * Parse installed version from `npm list -g <pkg> --depth=0 --json` output.
 * npm may return the queried package as the JSON root (name + version) or nested under dependencies.
 */
import { spawnNpmSync } from './npm-spawn.js';
import { sanitizePackageName } from './security/validation.js';
function stdoutToString(stdout) {
    return typeof stdout === 'string' ? stdout : stdout.toString('utf-8');
}
/** Deep scan (npm global tree shapes vary by npm major). */
function deepFindPackageVersion(node, targetName) {
    if (node === null || typeof node !== 'object') {
        return null;
    }
    if (Array.isArray(node)) {
        for (const el of node) {
            const f = deepFindPackageVersion(el, targetName);
            if (f) {
                return f;
            }
        }
        return null;
    }
    const o = node;
    if (o.name === targetName && typeof o.version === 'string') {
        return o.version;
    }
    for (const v of Object.values(o)) {
        const f = deepFindPackageVersion(v, targetName);
        if (f) {
            return f;
        }
    }
    return null;
}
export function parseNpmListGlobalVersion(stdout, packageName) {
    const validation = sanitizePackageName(packageName);
    if (!validation.valid || !validation.sanitized) {
        return null;
    }
    const n = validation.sanitized;
    let data;
    try {
        data = JSON.parse(stdoutToString(stdout));
    }
    catch {
        return null;
    }
    if (typeof data !== 'object' || data === null) {
        return null;
    }
    const o = data;
    if (o.name === n && typeof o.version === 'string') {
        return o.version;
    }
    const deps = o.dependencies;
    if (deps && typeof deps === 'object' && deps !== null) {
        const d = deps;
        const direct = d[n];
        if (direct && typeof direct === 'object' && direct !== null) {
            const v = direct.version;
            if (typeof v === 'string') {
                return v;
            }
        }
        for (const [key, val] of Object.entries(d)) {
            if (key === n || key.startsWith(`${n}@`)) {
                if (val && typeof val === 'object' && val !== null) {
                    const v = val.version;
                    if (typeof v === 'string') {
                        return v;
                    }
                }
            }
        }
    }
    return deepFindPackageVersion(data, n);
}
/** Looks like a normal x.y.z (or x.y) semver string from npm. */
export function looksLikeNpmVersion(v) {
    return /^\d+\.\d+(\.\d+)?/.test(v.trim());
}
/**
 * Read globally installed version from `npm list -g` JSON.
 * Important: `npm list` often exits with code 1 (extraneous/missing peers) even when JSON is valid —
 * do not treat non-zero status as "no install".
 */
export function getInstalledGlobalPackageVersion(packageName) {
    const validation = sanitizePackageName(packageName);
    if (!validation.valid || !validation.sanitized) {
        return null;
    }
    const result = spawnNpmSync(['list', '-g', validation.sanitized, '--depth=0', '--json']);
    if (!result.stdout) {
        return null;
    }
    const v = parseNpmListGlobalVersion(result.stdout, packageName);
    return v && looksLikeNpmVersion(v) ? v : null;
}
//# sourceMappingURL=npm-list-version.js.map