// engine/code-transforms/typescript/add-error-handling.ts — (AC-04).
// Wraps async function bodies with try/catch blocks.
import { getTypeScript, parseSource, getLineNumber, createChange } from './ast-utils.js';
function isAsyncFunction(ts, node) {
    if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node)) {
        const mods = ts.getModifiers(node);
        return mods?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) === true;
    }
    return false;
}
function getNodeName(ts, node, sourceFile) {
    if ((ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && node.name) {
        return node.name.getText(sourceFile);
    }
    return '<anonymous>';
}
function buildCatchBlock(style, fnName) {
    switch (style) {
        case 'return-null':
            return [
                `catch (error: unknown) {`,
                `    console.error('Error in ${fnName}:', error);`,
                `    return null;`,
                `  }`,
            ].join('\n');
        case 'log-and-rethrow':
            return [
                `catch (error: unknown) {`,
                `    console.error('Error in ${fnName}:', error);`,
                `    throw error;`,
                `  }`,
            ].join('\n');
        default: // 'rethrow'
            return [`catch (error: unknown) {`, `    throw error;`, `  }`].join('\n');
    }
}
function hasExistingTryCatch(ts, body) {
    for (const stmt of body.statements) {
        if (ts.isTryStatement(stmt)) {
            return true;
        }
    }
    return false;
}
/**
 * Add try/catch error handling to async functions.
 */
export function addErrorHandling(source, options, fileName) {
    const ts = getTypeScript();
    const sourceFile = parseSource(source, fileName);
    const style = options.style ?? 'rethrow';
    const targets = [];
    function visit(node) {
        if (isAsyncFunction(ts, node)) {
            let body;
            if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
                body = node.body;
            }
            else if (ts.isArrowFunction(node) && ts.isBlock(node.body)) {
                body = node.body;
            }
            if (body && !hasExistingTryCatch(ts, body)) {
                const name = getNodeName(ts, node, sourceFile);
                targets.push({
                    name,
                    bodyStart: body.getStart(sourceFile) + 1,
                    bodyEnd: body.getEnd() - 1,
                    line: getLineNumber(sourceFile, node.getStart(sourceFile)),
                });
            }
        }
        ts.forEachChild(node, visit);
    }
    visit(sourceFile);
    if (targets.length === 0) {
        return { transformed: source, changes: [] };
    }
    // Apply in reverse order
    targets.sort((a, b) => b.bodyStart - a.bodyStart);
    let result = source;
    const changes = [];
    for (const target of targets) {
        const innerBody = result.slice(target.bodyStart, target.bodyEnd);
        const catchBlock = buildCatchBlock(style, target.name);
        const wrapped = `\n  try {${innerBody}} ${catchBlock}\n`;
        result = result.slice(0, target.bodyStart) + wrapped + result.slice(target.bodyEnd);
        changes.push(createChange(target.line, `Wrapped '${target.name}' with try/catch (${style})`));
    }
    changes.sort((a, b) => a.line - b.line);
    return { transformed: result, changes };
}
//# sourceMappingURL=add-error-handling.js.map