"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var ExecuteCodeHandler_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecuteCodeHandler = void 0;
const common_1 = require("@nestjs/common");
const execute_code_args_dto_1 = require("./dto/execute-code.args.dto");
const class_transformer_1 = require("class-transformer");
const class_validator_1 = require("class-validator");
const utils_1 = require("../../utils");
const spawn_with_kill_1 = require("../../utils/spawn-with-kill");
const path = require("path");
const fs = require("fs/promises");
const crypto = require("crypto");
const LANGUAGE_CONFIGS = {
    typescript: {
        extension: '.ts',
        command: (tempFilePath) => `npx tsx "${tempFilePath}"`,
    },
    python: {
        extension: '.py',
        command: (tempFilePath) => `python3 "${tempFilePath}"`,
    },
};
let ExecuteCodeHandler = ExecuteCodeHandler_1 = class ExecuteCodeHandler {
    constructor() {
        this.toolName = 'execute_code';
        this.logger = new common_1.Logger(ExecuteCodeHandler_1.name);
        this.projectRoot = process.env.REPOBURG_PROJECT_PATH || process.cwd();
        this.tempDir = path.join(this.projectRoot, '.repoburg', 'temp');
    }
    getMetadata() {
        return {
            name: this.toolName,
            description: this.getDefinition(),
            arguments: [
                {
                    name: 'code',
                    type: 'string',
                    description: 'The code to execute. TypeScript or Python depending on the language parameter.',
                    required: true,
                },
                {
                    name: 'language',
                    type: 'string',
                    description: "The programming language of the code. 'typescript' (default) or 'python'.",
                    required: false,
                },
            ],
        };
    }
    generateExample(toolCall) {
        return (0, utils_1.generateToolCallJson)(toolCall);
    }
    getDefinition() {
        const tsExample = this.generateExample({
            tool_name: this.toolName,
            code: `
import * as fs from 'fs';
const files = fs.readdirSync('.');
console.log(files.join('\\n'));
`,
        });
        const pythonExample = this.generateExample({
            tool_name: this.toolName,
            language: 'python',
            code: `
import os
files = os.listdir('.')
print('\\n'.join(files))
`,
        });
        const definition = `
-------------
### ${this.toolName}
  Executes the provided code in a temporary environment.
  Useful for testing logic, verifying algorithms, or inspecting the system programmatically without permanently creating files.

  Supports two languages:
  - **typescript** (default): Executed via 'npx tsx'. Use ES modules and standard Node.js APIs.
  - **python**: Executed via 'python3'. Use standard Python 3 libraries.

  Choose the language best suited for the task: use TypeScript for filesystem/Node.js operations, Python for data analysis, math, or when the user explicitly requests Python. If the user specifies a language, use it.

  If Python execution fails with a "not available" error, retry using TypeScript instead. Do NOT attempt to install Python.

  #### Parameters
  - "code": (string) The code to execute.
  - "language": (string, optional) The programming language: 'typescript' (default) or 'python'.

  #### TypeScript Example
:${tsExample}

  #### Python Example
:${pythonExample}
-------------
`;
        return `\n${definition.trim()}\n`;
    }
    async isPythonAvailable() {
        if (this.pythonAvailable !== undefined) {
            return this.pythonAvailable;
        }
        try {
            const result = await (0, spawn_with_kill_1.execWithProcessGroupKill)('python3 --version', {
                cwd: this.projectRoot,
                timeout: 5000,
            });
            this.pythonAvailable = !result.killed && result.exitCode === 0;
        }
        catch {
            this.pythonAvailable = false;
        }
        this.logger.log(`Python availability check: ${this.pythonAvailable ? 'available' : 'not available'}`);
        return this.pythonAvailable;
    }
    async execute(args, _context) {
        const validatedArgs = (0, class_transformer_1.plainToClass)(execute_code_args_dto_1.ExecuteCodeArgsDto, args);
        const errors = await (0, class_validator_1.validate)(validatedArgs);
        if (errors.length > 0) {
            const errorMessages = errors
                .map((err) => Object.values(err.constraints || {}).join(', '))
                .join('; ');
            return {
                status: 'FAILURE',
                summary: `Invalid arguments for ${this.toolName}.`,
                error_message: errorMessages,
                persisted_args: args,
                execution_log: { output: '', error_message: errorMessages },
            };
        }
        const { code, language } = validatedArgs;
        const config = LANGUAGE_CONFIGS[language];
        if (language === 'python') {
            const pythonReady = await this.isPythonAvailable();
            if (!pythonReady) {
                return {
                    status: 'FAILURE',
                    summary: 'Python is not available on this system.',
                    error_message: 'Python (python3) is not available on this system. Please use TypeScript instead by omitting the language parameter or setting language to "typescript". Do NOT attempt to install Python.',
                    persisted_args: { content: code, language },
                    execution_log: {
                        output: '',
                        error_message: 'Python (python3) is not available on this system. Please use TypeScript instead.',
                    },
                };
            }
        }
        const randomId = crypto.randomBytes(8).toString('hex');
        const tempFilePath = path.join(this.tempDir, `exec-${randomId}${config.extension}`);
        let unifiedOutput;
        let executionStatus = 'SUCCESS';
        let errorDetails;
        try {
            await fs.mkdir(this.tempDir, { recursive: true });
            await fs.writeFile(tempFilePath, code, 'utf-8');
            const result = await (0, spawn_with_kill_1.execWithProcessGroupKill)(config.command(tempFilePath), {
                cwd: this.projectRoot,
                timeout: 30000,
            });
            if (result.killed) {
                executionStatus = 'FAILURE';
                unifiedOutput = `EXECUTION TIMED OUT after 30000ms\n\nSTDOUT:\n${result.stdout || 'N/A'}\n\nSTDERR:\n${result.stderr || 'N/A'}`;
                errorDetails = 'Code execution timed out after 30000ms.';
                this.logger.warn(`Code execution timed out`);
            }
            else if (result.exitCode !== 0) {
                executionStatus = 'FAILURE';
                unifiedOutput = `EXECUTION FAILED with exit code ${result.exitCode}\n\nSTDOUT:\n${result.stdout || 'N/A'}\n\nSTDERR:\n${result.stderr || 'N/A'}`;
                errorDetails = `Code execution failed with exit code ${result.exitCode}.`;
                this.logger.error(`Error executing code: ${unifiedOutput}`);
            }
            else {
                unifiedOutput =
                    result.stdout || result.stderr || '(Script produced no output)';
            }
        }
        catch (error) {
            const execError = error;
            executionStatus = 'FAILURE';
            unifiedOutput = `EXECUTION FAILED: ${execError.message}\n\nSTDOUT:\n${execError.stdout || 'N/A'}\n\nSTDERR:\n${execError.stderr || 'N/A'}`;
            errorDetails = execError.message;
            this.logger.error(`Error executing code: ${unifiedOutput}`);
        }
        finally {
            try {
                await fs.unlink(tempFilePath);
            }
            catch (cleanupErr) {
                this.logger.warn(`Failed to delete temp file ${tempFilePath}: ${cleanupErr}`);
            }
        }
        return {
            status: executionStatus,
            summary: executionStatus === 'SUCCESS'
                ? `Code executed successfully.`
                : `Code execution failed.`,
            error_message: errorDetails,
            persisted_args: { content: code, language },
            execution_log: {
                output: unifiedOutput,
                error_message: errorDetails || '',
            },
        };
    }
};
exports.ExecuteCodeHandler = ExecuteCodeHandler;
exports.ExecuteCodeHandler = ExecuteCodeHandler = ExecuteCodeHandler_1 = __decorate([
    (0, common_1.Injectable)()
], ExecuteCodeHandler);
//# sourceMappingURL=execute-code.handler.js.map