"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.OperationExecutor = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const diff_1 = require("diff");
const OperationValidator_1 = require("./OperationValidator");
const OperationCache_1 = require("./OperationCache");
const OperationFingerprint_1 = require("./OperationFingerprint");
const OperationReplay_1 = require("./OperationReplay");
class OperationExecutor {
    constructor(baseDir, options) {
        const dir = baseDir || process.cwd();
        this.baseDir = dir;
        this.historyDir = path.join(dir, ".mycontext", "operations");
        // Initialize enhanced services if enabled
        if (options?.enableValidation) {
            this.validator = new OperationValidator_1.OperationValidator(dir);
        }
        if (options?.enableCaching) {
            this.fingerprint = new OperationFingerprint_1.OperationFingerprintService(dir);
            this.cache = new OperationCache_1.OperationCache(dir, this.fingerprint);
            if (this.validator) {
                this.replay = new OperationReplay_1.OperationReplayService(this.cache, this.fingerprint, this.validator);
            }
        }
    }
    /**
     * Initialize enhanced services
     */
    async init() {
        if (this.fingerprint) {
            await this.fingerprint.init();
        }
        if (this.cache) {
            await this.cache.init();
        }
    }
    /**
     * Enhanced execute with validation, caching, and idempotency
     */
    async executeEnhanced(op, options = {}) {
        const startTime = Date.now();
        const result = {
            success: false,
            executionTimeMs: 0,
        };
        try {
            // Step 1: Pre-flight validation
            if (options.enableValidation && this.validator) {
                const preFlight = await this.validator.validatePreFlight(op);
                result.validation = { preFlight };
                if (!preFlight.canProceed) {
                    result.success = false;
                    result.error = `Pre-flight validation failed: ${preFlight.errors.map((e) => e.message).join(", ")}`;
                    result.executionTimeMs = Date.now() - startTime;
                    return result;
                }
                // High-risk operations may require user approval
                if (preFlight.requiresUserApproval && !options.dryRun) {
                    result.success = false;
                    result.error = "High-risk operation requires user approval";
                    result.executionTimeMs = Date.now() - startTime;
                    return result;
                }
            }
            // Step 2: Check cache (if enabled)
            if (options.enableCaching && this.cache && this.fingerprint) {
                const cachedResult = await this.cache.get(op);
                if (cachedResult) {
                    result.success = true;
                    result.outputPath = cachedResult.outputPath;
                    result.diff = cachedResult.diff;
                    result.caching = {
                        cacheHit: true,
                        fingerprint: this.fingerprint.generateFingerprint(op),
                        replayed: true,
                    };
                    result.executionTimeMs = Date.now() - startTime;
                    return result;
                }
                else {
                    result.caching = {
                        cacheHit: false,
                        fingerprint: this.fingerprint.generateFingerprint(op),
                        replayed: false,
                    };
                }
            }
            // Step 3: Check idempotency (if enabled)
            if (options.enableIdempotencyCheck && this.fingerprint) {
                const skipCheck = await this.fingerprint.canSkip(op);
                if (skipCheck.skip && options.skipIfApplied) {
                    result.success = true;
                    result.idempotency = {
                        skipped: true,
                        reason: skipCheck.reason,
                    };
                    result.executionTimeMs = Date.now() - startTime;
                    return result;
                }
            }
            // Step 4: Dry-run mode
            if (options.dryRun && this.validator) {
                const dryRunResult = await this.validator.dryRun(op);
                result.success = dryRunResult.success;
                result.error = dryRunResult.errors.join(", ");
                result.executionTimeMs = Date.now() - startTime;
                return result;
            }
            // Step 5: Execute the operation
            const execResult = await this.execute(op);
            Object.assign(result, execResult);
            // Step 6: Post-flight validation
            if (options.enableValidation && this.validator && execResult.success) {
                const postFlight = await this.validator.validatePostFlight(op);
                if (result.validation) {
                    result.validation.postFlight = postFlight;
                }
                else {
                    result.validation = { preFlight: postFlight, postFlight };
                }
                if (!postFlight.passed) {
                    result.success = false;
                    result.error = `Post-flight validation failed: ${postFlight.errors.map((e) => e.message).join(", ")}`;
                }
            }
            // Step 7: Cache the result (if successful and caching enabled)
            if (result.success &&
                options.enableCaching &&
                this.cache &&
                this.fingerprint) {
                await this.cache.set(op, execResult, Date.now() - startTime);
                await this.fingerprint.markApplied(op);
            }
            result.executionTimeMs = Date.now() - startTime;
            return result;
        }
        catch (error) {
            result.success = false;
            result.error = error.message;
            result.executionTimeMs = Date.now() - startTime;
            return result;
        }
    }
    /**
     * Original execute method (preserved for backward compatibility)
     */
    async execute(op) {
        try {
            let result;
            switch (op.type) {
                case "create-file":
                    result = await this.createFile(op.params);
                    break;
                case "patch-file":
                    result = await this.patchFile(op.params);
                    break;
                case "rename-file":
                    result = await this.renameFile(op.params);
                    break;
                case "delete-file":
                    result = await this.deleteFile(op.params);
                    break;
                case "add-import":
                    result = await this.addImport(op.params);
                    break;
                case "add-export":
                    result = await this.addExport(op.params);
                    break;
                case "add-classname":
                    result = await this.addClassname(op.params);
                    break;
                case "add-i18n-key":
                    result = await this.addI18nKey(op.params);
                    break;
                case "remove-import":
                    result = await this.removeImport(op.params);
                    break;
                case "add-server-action":
                    result = await this.addServerAction(op.params);
                    break;
                case "add-permission":
                    result = await this.addPermission(op.params);
                    break;
                case "add-test":
                    result = await this.addTest(op.params);
                    break;
                case "add-route":
                    result = await this.addRoute(op.params);
                    break;
                case "add-component":
                    result = await this.addComponent(op.params);
                    break;
                case "add-action":
                    result = await this.addAction(op.params);
                    break;
                case "add-model":
                    result = await this.addModel(op.params);
                    break;
                default:
                    result = { success: false, error: `Unsupported operation type: ${op.type}` };
            }
            await this.recordOperation(op, result);
            return result;
        }
        catch (error) {
            const result = {
                success: false,
                error: error.message,
            };
            await this.recordOperation(op, result);
            return result;
        }
    }
    async executeBatch(ops) {
        const results = [];
        const applied = [];
        try {
            for (const op of ops) {
                // Check dependencies
                if (op.dependsOn) {
                    for (const depId of op.dependsOn) {
                        const depOp = ops.find(o => o.id === depId);
                        if (depOp && !applied.find(a => a.id === depId)) {
                            throw new Error(`Dependency not satisfied: ${depId} must run before ${op.id}`);
                        }
                    }
                }
                const result = await this.execute(op);
                results.push(result);
                if (result.success) {
                    applied.push(op);
                }
                else {
                    // Rollback on failure
                    await this.rollbackBatch(applied);
                    break;
                }
            }
        }
        catch (error) {
            // Rollback everything on exception
            await this.rollbackBatch(applied);
            results.push({
                success: false,
                error: `Batch failed: ${error.message}`,
            });
        }
        return results;
    }
    async rollback(op) {
        const historyPath = path.join(this.historyDir, `${op.id}.json`);
        if (!(await fs.pathExists(historyPath)))
            return false;
        const record = await fs.readJson(historyPath);
        if (!record.before)
            return false;
        try {
            switch (op.type) {
                case "create-file":
                    await fs.remove(op.params.path);
                    break;
                case "patch-file":
                case "add-import":
                case "add-export":
                case "add-classname":
                    await fs.writeFile(op.params.path, record.before, "utf-8");
                    break;
                case "rename-file":
                    await fs.move(op.params.to, op.params.from, { overwrite: true });
                    break;
                case "delete-file":
                    if (record.before) {
                        await fs.writeFile(op.params.path, record.before, "utf-8");
                    }
                    break;
                default:
                    return false;
            }
            op.status = "rolled-back";
            return true;
        }
        catch {
            return false;
        }
    }
    async rollbackBatch(ops) {
        let allSuccess = true;
        for (const op of ops.reverse()) {
            const ok = await this.rollback(op);
            if (!ok)
                allSuccess = false;
        }
        return allSuccess;
    }
    async createFile(params) {
        const absPath = path.resolve(params.path);
        await fs.ensureDir(path.dirname(absPath));
        if (await fs.pathExists(absPath) && !params.overwrite) {
            return { success: false, error: `File already exists: ${params.path}` };
        }
        await fs.writeFile(absPath, params.content, "utf-8");
        return { success: true, outputPath: params.path };
    }
    async patchFile(params) {
        const absPath = path.resolve(params.path);
        if (!(await fs.pathExists(absPath))) {
            return { success: false, error: `File not found: ${params.path}` };
        }
        const content = await fs.readFile(absPath, "utf-8");
        const before = content;
        if (!content.includes(params.oldString)) {
            return {
                success: false,
                error: `String not found in ${params.path}: "${params.oldString.substring(0, 50)}..."`,
            };
        }
        const after = content.replace(params.oldString, params.newString);
        await fs.writeFile(absPath, after, "utf-8");
        return {
            success: true,
            outputPath: params.path,
            diff: (0, diff_1.createPatch)(params.path, before, after),
        };
    }
    async renameFile(params) {
        const fromAbs = path.resolve(params.from);
        const toAbs = path.resolve(params.to);
        if (!(await fs.pathExists(fromAbs))) {
            return { success: false, error: `File not found: ${params.from}` };
        }
        await fs.ensureDir(path.dirname(toAbs));
        await fs.move(fromAbs, toAbs, { overwrite: true });
        return { success: true, outputPath: params.to };
    }
    async deleteFile(params) {
        const absPath = path.resolve(params.path);
        if (!(await fs.pathExists(absPath))) {
            return { success: false, error: `File not found: ${params.path}` };
        }
        await fs.remove(absPath);
        return { success: true, outputPath: params.path };
    }
    async addImport(params) {
        const absPath = path.resolve(params.file);
        if (!(await fs.pathExists(absPath))) {
            return { success: false, error: `File not found: ${params.file}` };
        }
        let content = await fs.readFile(absPath, "utf-8");
        if (content.includes(params.import_)) {
            return { success: true, outputPath: params.file, diff: "" };
        }
        const importStatement = `import { ${params.import_} } from "${params.source}";\n`;
        // Insert after the last import statement
        const importMatch = content.match(/^import .+;$/gm);
        if (importMatch && importMatch.length > 0) {
            const lastImport = importMatch[importMatch.length - 1];
            content = content.replace(lastImport, lastImport + "\n" + importStatement);
        }
        else {
            content = importStatement + content;
        }
        await fs.writeFile(absPath, content, "utf-8");
        return {
            success: true,
            outputPath: params.file,
            diff: (0, diff_1.createPatch)(params.file, "", content),
        };
    }
    async addExport(params) {
        const absPath = path.resolve(params.file);
        if (!(await fs.pathExists(absPath))) {
            return { success: false, error: `File not found: ${params.file}` };
        }
        let content = await fs.readFile(absPath, "utf-8");
        const exportStatement = `export { ${params.export_} };\n`;
        if (content.includes(exportStatement.trim())) {
            return { success: true, outputPath: params.file };
        }
        content += "\n" + exportStatement;
        await fs.writeFile(absPath, content, "utf-8");
        return { success: true, outputPath: params.file };
    }
    async addClassname(params) {
        const absPath = path.resolve(params.file);
        if (!(await fs.pathExists(absPath))) {
            return { success: false, error: `File not found: ${params.file}` };
        }
        let content = await fs.readFile(absPath, "utf-8");
        const regex = new RegExp(`(className=['"\`][^'"\`]*)(${params.component})?([^'"\`]*['"\`])`);
        if (!regex.test(content)) {
            // No existing className — add one
            const tagRegex = new RegExp(`<${params.component}(\\s[^>]*)?>`, "i");
            content = content.replace(tagRegex, `<${params.component}$1 className="${params.classname}">`);
        }
        else {
            // Append to existing className
            content = content.replace(regex, (match, prefix, _, suffix) => {
                const existing = prefix.replace(/className=['"`]/, "").trim();
                const newClasses = existing
                    ? `${existing} ${params.classname}`
                    : params.classname;
                return `className="${newClasses}"`;
            });
        }
        await fs.writeFile(absPath, content, "utf-8");
        return { success: true, outputPath: params.file };
    }
    async addI18nKey(params) {
        const locale = params.locale || "en";
        const dictPath = path.join(process.cwd(), "i18n", `${locale}.json`);
        let dict = {};
        if (await fs.pathExists(dictPath)) {
            dict = await fs.readJson(dictPath);
        }
        else {
            await fs.ensureDir(path.dirname(dictPath));
        }
        dict[params.key] = params.value;
        await fs.writeJson(dictPath, dict, { spaces: 2 });
        return {
            success: true,
            outputPath: path.relative(process.cwd(), dictPath),
        };
    }
    async removeImport(params) {
        const absPath = path.resolve(params.file);
        if (!(await fs.pathExists(absPath))) {
            return { success: false, error: `File not found: ${params.file}` };
        }
        let content = await fs.readFile(absPath, "utf-8");
        const regex = new RegExp(`import\\s*\\{[^}]*\\b${params.import_}\\b[^}]*\\}\\s*from\\s*["'][^"']+["'];?`, "g");
        const before = content;
        content = content.replace(regex, (match) => {
            const cleaned = match.replace(/\s*,\s*$/, "").replace(/\s*,\s*/, ",");
            const withoutImport = cleaned.replace(new RegExp(`,?\\s*${params.import_}\\s*,?`), "");
            const final = withoutImport.replace(/,\s*}/g, " }").replace(/\{\s*,/g, "{ ");
            return final === withoutImport ? final : final;
        });
        if (content === before) {
            return { success: false, error: `Import '${params.import_}' not found in ${params.file}` };
        }
        await fs.writeFile(absPath, content, "utf-8");
        return { success: true, outputPath: params.file, diff: (0, diff_1.createPatch)(params.file, before, content) };
    }
    async addServerAction(params) {
        const absPath = path.resolve(params.file);
        await fs.ensureDir(path.dirname(absPath));
        const paramsBlock = params.params
            ? Object.entries(params.params)
                .map(([k, v]) => `${k}: ${v}`)
                .join(", ")
            : "";
        const actionCode = `
export async function ${params.name}(${paramsBlock ? `{ ${paramsBlock} }: { ${Object.entries(params.params || {}).map(([k, v]) => `${k}: ${v}`).join("; ")} }` : ""}) {
  "use server";
  // TODO: Implement ${params.name}
  throw new Error("Not implemented");
}
`;
        if (await fs.pathExists(absPath)) {
            let content = await fs.readFile(absPath, "utf-8");
            content += "\n" + actionCode;
            await fs.writeFile(absPath, content, "utf-8");
            return { success: true, outputPath: params.file, diff: (0, diff_1.createPatch)(params.file, content, content + actionCode) };
        }
        await fs.writeFile(absPath, `"use server";\n\n${actionCode}`, "utf-8");
        return { success: true, outputPath: params.file };
    }
    async addPermission(params) {
        const permFilePath = path.join(process.cwd(), ".mycontext", "permissions.json");
        let perms = { roles: {}, resources: {} };
        if (await fs.pathExists(permFilePath)) {
            perms = await fs.readJson(permFilePath);
        }
        else {
            await fs.ensureDir(path.dirname(permFilePath));
        }
        if (!perms.roles[params.role]) {
            perms.roles[params.role] = { permissions: [] };
        }
        if (!perms.resources[params.resource]) {
            perms.resources[params.resource] = { actions: [] };
        }
        const entry = `${params.role}:${params.resource}:${params.action}`;
        if (!perms.roles[params.role].permissions.includes(entry)) {
            perms.roles[params.role].permissions.push(entry);
        }
        if (!perms.resources[params.resource].actions.includes(params.action)) {
            perms.resources[params.resource].actions.push(params.action);
        }
        await fs.writeJson(permFilePath, perms, { spaces: 2 });
        return { success: true, outputPath: path.relative(process.cwd(), permFilePath) };
    }
    async addTest(params) {
        const absPath = path.resolve(params.file);
        await fs.ensureDir(path.dirname(absPath));
        const testContent = `
describe("${params.testName}", () => {
  it("should work as expected", () => {
    // TODO: Implement test
    expect(true).toBe(true);
  });
});
`;
        if (await fs.pathExists(absPath)) {
            let content = await fs.readFile(absPath, "utf-8");
            content += "\n" + testContent;
            await fs.writeFile(absPath, content, "utf-8");
            return { success: true, outputPath: params.file };
        }
        await fs.writeFile(absPath, testContent, "utf-8");
        return { success: true, outputPath: params.file };
    }
    async addRoute(params) {
        const srcDir = path.join(process.cwd(), "src");
        const routePath = path.join(srcDir, "app", params.path.replace(/^\//, ""), "page.tsx");
        await fs.ensureDir(path.dirname(routePath));
        const routeContent = `
import ${params.component} from "${params.component}";

export default function Page() {
  return <${params.component} />;
}
`;
        await fs.writeFile(routePath, routeContent, "utf-8");
        return { success: true, outputPath: path.relative(process.cwd(), routePath) };
    }
    async addComponent(params) {
        const srcDir = path.join(process.cwd(), "src");
        const componentDir = params.path
            ? path.resolve(params.path)
            : path.join(srcDir, "components");
        const ext = params.type === "server" ? ".tsx" : ".tsx";
        const compPath = path.join(componentDir, `${params.name}${ext}`);
        await fs.ensureDir(componentDir);
        const directive = params.type === "server" ? "" : '"use client";\n\n';
        const componentContent = `
${directive}interface ${params.name}Props {
  // TODO: Define props
}

export function ${params.name}({}: ${params.name}Props) {
  return (
    <div>
      <h1>${params.name}</h1>
    </div>
  );
}
`;
        await fs.writeFile(compPath, componentContent, "utf-8");
        return { success: true, outputPath: path.relative(process.cwd(), compPath) };
    }
    async addAction(params) {
        const srcDir = path.join(process.cwd(), "src");
        const actionDir = params.type === "api-route"
            ? path.join(srcDir, "app", "api", params.name)
            : path.join(srcDir, "actions");
        await fs.ensureDir(actionDir);
        if (params.type === "api-route") {
            const routePath = path.join(actionDir, "route.ts");
            const routeContent = `
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest) {
  return NextResponse.json({ message: "${params.description}" });
}
`;
            await fs.writeFile(routePath, routeContent, "utf-8");
            return { success: true, outputPath: path.relative(process.cwd(), routePath) };
        }
        const actionPath = path.join(actionDir, `${params.name}.ts`);
        const actionContent = `
"use server";

export async function ${params.name}(input: unknown) {
  // ${params.description}
  // TODO: Implement
  return { success: true };
}
`;
        await fs.writeFile(actionPath, actionContent, "utf-8");
        return { success: true, outputPath: path.relative(process.cwd(), actionPath) };
    }
    async addModel(params) {
        const srcDir = path.join(process.cwd(), "src");
        const modelsDir = path.join(srcDir, "models");
        await fs.ensureDir(modelsDir);
        const modelPath = path.join(modelsDir, `${params.name}.ts`);
        const fields = params.fields.map((f) => `  ${f.name}${f.required ? "" : "?"}: ${f.type};`).join("\n");
        const modelContent = `
export interface ${params.name} {
${fields}
}

export type Create${params.name}Input = Omit<${params.name}, "id" | "createdAt" | "updatedAt">;
export type Update${params.name}Input = Partial<Create${params.name}Input>;
`;
        await fs.writeFile(modelPath, modelContent, "utf-8");
        return { success: true, outputPath: path.relative(process.cwd(), modelPath) };
    }
    async recordOperation(op, result) {
        await fs.ensureDir(this.historyDir);
        const record = {
            operation: op,
            result,
            timestamp: new Date().toISOString(),
        };
        // Capture before-state for rollback
        if (op.params.path && (await fs.pathExists(op.params.path))) {
            try {
                record.before = await fs.readFile(op.params.path, "utf-8");
            }
            catch { }
        }
        await fs.writeJson(path.join(this.historyDir, `${op.id}.json`), record, { spaces: 2 });
    }
}
exports.OperationExecutor = OperationExecutor;
//# sourceMappingURL=OperationExecutor.js.map