// codragraph/src/core/ingestion/field-extractors/configs/csharp.ts
import { SupportedLanguages } from '../../../../_shared/index.js';
import { findVisibility, hasKeyword, hasModifier, collectModifierTexts } from './helpers.js';
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
const CSHARP_VIS = new Set(['public', 'private', 'protected', 'internal']);
/**
 * C# field extraction config.
 *
 * Handles field_declaration and property_declaration inside class/struct/interface bodies.
 * The body node in tree-sitter-c-sharp is 'declaration_list'.
 */
export const csharpConfig = {
    language: SupportedLanguages.CSharp,
    typeDeclarationNodes: [
        'class_declaration',
        'struct_declaration',
        'interface_declaration',
        'record_declaration',
    ],
    fieldNodeTypes: ['field_declaration', 'property_declaration'],
    bodyNodeTypes: ['declaration_list'],
    defaultVisibility: 'private',
    extractName(node) {
        // field_declaration > variable_declaration > variable_declarator > identifier
        for (let i = 0; i < node.namedChildCount; i++) {
            const child = node.namedChild(i);
            if (child?.type === 'variable_declaration') {
                for (let j = 0; j < child.namedChildCount; j++) {
                    const declarator = child.namedChild(j);
                    if (declarator?.type === 'variable_declarator') {
                        const name = declarator.childForFieldName('name');
                        return name?.text ?? declarator.firstNamedChild?.text;
                    }
                }
            }
        }
        // property_declaration: name field
        const nameNode = node.childForFieldName('name');
        if (nameNode)
            return nameNode.text;
        return undefined;
    },
    extractType(node) {
        // field_declaration > variable_declaration > type:(predefined_type | identifier | ...)
        for (let i = 0; i < node.namedChildCount; i++) {
            const child = node.namedChild(i);
            if (child?.type === 'variable_declaration') {
                const typeNode = child.childForFieldName('type');
                if (typeNode)
                    return extractSimpleTypeName(typeNode) ?? typeNode.text?.trim();
                // fallback: first child that is a type
                const first = child.firstNamedChild;
                if (first && first.type !== 'variable_declarator') {
                    return extractSimpleTypeName(first) ?? first.text?.trim();
                }
            }
        }
        // property_declaration: type is first named child
        const typeNode = node.childForFieldName('type');
        if (typeNode)
            return extractSimpleTypeName(typeNode) ?? typeNode.text?.trim();
        return undefined;
    },
    extractVisibility(node) {
        // Detect compound C# visibilities: protected internal, private protected
        const mods = collectModifierTexts(node);
        if (mods.has('protected') && mods.has('internal'))
            return 'protected internal';
        if (mods.has('private') && mods.has('protected'))
            return 'private protected';
        return findVisibility(node, CSHARP_VIS, 'private', 'modifier');
    },
    isStatic(node) {
        return hasKeyword(node, 'static') || hasModifier(node, 'modifier', 'static');
    },
    isReadonly(node) {
        return hasKeyword(node, 'readonly') || hasModifier(node, 'modifier', 'readonly');
    },
    extractPrimaryFields(ownerNode, context) {
        // C# record positional parameters become public init-only properties.
        // C# 12 class primary constructor parameters are captured as private fields.
        // The parameter_list has no field name — find it by type.
        let paramList = null;
        for (let i = 0; i < ownerNode.namedChildCount; i++) {
            const child = ownerNode.namedChild(i);
            if (child?.type === 'parameter_list') {
                paramList = child;
                break;
            }
        }
        if (!paramList)
            return [];
        const isRecord = ownerNode.type === 'record_declaration';
        const fields = [];
        for (let i = 0; i < paramList.namedChildCount; i++) {
            const param = paramList.namedChild(i);
            if (!param || param.type !== 'parameter')
                continue;
            const nameNode = param.childForFieldName('name');
            const typeNode = param.childForFieldName('type');
            if (!nameNode)
                continue;
            fields.push({
                name: nameNode.text,
                type: typeNode ? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim() ?? null) : null,
                // Record params are public init-only properties; class params are private captured fields
                visibility: isRecord ? 'public' : 'private',
                isStatic: false,
                isReadonly: isRecord, // record properties are init-only (readonly)
                sourceFile: context.filePath,
                line: param.startPosition.row + 1,
            });
        }
        return fields;
    },
};
