import { extractModifierVisibility, findChild, nodeEndLine } from './helpers.js';
/**
 * Extract symbols from Scala files.
 */
export function extractScalaSymbols(tree, _filePath) {
    const ctx = {
        definitions: [],
        calls: [],
        imports: [],
        classes: [],
        exports: [],
        typeMap: new Map(),
    };
    walkScalaNode(tree.rootNode, ctx);
    return ctx;
}
function walkScalaNode(node, ctx) {
    switch (node.type) {
        case 'class_definition':
            handleScalaClassDef(node, ctx);
            break;
        case 'trait_definition':
            handleScalaTraitDef(node, ctx);
            break;
        case 'object_definition':
            handleScalaObjectDef(node, ctx);
            break;
        case 'function_definition':
            handleScalaFunctionDef(node, ctx);
            break;
        case 'import_declaration':
            handleScalaImportDecl(node, ctx);
            break;
        case 'call_expression':
            handleScalaCallExpression(node, ctx);
            break;
        case 'val_definition':
        case 'var_definition':
            handleScalaValVarDef(node, ctx);
            break;
    }
    for (let i = 0; i < node.childCount; i++) {
        const child = node.child(i);
        if (child)
            walkScalaNode(child, ctx);
    }
}
// ── Walk-path per-node-type handlers ────────────────────────────────────────
function handleScalaClassDef(node, ctx) {
    emitScalaTypeDef(node, ctx, 'class');
}
function handleScalaTraitDef(node, ctx) {
    emitScalaTypeDef(node, ctx, 'interface');
}
function handleScalaObjectDef(node, ctx) {
    emitScalaTypeDef(node, ctx, 'class');
}
function emitScalaTypeDef(node, ctx, kind) {
    const nameNode = node.childForFieldName('name');
    if (!nameNode)
        return;
    const name = nameNode.text;
    const { children, methods } = collectScalaBodyMembers(node, name);
    // Push the type def before its methods so the definition order follows
    // source-line order (matches native, which walks depth-first).
    ctx.definitions.push({
        name,
        kind,
        line: node.startPosition.row + 1,
        endLine: nodeEndLine(node),
        children: children.length > 0 ? children : undefined,
    });
    for (const m of methods)
        ctx.definitions.push(m);
    extractScalaInheritance(node, name, ctx);
}
function handleScalaFunctionDef(node, ctx) {
    // Skip methods already emitted by class/trait/object handlers
    if (node.parent?.type === 'template_body') {
        const grandparent = node.parent.parent;
        if (grandparent &&
            (grandparent.type === 'class_definition' ||
                grandparent.type === 'trait_definition' ||
                grandparent.type === 'object_definition')) {
            return;
        }
    }
    const nameNode = node.childForFieldName('name');
    if (!nameNode)
        return;
    const params = extractScalaParameters(node);
    ctx.definitions.push({
        name: nameNode.text,
        kind: 'function',
        line: node.startPosition.row + 1,
        endLine: nodeEndLine(node),
        children: params.length > 0 ? params : undefined,
        visibility: extractModifierVisibility(node),
    });
}
function handleScalaImportDecl(node, ctx) {
    // import_declaration has alternating `identifier` and `.` children directly (NO import_expression wrapper)
    const parts = [];
    for (let i = 0; i < node.childCount; i++) {
        const child = node.child(i);
        if (!child)
            continue;
        if (child.type === 'identifier' || child.type === 'type_identifier') {
            parts.push(child.text);
        }
    }
    if (parts.length === 0)
        return;
    const fullPath = parts.join('.');
    const lastName = parts[parts.length - 1] ?? fullPath;
    ctx.imports.push({
        source: fullPath,
        names: [lastName],
        line: node.startPosition.row + 1,
        scalaImport: true,
    });
}
function handleScalaCallExpression(node, ctx) {
    const funcNode = node.childForFieldName('function');
    if (!funcNode)
        return;
    const call = { name: '', line: node.startPosition.row + 1 };
    if (funcNode.type === 'field_expression') {
        const field = funcNode.childForFieldName('field');
        const value = funcNode.childForFieldName('value');
        if (field)
            call.name = field.text;
        if (value)
            call.receiver = value.text;
    }
    else {
        call.name = funcNode.text;
    }
    if (call.name)
        ctx.calls.push(call);
}
function handleScalaValVarDef(node, ctx) {
    // Only handle top-level vals/vars — skip class members and function-local bindings
    if (node.parent?.type === 'template_body')
        return;
    if (node.parent?.type === 'block' || node.parent?.type === 'indented_block')
        return;
    const pattern = node.childForFieldName('pattern');
    if (!pattern)
        return;
    const nameNode = pattern.type === 'identifier' ? pattern : findChild(pattern, 'identifier');
    if (!nameNode)
        return;
    const kind = node.type === 'val_definition' ? 'constant' : 'variable';
    ctx.definitions.push({
        name: nameNode.text,
        kind,
        line: node.startPosition.row + 1,
        endLine: nodeEndLine(node),
    });
}
// ── Inheritance helpers ─────────────────────────────────────────────────────
function extractScalaInheritance(node, name, ctx) {
    const extendsClause = findChild(node, 'extends_clause');
    if (!extendsClause)
        return;
    let foundExtends = false;
    for (let i = 0; i < extendsClause.childCount; i++) {
        const child = extendsClause.child(i);
        if (!child)
            continue;
        if (child.type === 'type_identifier' ||
            child.type === 'generic_type' ||
            child.type === 'identifier') {
            const typeName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
            if (!typeName)
                continue;
            if (!foundExtends) {
                ctx.classes.push({
                    name,
                    extends: typeName,
                    line: node.startPosition.row + 1,
                });
                foundExtends = true;
            }
            else {
                ctx.classes.push({
                    name,
                    implements: typeName,
                    line: node.startPosition.row + 1,
                });
            }
        }
    }
}
// ── Body member extraction ──────────────────────────────────────────────────
function collectScalaBodyMembers(parentNode, parentName) {
    const children = [];
    const methods = [];
    const body = findChild(parentNode, 'template_body');
    if (!body)
        return { children, methods };
    for (let i = 0; i < body.childCount; i++) {
        const member = body.child(i);
        if (!member)
            continue;
        if (member.type === 'function_definition') {
            const methName = member.childForFieldName('name');
            if (methName) {
                const params = extractScalaParameters(member);
                methods.push({
                    name: `${parentName}.${methName.text}`,
                    kind: 'method',
                    line: member.startPosition.row + 1,
                    endLine: member.endPosition.row + 1,
                    visibility: extractModifierVisibility(member),
                    children: params.length > 0 ? params : undefined,
                });
            }
        }
        else if (member.type === 'val_definition' || member.type === 'var_definition') {
            const pattern = member.childForFieldName('pattern');
            if (pattern) {
                const nameNode = pattern.type === 'identifier' ? pattern : findChild(pattern, 'identifier');
                if (nameNode) {
                    children.push({
                        name: nameNode.text,
                        kind: 'property',
                        line: member.startPosition.row + 1,
                        visibility: extractModifierVisibility(member),
                    });
                }
            }
        }
    }
    return { children, methods };
}
// ── Parameter extraction ────────────────────────────────────────────────────
function extractScalaParameters(funcNode) {
    const params = [];
    const paramList = findChild(funcNode, 'parameters');
    if (!paramList)
        return params;
    for (let i = 0; i < paramList.childCount; i++) {
        const param = paramList.child(i);
        if (param?.type !== 'parameter')
            continue;
        const nameNode = findChild(param, 'identifier');
        if (nameNode) {
            params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
        }
    }
    return params;
}
//# sourceMappingURL=scala.js.map