// engine/code-transforms/typescript/extract-interface.ts — Extract interface (AC-06).
// Extracts an interface from a class declaration and adds implements clause.
import { getTypeScript, parseSource, getLineNumber, createChange } from './ast-utils.js';
function collectMembers(ts, node, sourceFile) {
    const members = [];
    for (const member of node.members) {
        // Skip private members
        /* v8 ignore start -- canHaveModifiers is true for all property/method class members */
        const mods = ts.canHaveModifiers(member) ? ts.getModifiers(member) : undefined;
        /* v8 ignore stop */
        if (mods?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword)) {
            continue;
        }
        if (ts.isPropertyDeclaration(member)) {
            const name = member.name.getText(sourceFile);
            const type = member.type ? member.type.getText(sourceFile) : 'unknown';
            members.push({ name, signature: `${name}: ${type}` });
        }
        if (ts.isMethodDeclaration(member)) {
            const name = member.name.getText(sourceFile);
            const params = member.parameters
                .map((p) => {
                const pName = p.name.getText(sourceFile);
                const pType = p.type ? p.type.getText(sourceFile) : 'unknown';
                return `${pName}: ${pType}`;
            })
                .join(', ');
            const retType = member.type ? member.type.getText(sourceFile) : 'void';
            members.push({ name, signature: `${name}(${params}): ${retType}` });
        }
    }
    return members;
}
function addImplementsClause(ts, classNode, sourceFile, source, interfaceName) {
    // Find the position of the opening brace
    const openBrace = source.indexOf('{', classNode.getStart(sourceFile));
    /* v8 ignore start -- defensive: class should always have { */
    if (openBrace < 0) {
        return source;
    }
    /* v8 ignore stop */
    // Check if class already has an implements clause
    if (classNode.heritageClauses?.some((h) => h.token === ts.SyntaxKind.ImplementsKeyword)) {
        // Append to existing implements
        const impl = classNode.heritageClauses.find((h) => h.token === ts.SyntaxKind.ImplementsKeyword);
        /* v8 ignore start -- impl is always found since .some() above confirmed it exists */
        if (impl) {
            /* v8 ignore stop */
            const end = impl.getEnd();
            return source.slice(0, end) + `, ${interfaceName}` + source.slice(end);
        }
    }
    // Insert implements before opening brace
    return source.slice(0, openBrace) + `implements ${interfaceName} ` + source.slice(openBrace);
}
/**
 * Extract an interface from a class declaration.
 */
export function extractInterface(source, options, fileName) {
    const ts = getTypeScript();
    const sourceFile = parseSource(source, fileName);
    const changes = [];
    // Find the target class
    let targetClass;
    function visit(node) {
        if (ts.isClassDeclaration(node) && node.name?.getText(sourceFile) === options.className) {
            targetClass = node;
        }
        ts.forEachChild(node, visit);
    }
    visit(sourceFile);
    if (!targetClass) {
        return { transformed: source, changes: [] };
    }
    const members = collectMembers(ts, targetClass, sourceFile);
    if (members.length === 0) {
        return { transformed: source, changes: [] };
    }
    const ifaceName = options.interfaceName ?? `I${options.className}`;
    const memberLines = members.map((m) => `  ${m.signature};`).join('\n');
    const interfaceDecl = `interface ${ifaceName} {\n${memberLines}\n}\n\n`;
    // Insert interface before the class
    const classStart = targetClass.getStart(sourceFile);
    let result = source.slice(0, classStart) + interfaceDecl + source.slice(classStart);
    // Re-parse and add implements clause
    const newSourceFile = parseSource(result, fileName);
    let newClassNode;
    function visitNew(node) {
        if (ts.isClassDeclaration(node) && node.name?.getText(newSourceFile) === options.className) {
            newClassNode = node;
        }
        ts.forEachChild(node, visitNew);
    }
    visitNew(newSourceFile);
    if (newClassNode) {
        result = addImplementsClause(ts, newClassNode, newSourceFile, result, ifaceName);
    }
    const line = getLineNumber(sourceFile, classStart);
    changes.push(createChange(line, `Extracted interface '${ifaceName}' from class '${options.className}'`));
    changes.push(createChange(line, `Added 'implements ${ifaceName}' to class '${options.className}'`));
    return { transformed: result, changes };
}
//# sourceMappingURL=extract-interface.js.map