"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.OperationValidator = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const operation_schema_1 = require("@myycontext/core/src/types/operation-schema");
/**
 * OperationValidator: Pre-flight and post-flight validation
 */
class OperationValidator {
    constructor(baseDir) {
        this.baseDir = baseDir || process.cwd();
        this.validationCache = new Map();
    }
    /**
     * Pre-flight validation: Validate before execution
     */
    async validatePreFlight(op) {
        const checks = [];
        const schema = (0, operation_schema_1.getOperationSchema)(op.type);
        // 1. Schema validation
        const schemaCheck = this.validateSchema(op);
        checks.push(schemaCheck);
        // 2. Pre-condition validation
        const preConditionChecks = await this.validatePreConditions(op, schema.preConditions);
        checks.push(...preConditionChecks);
        // 3. File system validation
        const fsChecks = await this.validateFileSystem(op);
        checks.push(...fsChecks);
        // 4. Syntax validation (for existing files)
        const syntaxChecks = await this.validateSyntax(op);
        checks.push(...syntaxChecks);
        // 5. Risk assessment
        const riskCheck = this.assessRisk(op, schema);
        checks.push(riskCheck);
        const errors = checks.filter((c) => c.severity === "error" && !c.passed);
        const warnings = checks.filter((c) => c.severity === "warning" && !c.passed);
        const passed = errors.length === 0;
        const report = {
            operationId: op.id,
            operationType: op.type,
            timestamp: new Date().toISOString(),
            phase: "pre-flight",
            passed,
            checks,
            errors,
            warnings,
            riskLevel: schema.riskLevel,
            estimatedDurationMs: schema.estimatedDurationMs,
            canProceed: passed,
            requiresUserApproval: schema.riskLevel === "high" && passed,
        };
        this.validationCache.set(op.id, report);
        return report;
    }
    /**
     * Post-flight validation: Validate after execution
     */
    async validatePostFlight(op) {
        const checks = [];
        const schema = (0, operation_schema_1.getOperationSchema)(op.type);
        // 1. Post-condition validation
        const postConditionChecks = await this.validatePostConditions(op, schema.postConditions);
        checks.push(...postConditionChecks);
        // 2. Side-effect validation
        const sideEffectChecks = await this.validateSideEffects(op, schema);
        checks.push(...sideEffectChecks);
        // 3. Syntax validation (for modified files)
        const syntaxChecks = await this.validateSyntax(op);
        checks.push(...syntaxChecks);
        const errors = checks.filter((c) => c.severity === "error" && !c.passed);
        const warnings = checks.filter((c) => c.severity === "warning" && !c.passed);
        const passed = errors.length === 0;
        const report = {
            operationId: op.id,
            operationType: op.type,
            timestamp: new Date().toISOString(),
            phase: "post-flight",
            passed,
            checks,
            errors,
            warnings,
            riskLevel: schema.riskLevel,
            estimatedDurationMs: schema.estimatedDurationMs,
            canProceed: passed,
        };
        return report;
    }
    /**
     * Dry-run: Simulate execution without side effects
     */
    async dryRun(op) {
        const schema = (0, operation_schema_1.getOperationSchema)(op.type);
        const result = {
            success: true,
            wouldCreate: [],
            wouldModify: [],
            wouldDelete: [],
            warnings: [],
            errors: [],
        };
        try {
            // Analyze what would happen based on operation type
            switch (op.type) {
                case "create-file": {
                    const filePath = op.params.path;
                    const absPath = path.resolve(this.baseDir, filePath);
                    if (await fs.pathExists(absPath)) {
                        if (!op.params.overwrite) {
                            result.errors.push(`File already exists: ${filePath}`);
                            result.success = false;
                        }
                        else {
                            result.wouldModify.push(filePath);
                            result.warnings.push(`Would overwrite existing file: ${filePath}`);
                        }
                    }
                    else {
                        result.wouldCreate.push(filePath);
                    }
                    break;
                }
                case "patch-file": {
                    const filePath = op.params.path;
                    const absPath = path.resolve(this.baseDir, filePath);
                    if (!(await fs.pathExists(absPath))) {
                        result.errors.push(`File not found: ${filePath}`);
                        result.success = false;
                    }
                    else {
                        const content = await fs.readFile(absPath, "utf-8");
                        if (!content.includes(op.params.oldString)) {
                            result.errors.push(`String not found in ${filePath}`);
                            result.success = false;
                        }
                        else {
                            result.wouldModify.push(filePath);
                        }
                    }
                    break;
                }
                case "rename-file": {
                    const from = op.params.from;
                    const to = op.params.to;
                    const fromAbs = path.resolve(this.baseDir, from);
                    const toAbs = path.resolve(this.baseDir, to);
                    if (!(await fs.pathExists(fromAbs))) {
                        result.errors.push(`File not found: ${from}`);
                        result.success = false;
                    }
                    else if (await fs.pathExists(toAbs)) {
                        result.errors.push(`Destination already exists: ${to}`);
                        result.success = false;
                    }
                    else {
                        result.wouldDelete.push(from);
                        result.wouldCreate.push(to);
                        result.warnings.push(`This may break import statements`);
                    }
                    break;
                }
                case "delete-file": {
                    const filePath = op.params.path;
                    const absPath = path.resolve(this.baseDir, filePath);
                    if (!(await fs.pathExists(absPath))) {
                        result.warnings.push(`File already deleted: ${filePath}`);
                    }
                    else {
                        result.wouldDelete.push(filePath);
                        result.warnings.push(`This may break import statements`);
                    }
                    break;
                }
                case "add-import":
                case "add-export":
                case "remove-import":
                case "add-classname":
                case "add-server-action":
                case "add-test": {
                    const filePath = op.params.file;
                    const absPath = path.resolve(this.baseDir, filePath);
                    if (await fs.pathExists(absPath)) {
                        result.wouldModify.push(filePath);
                    }
                    else {
                        result.wouldCreate.push(filePath);
                    }
                    break;
                }
                case "add-component":
                case "add-action":
                case "add-model":
                case "add-route": {
                    // These create new files
                    result.wouldCreate.push(`${op.type}:${op.params.name || op.params.path}`);
                    break;
                }
                default:
                    result.warnings.push(`Dry-run not fully implemented for ${op.type}`);
            }
            // Check for high-risk operations
            if (schema.riskLevel === "high") {
                result.warnings.push(`HIGH RISK: This operation may break existing code`);
            }
        }
        catch (error) {
            result.errors.push(`Dry-run error: ${error.message}`);
            result.success = false;
        }
        return result;
    }
    /**
     * Detect conflicts with other operations
     */
    async detectConflicts(op, otherOps) {
        const conflicts = [];
        for (const other of otherOps) {
            if (other.id === op.id)
                continue;
            // File overlap detection
            const opFiles = this.getAffectedFiles(op);
            const otherFiles = this.getAffectedFiles(other);
            const overlap = opFiles.filter((f) => otherFiles.includes(f));
            if (overlap.length > 0) {
                conflicts.push({
                    operationId: other.id,
                    type: "file-overlap",
                    message: `Both operations affect: ${overlap.join(", ")}`,
                });
            }
            // Dependency cycle detection
            if (op.dependsOn?.includes(other.id) && other.dependsOn?.includes(op.id)) {
                conflicts.push({
                    operationId: other.id,
                    type: "dependency-cycle",
                    message: `Circular dependency detected`,
                });
            }
        }
        return {
            hasConflicts: conflicts.length > 0,
            conflicts,
        };
    }
    /**
     * Validate operation schema
     */
    validateSchema(op) {
        const result = (0, operation_schema_1.validateOperationParams)(op.type, op.params);
        return {
            name: "Schema Validation",
            severity: "error",
            passed: result.valid,
            message: result.valid
                ? "Operation parameters are valid"
                : `Schema validation failed: ${result.errors.join(", ")}`,
            suggestion: result.valid
                ? undefined
                : "Check operation parameters against schema definition",
        };
    }
    /**
     * Validate pre-conditions
     */
    async validatePreConditions(op, preConditions) {
        const checks = [];
        for (const condition of preConditions) {
            const check = await this.checkPreCondition(op, condition);
            checks.push(check);
        }
        return checks;
    }
    /**
     * Check a single pre-condition
     */
    async checkPreCondition(op, condition) {
        const resolvedPath = this.resolvePath(condition, op);
        switch (condition.type) {
            case "file-exists": {
                const exists = await fs.pathExists(resolvedPath);
                return {
                    name: `Pre-condition: ${condition.type}`,
                    severity: "error",
                    passed: exists,
                    message: exists
                        ? `File exists: ${condition.path}`
                        : `File not found: ${condition.path}`,
                    suggestion: exists ? undefined : "Create the file first",
                };
            }
            case "file-not-exists": {
                const exists = await fs.pathExists(resolvedPath);
                return {
                    name: `Pre-condition: ${condition.type}`,
                    severity: "error",
                    passed: !exists,
                    message: !exists
                        ? `File does not exist: ${condition.path}`
                        : `File already exists: ${condition.path}`,
                    suggestion: !exists ? undefined : "Delete the file or use overwrite flag",
                };
            }
            case "file-contains": {
                if (!(await fs.pathExists(resolvedPath))) {
                    return {
                        name: `Pre-condition: ${condition.type}`,
                        severity: "error",
                        passed: false,
                        message: `File not found: ${condition.path}`,
                    };
                }
                const content = await fs.readFile(resolvedPath, "utf-8");
                const contains = content.includes(condition.content);
                return {
                    name: `Pre-condition: ${condition.type}`,
                    severity: "error",
                    passed: contains,
                    message: contains
                        ? `File contains expected content`
                        : `File does not contain: "${condition.content.substring(0, 50)}..."`,
                };
            }
            case "directory-exists": {
                const exists = await fs.pathExists(resolvedPath);
                const stat = exists ? await fs.stat(resolvedPath) : null;
                const isDir = stat?.isDirectory() || false;
                return {
                    name: `Pre-condition: ${condition.type}`,
                    severity: "error",
                    passed: exists && isDir,
                    message: exists && isDir
                        ? `Directory exists: ${condition.path}`
                        : `Directory not found: ${condition.path}`,
                };
            }
            default:
                return {
                    name: `Pre-condition: ${condition.type}`,
                    severity: "warning",
                    passed: true,
                    message: `Pre-condition check not implemented: ${condition.type}`,
                };
        }
    }
    /**
     * Validate post-conditions
     */
    async validatePostConditions(op, postConditions) {
        const checks = [];
        for (const condition of postConditions) {
            const check = await this.checkPostCondition(op, condition);
            checks.push(check);
        }
        return checks;
    }
    /**
     * Check a single post-condition
     */
    async checkPostCondition(op, condition) {
        const resolvedPath = this.resolvePath(condition, op);
        switch (condition.type) {
            case "file-created": {
                const exists = await fs.pathExists(resolvedPath);
                return {
                    name: `Post-condition: ${condition.type}`,
                    severity: "error",
                    passed: exists,
                    message: exists
                        ? `File created: ${condition.path}`
                        : `File was not created: ${condition.path}`,
                };
            }
            case "file-modified": {
                const exists = await fs.pathExists(resolvedPath);
                return {
                    name: `Post-condition: ${condition.type}`,
                    severity: "error",
                    passed: exists,
                    message: exists
                        ? `File modified: ${condition.path}`
                        : `File not found: ${condition.path}`,
                };
            }
            case "file-deleted": {
                const exists = await fs.pathExists(resolvedPath);
                return {
                    name: `Post-condition: ${condition.type}`,
                    severity: "error",
                    passed: !exists,
                    message: !exists
                        ? `File deleted: ${condition.path}`
                        : `File still exists: ${condition.path}`,
                };
            }
            case "valid-syntax": {
                if (!(await fs.pathExists(resolvedPath))) {
                    return {
                        name: `Post-condition: ${condition.type}`,
                        severity: "error",
                        passed: false,
                        message: `File not found: ${condition.path}`,
                    };
                }
                // Basic syntax check (could use TypeScript compiler API for more thorough check)
                const content = await fs.readFile(resolvedPath, "utf-8");
                const hasSyntaxError = this.checkBasicSyntax(content, resolvedPath);
                return {
                    name: `Post-condition: ${condition.type}`,
                    severity: "error",
                    passed: !hasSyntaxError,
                    message: hasSyntaxError
                        ? `Syntax error in: ${condition.path}`
                        : `Valid syntax: ${condition.path}`,
                };
            }
            default:
                return {
                    name: `Post-condition: ${condition.type}`,
                    severity: "warning",
                    passed: true,
                    message: `Post-condition check not implemented: ${condition.type}`,
                };
        }
    }
    /**
     * Validate file system state
     */
    async validateFileSystem(op) {
        const checks = [];
        // Check parent directory exists for create operations
        if (op.type === "create-file" || op.type === "add-component") {
            const filePath = op.params.path;
            const parentDir = path.dirname(path.resolve(this.baseDir, filePath));
            const exists = await fs.pathExists(parentDir);
            checks.push({
                name: "Parent Directory Check",
                severity: "info",
                passed: exists,
                message: exists
                    ? `Parent directory exists`
                    : `Parent directory will be created: ${parentDir}`,
            });
        }
        return checks;
    }
    /**
     * Validate syntax
     */
    async validateSyntax(op) {
        const checks = [];
        const affectedFiles = this.getAffectedFiles(op);
        for (const file of affectedFiles) {
            const absPath = path.resolve(this.baseDir, file);
            if (!(await fs.pathExists(absPath)))
                continue;
            const content = await fs.readFile(absPath, "utf-8");
            const error = this.checkBasicSyntax(content, absPath);
            checks.push({
                name: `Syntax Check: ${file}`,
                severity: "warning",
                passed: !error,
                message: error || `Valid syntax`,
            });
        }
        return checks;
    }
    /**
     * Basic syntax check (not comprehensive)
     */
    checkBasicSyntax(content, filePath) {
        const ext = path.extname(filePath);
        // JSON validation
        if (ext === ".json") {
            try {
                JSON.parse(content);
                return null;
            }
            catch (e) {
                return `JSON syntax error: ${e.message}`;
            }
        }
        // TypeScript/JavaScript basic checks
        if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx") {
            // Check for obvious syntax errors
            const hasUnmatchedBraces = (content.match(/{/g) || []).length !== (content.match(/}/g) || []).length;
            const hasUnmatchedParens = (content.match(/\(/g) || []).length !== (content.match(/\)/g) || []).length;
            const hasUnmatchedBrackets = (content.match(/\[/g) || []).length !== (content.match(/\]/g) || []).length;
            if (hasUnmatchedBraces)
                return "Unmatched curly braces";
            if (hasUnmatchedParens)
                return "Unmatched parentheses";
            if (hasUnmatchedBrackets)
                return "Unmatched square brackets";
        }
        return null;
    }
    /**
     * Validate side effects
     */
    async validateSideEffects(op, schema) {
        const checks = [];
        // Verify expected side effects occurred
        for (const sideEffect of schema.sideEffects || []) {
            if (sideEffect.type === "file-system") {
                const resolvedPath = this.resolvePath(sideEffect, op);
                const exists = await fs.pathExists(resolvedPath);
                const expectedExists = sideEffect.action === "create" || sideEffect.action === "modify";
                const passed = exists === expectedExists;
                checks.push({
                    name: `Side Effect: ${sideEffect.action} ${sideEffect.path}`,
                    severity: "error",
                    passed,
                    message: passed
                        ? `Side effect verified`
                        : `Expected ${sideEffect.action} but file ${exists ? "exists" : "does not exist"}`,
                });
            }
        }
        return checks;
    }
    /**
     * Assess operation risk
     */
    assessRisk(op, schema) {
        const riskFactors = [];
        if (schema.riskLevel === "high") {
            riskFactors.push("High-risk operation type");
        }
        if (op.type === "delete-file" || op.type === "rename-file") {
            riskFactors.push("May break import statements");
        }
        if (schema.sideEffects.some((e) => e.type === "import-graph")) {
            riskFactors.push("Modifies import graph");
        }
        return {
            name: "Risk Assessment",
            severity: riskFactors.length > 0 ? "warning" : "info",
            passed: true,
            message: riskFactors.length > 0
                ? `Risk factors: ${riskFactors.join(", ")}`
                : "Low risk operation",
            suggestion: riskFactors.length > 0
                ? "Review operation carefully before proceeding"
                : undefined,
        };
    }
    /**
     * Get files affected by operation
     */
    getAffectedFiles(op) {
        const files = [];
        switch (op.type) {
            case "create-file":
            case "patch-file":
            case "delete-file":
                if (op.params.path)
                    files.push(op.params.path);
                break;
            case "rename-file":
                if (op.params.from)
                    files.push(op.params.from);
                if (op.params.to)
                    files.push(op.params.to);
                break;
            case "add-import":
            case "add-export":
            case "remove-import":
            case "add-classname":
            case "add-server-action":
            case "add-test":
                if (op.params.file)
                    files.push(op.params.file);
                break;
        }
        return files;
    }
    /**
     * Resolve template variables in paths
     */
    resolvePath(condition, op) {
        const pathTemplate = condition.path || condition.file || "";
        let resolved = pathTemplate;
        // Replace {{params.X}} with actual values
        const regex = /\{\{params\.(\w+)\}\}/g;
        resolved = resolved.replace(regex, (_, key) => {
            return op.params[key] || "";
        });
        return path.resolve(this.baseDir, resolved);
    }
    /**
     * Get cached validation report
     */
    getCachedReport(operationId) {
        return this.validationCache.get(operationId);
    }
    /**
     * Clear validation cache
     */
    clearCache() {
        this.validationCache.clear();
    }
}
exports.OperationValidator = OperationValidator;
//# sourceMappingURL=OperationValidator.js.map