// packages/plugin-awf/src/utils/validation.ts
// AWF Installation Validation - Verify installed components
// version: 6.2.3 (no changes)
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import { join } from "node:path";
/**
 * Find files recursively by name
 */
export function findFiles(dir, filename) {
    const results = [];
    function scan(d) {
        try {
            const entries = readdirSync(d, { withFileTypes: true });
            for (const entry of entries) {
                const fullPath = join(d, entry.name);
                if (entry.isDirectory()) {
                    scan(fullPath);
                }
                else if (entry.name === filename) {
                    results.push(fullPath);
                }
            }
        }
        catch {
            // Directory not accessible, skip
        }
    }
    if (existsSync(dir)) {
        scan(dir);
    }
    return results;
}
/**
 * Count files in a directory recursively
 */
export function countFiles(dir) {
    let count = 0;
    function scan(d) {
        try {
            const entries = readdirSync(d, { withFileTypes: true });
            for (const entry of entries) {
                if (entry.isDirectory()) {
                    scan(join(d, entry.name));
                }
                else {
                    count++;
                }
            }
        }
        catch {
            // Directory not accessible, skip
        }
    }
    if (existsSync(dir)) {
        scan(dir);
    }
    return count;
}
/**
 * Validate SKILL.md file has required frontmatter
 */
export function validateSkillMd(filePath) {
    try {
        const content = readFileSync(filePath, "utf-8");
        // Valid if has content and starts with heading or frontmatter
        if (content.trim().length < 10)
            return false;
        return content.startsWith("---") || content.startsWith("#");
    }
    catch {
        return false;
    }
}
/**
 * Validate Cursor .mdc file format
 */
export function validateMdcFile(filePath) {
    try {
        const content = readFileSync(filePath, "utf-8");
        // Must start with frontmatter
        if (!content.startsWith("---")) {
            return { valid: false, issue: "Missing YAML frontmatter" };
        }
        // Check for required fields
        if (!content.includes("globs:")) {
            return { valid: false, issue: "Missing globs field" };
        }
        // Validate globs format - should NOT have brackets
        const globsMatch = content.match(/globs:\s*(\[.+\])/);
        if (globsMatch) {
            return { valid: false, issue: "globs uses array format (should be comma-separated)" };
        }
        return { valid: true };
    }
    catch (error) {
        return { valid: false, issue: error instanceof Error ? error.message : String(error) };
    }
}
/**
 * Validate installation results
 */
export function validateInstallation(spec, rootDir, results) {
    const errors = [];
    const warnings = [];
    // Helper: detect copy failures (error even if count=0)
    const checkCopyError = (name, result) => {
        if (result?.error) {
            errors.push(`${name} copy failed: ${result.error}`);
        }
    };
    // 1. Validate skills copied
    checkCopyError("Skills", results.skills);
    if (spec.skills.enabled && results.skills && results.skills.count > 0) {
        const skillsPath = join(rootDir, spec.skills.target);
        const skillFiles = findFiles(skillsPath, "SKILL.md");
        if (skillFiles.length === 0) {
            errors.push(`No SKILL.md files found in ${spec.skills.target}`);
        }
        else if (skillFiles.length < 10) {
            warnings.push(`Only ${skillFiles.length} skills copied, expected 50+`);
        }
        // Validate random sample of skills
        const sample = skillFiles.slice(0, 3);
        for (const file of sample) {
            if (!validateSkillMd(file)) {
                warnings.push(`Invalid skill format: ${file}`);
            }
        }
    }
    // 2. Validate workflows/commands copied
    checkCopyError("Workflows", results.workflows);
    if (spec.workflows.enabled && results.workflows && results.workflows.count > 0) {
        const workflowsPath = join(rootDir, spec.workflows.target);
        try {
            const files = readdirSync(workflowsPath);
            if (files.length < 10) {
                warnings.push(`Only ${files.length} workflows copied, expected 30+`);
            }
        }
        catch {
            errors.push(`Workflows directory not accessible: ${workflowsPath}`);
        }
    }
    // 3. Validate rules copied
    checkCopyError("Rules", results.rules);
    if (spec.rules.enabled && results.rules && results.rules.count > 0) {
        const rulesPath = join(rootDir, spec.rules.target);
        try {
            const files = readdirSync(rulesPath);
            if (files.length < 5) {
                warnings.push(`Only ${files.length} rules copied, expected 10+`);
            }
            // For Cursor, validate .mdc format
            if (spec.rules.ext === ".mdc") {
                const mdcFiles = files.filter(f => f.endsWith(".mdc")).slice(0, 3);
                for (const file of mdcFiles) {
                    const validation = validateMdcFile(join(rulesPath, file));
                    if (!validation.valid) {
                        errors.push(`Invalid .mdc format in ${file}: ${validation.issue}`);
                    }
                }
            }
        }
        catch {
            errors.push(`Rules directory not accessible: ${rulesPath}`);
        }
    }
    // 4. Validate root config
    if (results.rootConfig) {
        const configPath = join(rootDir, spec.rootConfig.target);
        try {
            if (!existsSync(configPath)) {
                errors.push(`Root config not found: ${configPath}`);
            }
            else {
                const stat = statSync(configPath);
                if (stat.size < 100) {
                    warnings.push("Root config file is suspiciously small (<100 bytes)");
                }
            }
        }
        catch {
            errors.push(`Cannot access root config: ${configPath}`);
        }
    }
    // 5. Validate core engine files
    checkCopyError("Core", results.core);
    if (spec.core.enabled && results.core && results.core.count > 0) {
        const corePath = join(rootDir, spec.core.target);
        if (!existsSync(corePath)) {
            errors.push(`Core directory not found: ${corePath}`);
        }
    }
    // 6. Validate memory structure
    checkCopyError("Memory", results.memory);
    if (spec.memory.enabled && results.memory && results.memory.count > 0) {
        const memoryPath = join(rootDir, spec.memory.target);
        const stateFile = join(memoryPath, "state.json");
        if (!existsSync(stateFile)) {
            warnings.push("Memory state.json not created");
        }
    }
    // 7. Validate personas
    checkCopyError("Personas", results.personas);
    // 8. Validate templates
    checkCopyError("Templates", results.templates);
    return {
        valid: errors.length === 0,
        errors,
        warnings,
    };
}
/**
 * Generate validation report
 */
export function formatValidationReport(result) {
    const lines = [];
    if (result.valid) {
        lines.push("✅ Validation passed");
    }
    else {
        lines.push("❌ Validation failed");
    }
    if (result.errors.length > 0) {
        lines.push("\n🔴 Errors:");
        for (const error of result.errors) {
            lines.push(`   - ${error}`);
        }
    }
    if (result.warnings.length > 0) {
        lines.push("\n🟡 Warnings:");
        for (const warning of result.warnings) {
            lines.push(`   - ${warning}`);
        }
    }
    return lines.join("\n");
}
