// engine/code-transforms/typescript/add-jsdoc.ts — Add JSDoc comments (AC-07).
// Generates JSDoc stubs with @param, @returns, @throws for functions.
import { getTypeScript, parseSource, getLineNumber, createChange } from './ast-utils.js';
function hasJSDoc(source, nodeStart) {
    // Check if there is already a JSDoc comment before this node
    const before = source.slice(Math.max(0, nodeStart - 200), nodeStart);
    return before.trimEnd().endsWith('*/');
}
function bodyHasThrow(ts, body) {
    if (ts.isThrowStatement(body)) {
        return true;
    }
    let found = false;
    ts.forEachChild(body, (child) => {
        if (bodyHasThrow(ts, child)) {
            found = true;
        }
    });
    return found;
}
function buildJSDoc(ts, node, sourceFile, options) {
    const lines = ['/**'];
    /* v8 ignore next 1 -- FunctionDeclaration without name is extremely rare in valid TS */
    const name = node.name ? node.name.getText(sourceFile) : 'anonymous';
    lines.push(` * ${name}`);
    if (options.includeParams !== false) {
        for (const param of node.parameters) {
            const pName = param.name.getText(sourceFile);
            const pType = param.type ? param.type.getText(sourceFile) : 'unknown';
            lines.push(` * @param {${pType}} ${pName}`);
        }
    }
    if (options.includeReturns !== false && node.type) {
        const retType = node.type.getText(sourceFile);
        if (retType !== 'void') {
            lines.push(` * @returns {${retType}}`);
        }
    }
    if (options.includeThrows !== false && node.body && bodyHasThrow(ts, node.body)) {
        lines.push(` * @throws {Error}`);
    }
    lines.push(' */');
    return lines.join('\n');
}
/**
 * Add JSDoc comments to functions and methods that don't have them.
 */
export function addJSDoc(source, options, fileName) {
    const ts = getTypeScript();
    const sourceFile = parseSource(source, fileName);
    const insertions = [];
    function visit(node) {
        if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
            const start = node.getStart(sourceFile);
            if (!hasJSDoc(source, start) && node.name) {
                const name = node.name.getText(sourceFile);
                const doc = buildJSDoc(ts, node, sourceFile, options);
                insertions.push({
                    position: start,
                    text: doc + '\n',
                    line: getLineNumber(sourceFile, start),
                    name,
                });
            }
        }
        ts.forEachChild(node, visit);
    }
    visit(sourceFile);
    if (insertions.length === 0) {
        return { transformed: source, changes: [] };
    }
    // Apply in reverse order
    insertions.sort((a, b) => b.position - a.position);
    let result = source;
    const changes = [];
    for (const ins of insertions) {
        result = result.slice(0, ins.position) + ins.text + result.slice(ins.position);
        changes.push(createChange(ins.line, `Added JSDoc to '${ins.name}'`));
    }
    changes.sort((a, b) => a.line - b.line);
    return { transformed: result, changes };
}
//# sourceMappingURL=add-jsdoc.js.map