// codragraph/src/core/ingestion/variable-extractors/configs/python.ts
import { SupportedLanguages } from '../../../../_shared/index.js';
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
/**
 * Python variable extraction config.
 *
 * Handles module-level assignments and annotated assignments:
 * - `MAX_SIZE = 100` → const by UPPER_CASE convention
 * - `name: str = "default"` → annotated assignment with type
 * - `_private_var = 42` → protected by convention
 * - `__private_var = 42` → private by convention
 *
 * tree-sitter-python uses:
 * - expression_statement containing assignment or type nodes
 */
function extractNameFromPython(node) {
    const inner = node.firstNamedChild;
    if (!inner)
        return undefined;
    // Annotated assignment: name: str = "default"
    // AST: expression_statement > type > identifier
    if (inner.type === 'type') {
        const ident = inner.childForFieldName('name') ?? inner.firstNamedChild;
        return ident?.type === 'identifier' ? ident.text : undefined;
    }
    // Plain assignment: x = 5
    if (inner.type === 'assignment') {
        const left = inner.childForFieldName('left');
        if (left?.type === 'identifier')
            return left.text;
    }
    return undefined;
}
function extractTypeFromPython(node) {
    const inner = node.firstNamedChild;
    if (!inner)
        return undefined;
    // Standalone annotated type without assignment: `name: str`
    if (inner.type === 'type') {
        const typeNode = inner.childForFieldName('type') ?? inner.namedChild(1);
        if (typeNode)
            return extractSimpleTypeName(typeNode) ?? typeNode.text?.trim();
    }
    // Annotated assignment: `name: str = "hello"`
    // AST: expression_statement > assignment > [identifier, type > identifier, ...]
    if (inner.type === 'assignment') {
        for (let i = 0; i < inner.childCount; i++) {
            const child = inner.child(i);
            if (child?.type === 'type') {
                const typeId = child.firstNamedChild;
                if (typeId)
                    return extractSimpleTypeName(typeId) ?? typeId.text?.trim();
            }
        }
    }
    return undefined;
}
function extractVisFromPython(node) {
    const name = extractNameFromPython(node);
    if (!name)
        return 'public';
    // Dunder names (__name__, __all__) are public Python conventions
    if (name.startsWith('__') && name.endsWith('__'))
        return 'public';
    // Double underscore prefix (name mangled) = private
    if (name.startsWith('__'))
        return 'private';
    // Single underscore prefix = protected by convention
    if (name.startsWith('_'))
        return 'protected';
    return 'public';
}
export const pythonVariableConfig = {
    language: SupportedLanguages.Python,
    constNodeTypes: [],
    staticNodeTypes: [],
    // expression_statement is broad — isVariableDeclaration returns true for
    // all expression_statement nodes, but extract() safely filters non-assignments
    // by returning null when extractNameFromPython finds no assignment target.
    variableNodeTypes: ['expression_statement'],
    extractName: extractNameFromPython,
    extractType: extractTypeFromPython,
    extractVisibility: extractVisFromPython,
    isConst(node) {
        // Python convention: UPPER_CASE names are constants
        const name = extractNameFromPython(node);
        if (!name)
            return false;
        return name === name.toUpperCase() && /^[A-Z][A-Z0-9_]*$/.test(name);
    },
    isStatic(_node) {
        return false;
    },
    isMutable(node) {
        const name = extractNameFromPython(node);
        if (!name)
            return true;
        // By convention, UPPER_CASE names are immutable constants
        return !(name === name.toUpperCase() && /^[A-Z][A-Z0-9_]*$/.test(name));
    },
};
