// engine/plugins/validator.ts — Plugin manifest validation (SPEC-085 AC-11)
import { parseSemver, isPluginCompatible } from './compatibility.js';
const VALID_PERMISSIONS = new Set([
    'tools:register',
    'workers:register',
    'analyzers:register',
    'templates:register',
    'storage:read',
    'storage:write',
]);
const REQUIRED_STRING_FIELDS = [
    'id',
    'name',
    'version',
    'author',
    'description',
    'license',
    'planuVersion',
    'entryPoint',
];
const PLUGIN_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
/** Validates required string fields are present and non-empty. */
function validateRequiredFields(m, errors) {
    for (const field of REQUIRED_STRING_FIELDS) {
        const val = m[field];
        if (typeof val !== 'string' || !val.trim()) {
            errors.push(`Missing or empty required field: ${field}`);
        }
    }
}
/** Validates field formats: id pattern, semver version, entryPoint safety, planuVersion range. */
function validateFieldFormats(m, errors) {
    if (typeof m.id === 'string' && !PLUGIN_ID_PATTERN.test(m.id)) {
        errors.push(`Invalid plugin id "${m.id}": must be lowercase alphanumeric with hyphens, starting with a letter`);
    }
    if (typeof m.version === 'string' && !parseSemver(m.version)) {
        errors.push(`Invalid version "${m.version}": must be valid semver (e.g., 1.0.0)`);
    }
    if (typeof m.entryPoint === 'string') {
        if (m.entryPoint.includes('..') || m.entryPoint.startsWith('/')) {
            errors.push('entryPoint must be a relative path within the plugin directory');
        }
    }
    if (typeof m.planuVersion === 'string') {
        const hasVersion = /\d+\.\d+\.\d+/.test(m.planuVersion);
        if (!hasVersion) {
            errors.push(`Invalid planuVersion range "${m.planuVersion}": must contain at least one semver version`);
        }
    }
}
/** Validates the permissions array. */
function validatePermissions(m, errors, warnings) {
    if (!Array.isArray(m.permissions)) {
        errors.push('permissions must be an array');
        return;
    }
    for (const perm of m.permissions) {
        if (typeof perm !== 'string' || !VALID_PERMISSIONS.has(perm)) {
            errors.push(`Invalid permission: "${String(perm)}"`);
        }
    }
    if (m.permissions.length === 0) {
        warnings.push('Plugin declares no permissions — it will have no capabilities');
    }
}
/**
 * Validates that a value looks like a PluginManifest structurally.
 */
export function validateManifestStructure(manifest) {
    const errors = [];
    const warnings = [];
    if (!manifest || typeof manifest !== 'object') {
        return { valid: false, errors: ['Manifest must be a JSON object'], warnings };
    }
    const m = manifest;
    validateRequiredFields(m, errors);
    validateFieldFormats(m, errors);
    validatePermissions(m, errors, warnings);
    return { valid: errors.length === 0, errors, warnings };
}
/**
 * Validates a manifest against built-in tool names and other active plugins.
 */
export function validateToolConflicts(manifest, pluginTools, builtInTools, activePluginTools) {
    const errors = [];
    const warnings = [];
    for (const toolName of pluginTools) {
        const prefixedName = `plugin:${manifest.id}:${toolName}`;
        if (builtInTools.has(toolName) || builtInTools.has(prefixedName)) {
            errors.push(`Tool "${toolName}" conflicts with built-in tool`);
        }
        const existingPlugin = activePluginTools.get(prefixedName);
        if (existingPlugin && existingPlugin !== manifest.id) {
            errors.push(`Tool "${toolName}" conflicts with plugin "${existingPlugin}"`);
        }
    }
    return { valid: errors.length === 0, errors, warnings };
}
/**
 * Validates version compatibility of a plugin against current Planu version.
 */
export function validateVersionCompatibility(manifest, currentVersion) {
    const errors = [];
    const warnings = [];
    if (!isPluginCompatible(manifest.planuVersion, currentVersion)) {
        errors.push(`Plugin "${manifest.name}" requires Planu ${manifest.planuVersion}, current version is ${currentVersion}`);
    }
    return { valid: errors.length === 0, errors, warnings };
}
/**
 * Runs full validation suite on a manifest.
 */
export function validatePlugin(manifest, currentVersion, builtInTools, activePluginTools, pluginToolNames = []) {
    const structResult = validateManifestStructure(manifest);
    if (!structResult.valid) {
        return structResult;
    }
    const m = manifest;
    const versionResult = validateVersionCompatibility(m, currentVersion);
    const toolResult = validateToolConflicts(m, pluginToolNames, builtInTools, activePluginTools);
    const allErrors = [...structResult.errors, ...versionResult.errors, ...toolResult.errors];
    const allWarnings = [...structResult.warnings, ...versionResult.warnings, ...toolResult.warnings];
    return { valid: allErrors.length === 0, errors: allErrors, warnings: allWarnings };
}
//# sourceMappingURL=validator.js.map