// engine/plugins/compatibility.ts — Semver range matching (SPEC-085 AC-13)
/**
 * Parses a semver string like "1.2.3" into numeric components.
 * Returns null if invalid.
 */
export function parseSemver(version) {
    const clean = version.replace(/^v/, '');
    const parts = clean.split('.');
    if (parts.length !== 3) {
        return null;
    }
    const major = Number(parts[0]);
    const minor = Number(parts[1]);
    const patch = Number(parts[2]);
    if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
        return null;
    }
    if (major < 0 || minor < 0 || patch < 0) {
        return null;
    }
    return { major, minor, patch };
}
/**
 * Compares two parsed versions.
 * Returns -1 if a < b, 0 if a == b, 1 if a > b.
 */
function compareVersions(a, b) {
    if (a.major !== b.major) {
        return a.major < b.major ? -1 : 1;
    }
    if (a.minor !== b.minor) {
        return a.minor < b.minor ? -1 : 1;
    }
    if (a.patch !== b.patch) {
        return a.patch < b.patch ? -1 : 1;
    }
    return 0;
}
/** Parses the version from a prefixed condition and compares it. */
function parseAndCompare(versionStr, current) {
    const target = parseSemver(versionStr);
    if (!target) {
        return null;
    }
    return { target, cmp: compareVersions(current, target) };
}
/** Evaluates caret (^) condition: compatible with major version. */
function evalCaret(versionStr, current) {
    const result = parseAndCompare(versionStr, current);
    if (!result) {
        return false;
    }
    return current.major === result.target.major && result.cmp >= 0;
}
/** Evaluates tilde (~) condition: compatible with minor version. */
function evalTilde(versionStr, current) {
    const result = parseAndCompare(versionStr, current);
    if (!result) {
        return false;
    }
    return (current.major === result.target.major &&
        current.minor === result.target.minor &&
        result.cmp >= 0);
}
/** Evaluates a comparison operator condition. */
function evalComparison(operator, versionStr, current) {
    const result = parseAndCompare(versionStr, current);
    if (!result) {
        return false;
    }
    switch (operator) {
        case '>=':
            return result.cmp >= 0;
        case '>':
            return result.cmp > 0;
        case '<=':
            return result.cmp <= 0;
        case '<':
            return result.cmp < 0;
        default:
            return result.cmp === 0;
    }
}
/**
 * Evaluates a single semver condition like ">=1.2.0", "^1.2.0", "~1.2.0", "<2.0.0".
 */
export function evaluateCondition(condition, current) {
    const trimmed = condition.trim();
    if (!trimmed) {
        return true;
    }
    if (trimmed.startsWith('^')) {
        return evalCaret(trimmed.slice(1), current);
    }
    if (trimmed.startsWith('~')) {
        return evalTilde(trimmed.slice(1), current);
    }
    if (trimmed.startsWith('>=')) {
        return evalComparison('>=', trimmed.slice(2), current);
    }
    if (trimmed.startsWith('>')) {
        return evalComparison('>', trimmed.slice(1), current);
    }
    if (trimmed.startsWith('<=')) {
        return evalComparison('<=', trimmed.slice(2), current);
    }
    if (trimmed.startsWith('<')) {
        return evalComparison('<', trimmed.slice(1), current);
    }
    const exactStr = trimmed.startsWith('=') ? trimmed.slice(1) : trimmed;
    return evalComparison('=', exactStr, current);
}
/**
 * Checks if a version satisfies a semver range string.
 * Supports: ^, ~, >=, >, <=, <, exact match, space-separated AND, || for OR.
 */
export function satisfiesRange(version, range) {
    const current = parseSemver(version);
    if (!current) {
        return false;
    }
    const orGroups = range.split('||').map((g) => g.trim());
    return orGroups.some((group) => {
        const conditions = group.split(/\s+/).filter(Boolean);
        return conditions.every((cond) => evaluateCondition(cond, current));
    });
}
/**
 * Checks if a plugin is compatible with the given Planu version.
 */
export function isPluginCompatible(planuVersionRange, currentVersion) {
    return satisfiesRange(currentVersion, planuVersionRange);
}
/**
 * Filters plugin registrations to only those compatible with the given version.
 */
export function getCompatiblePlugins(plugins, currentVersion) {
    return plugins.filter((p) => isPluginCompatible(p.manifest.planuVersion, currentVersion));
}
//# sourceMappingURL=compatibility.js.map