/**
 * MOG execute_code provider — execute TypeScript code against a headless spreadsheet engine.
 *
 * Drop-in replacement for the COM execute_code provider. Same tool name,
 * same schema shape, but runs code in-process against @mog-sdk/node's
 * workbook via a VM sandbox.
 */
import { Type } from '@sinclair/typebox';
import * as vm from 'node:vm';
import * as ts from 'typescript';
import { EXECUTE_CODE } from '../../../../tool-names.js';
import { truncateOutput } from '../../../../utils/truncate.js';
import { getDirtyCellSummary } from '../../../new-sheet/cell-diff/index.js';
import { MutationTracker } from '../../../new-sheet/mutation-tracker.js';
import { buildSandbox } from '../../../new-sheet/sandbox-globals.js';
import { errorCheck } from '../../../new-sheet/wrappers/error-check.js';
function transpileTS(code) {
    const result = ts.transpileModule(code, {
        compilerOptions: {
            target: ts.ScriptTarget.ES2022,
            module: ts.ModuleKind.CommonJS,
            strict: false,
            esModuleInterop: true,
            allowJs: true
        }
    });
    return result.outputText;
}
function createCapturedConsole() {
    const logs = [];
    const push = (...args) => {
        logs.push(args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' '));
    };
    const noop = () => { };
    const console = {
        log: push,
        info: push,
        warn: push,
        error: push,
        debug: noop,
        trace: noop,
        dir: noop,
        table: noop,
        time: noop,
        timeEnd: noop,
        timeLog: noop,
        clear: noop,
        count: noop,
        countReset: noop,
        group: noop,
        groupEnd: noop,
        groupCollapsed: noop,
        assert: noop
    };
    return { console, getLogs: () => logs };
}
const TOOL_DESCRIPTION = `\
Execute TypeScript code against the MOG spreadsheet engine. All spreadsheet interactions go through this tool.
You MUST create a new workbook or open an existing one before doing anything else.
Globals: \`workbook\`, \`worksheet\`, \`console.log()\`, \`readFile(path)\`, \`writeFile(path, data)\`. All methods are async — use await.
Use readFile/writeFile to bridge data between bash and execute_code (e.g. generate formulas in Python, write to /tmp/formulas.json, then readFile in execute_code).

### Code Discipline
- Keep each code block under 100 lines
- Use console.log() to return values
- Always use batched APIs over loops for batch reads and writes
- Stop retrying after 2 attempts for unexplained failures
`;
const schema = Type.Object({
    description: Type.String({
        description: 'Short description of what the code does for non-technical users. You MUST reference ranges for any read / write operation. Use this notation for reads and writes, including the brackets: <Workbook:Sheet!Range>. Omit range if operating on the whole sheet or workbook.'
    }),
    code: Type.String({
        description: 'TypeScript code to execute. `workbook` and `worksheet` are already available. Use console.log() to return values. All API methods are async — use await.'
    })
});
export function createSheetExecTool(pool) {
    const assertSpreadsheetPolicy = (ctx) => {
        const spreadsheetPolicy = ctx?.permissionPolicy?.spreadsheet;
        if (!spreadsheetPolicy || spreadsheetPolicy.mutationBehavior !== 'deny') {
            return;
        }
        throw new Error('Spreadsheet execution is denied by the current permission policy.');
    };
    return {
        name: EXECUTE_CODE,
        label: 'Sheet exec',
        description: TOOL_DESCRIPTION,
        parameters: schema,
        async execute(_toolCallId, { code, description: _description }, _signal, _onUpdate, ctx) {
            assertSpreadsheetPolicy(ctx);
            if (!code) {
                const msg = 'No code provided — tool call had empty arguments (likely a truncated LLM response).';
                return {
                    content: [{ type: 'text', text: msg }],
                    details: { ok: false, error: msg }
                };
            }
            let jsCode;
            try {
                jsCode = transpileTS(code);
            }
            catch (err) {
                const msg = `TypeScript compilation error: ${err instanceof Error ? err.message : String(err)}`;
                return {
                    content: [{ type: 'text', text: msg }],
                    details: { ok: false, error: msg }
                };
            }
            const wrappedCode = `(async () => {\n${jsCode}\n})()`;
            const { console: sandboxConsole, getLogs } = createCapturedConsole();
            const tracker = new MutationTracker();
            const sandbox = buildSandbox({ pool, tracker, console: sandboxConsole });
            const context = vm.createContext(sandbox);
            let output;
            let error;
            try {
                const script = new vm.Script(wrappedCode, { filename: 'agent-code.js' });
                output = await script.runInContext(context, { timeout: 30_000 });
            }
            catch (err) {
                error = err instanceof Error ? err.message : String(err);
            }
            const cellEdits = await tracker.resolve();
            const editRanges = cellEdits.map((e) => `${e.sheet}!${e.address}`);
            const logs = getLogs();
            const parts = [];
            if (logs.length > 0)
                parts.push(logs.join('\n'));
            const cellDiffSummary = getDirtyCellSummary(cellEdits);
            if (cellDiffSummary)
                parts.push(cellDiffSummary);
            if (cellEdits.length > 0 && !error && pool.activeWorkbookName) {
                try {
                    const modifiedSheets = [...new Set(cellEdits.map((e) => e.sheet))];
                    const wb = pool.getWorkbook(pool.activeWorkbookName);
                    const errorResult = await errorCheck(wb, modifiedSheets);
                    if (errorResult !== 'No issues found.') {
                        parts.push(`⚠ Auto-check: ${errorResult}`);
                    }
                }
                catch {
                    // Silently ignore errorCheck failures
                }
            }
            if (parts.length === 0 && output != null) {
                parts.push(String(output));
            }
            const contentText = parts.join('\n\n') || '(no output)';
            if (error) {
                const fullText = cellDiffSummary ? `${error}\n\n${cellDiffSummary}` : error;
                return {
                    content: [{ type: 'text', text: fullText }],
                    details: {
                        ok: false,
                        error,
                        logs,
                        change_count: cellEdits.length,
                        edit_ranges: editRanges,
                        dirty_cells: cellEdits
                    }
                };
            }
            return {
                content: [{ type: 'text', text: truncateOutput(contentText) }],
                details: {
                    ok: true,
                    output: contentText,
                    logs,
                    change_count: cellEdits.length,
                    edit_ranges: editRanges,
                    dirty_cells: cellEdits
                }
            };
        }
    };
}
//# sourceMappingURL=mog.js.map