/**
 * Export Detection
 *
 * Determines whether a symbol (function, class, etc.) is exported/public
 * in its language. This is a pure function — safe for use in worker threads.
 *
 * Shared between parse-worker.ts (worker pool) and parsing-processor.ts (sequential fallback).
 */
import { findSiblingChild } from './utils/ast-helpers.js';
// ============================================================================
// Per-language export checkers
// ============================================================================
/** JS/TS: walk ancestors looking for export_statement or export_specifier. */
export const tsExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        const type = current.type;
        if (type === 'export_statement' ||
            type === 'export_specifier' ||
            (type === 'lexical_declaration' && current.parent?.type === 'export_statement')) {
            return true;
        }
        // Fallback: check if node text starts with 'export ' for edge cases
        if (current.text?.startsWith('export ')) {
            return true;
        }
        current = current.parent;
    }
    return false;
};
/** Python: public if no leading underscore (convention). */
export const pythonExportChecker = (_node, name) => !name.startsWith('_');
/** Java: check for 'public' modifier — modifiers are siblings of the name node, not parents. */
export const javaExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        if (current.parent) {
            const parent = current.parent;
            for (let i = 0; i < parent.childCount; i++) {
                const child = parent.child(i);
                if (child?.type === 'modifiers' && child.text?.includes('public')) {
                    return true;
                }
            }
            if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
                if (parent.text?.trimStart().startsWith('public')) {
                    return true;
                }
            }
        }
        current = current.parent;
    }
    return false;
};
/** C# declaration node types for sibling modifier scanning. */
const CSHARP_DECL_TYPES = new Set([
    'method_declaration',
    'local_function_statement',
    'constructor_declaration',
    'class_declaration',
    'interface_declaration',
    'struct_declaration',
    'enum_declaration',
    'record_declaration',
    // tree-sitter-c-sharp absorbs 'record struct' and 'record class' into
    // record_declaration — these two node types are listed defensively but
    // never emitted by the grammar in practice (verified against ^0.23.1).
    'record_struct_declaration',
    'record_class_declaration',
    'delegate_declaration',
    'property_declaration',
    'field_declaration',
    'event_declaration',
    'namespace_declaration',
    'file_scoped_namespace_declaration',
]);
/**
 * C#: modifier nodes are SIBLINGS of the name node inside the declaration.
 * Walk up to the declaration node, then scan its direct children.
 */
export const csharpExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        if (CSHARP_DECL_TYPES.has(current.type)) {
            for (let i = 0; i < current.childCount; i++) {
                const child = current.child(i);
                if (child?.type === 'modifier' && child.text === 'public')
                    return true;
            }
            return false;
        }
        current = current.parent;
    }
    return false;
};
/** Go: uppercase first letter = exported. */
export const goExportChecker = (_node, name) => {
    if (name.length === 0)
        return false;
    const first = name[0];
    return first === first.toUpperCase() && first !== first.toLowerCase();
};
/** Rust declaration node types for sibling visibility_modifier scanning. */
const RUST_DECL_TYPES = new Set([
    'function_item',
    'struct_item',
    'enum_item',
    'trait_item',
    'impl_item',
    'union_item',
    'type_item',
    'const_item',
    'static_item',
    'mod_item',
    'use_declaration',
    'associated_type',
    'function_signature_item',
]);
/**
 * Rust: visibility_modifier is a SIBLING of the name node within the declaration node
 * (function_item, struct_item, etc.), not a parent. Walk up to the declaration node,
 * then scan its direct children.
 */
export const rustExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        if (RUST_DECL_TYPES.has(current.type)) {
            for (let i = 0; i < current.childCount; i++) {
                const child = current.child(i);
                if (child?.type === 'visibility_modifier' && child.text?.startsWith('pub'))
                    return true;
            }
            return false;
        }
        current = current.parent;
    }
    return false;
};
/**
 * Kotlin: default visibility is public (unlike Java).
 * visibility_modifier is inside modifiers, a sibling of the name node within the declaration.
 */
export const kotlinExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        if (current.parent) {
            const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
            if (visMod) {
                const text = visMod.text;
                if (text === 'private' || text === 'internal' || text === 'protected')
                    return false;
                if (text === 'public')
                    return true;
            }
        }
        current = current.parent;
    }
    // No visibility modifier = public (Kotlin default)
    return true;
};
/**
 * C/C++: functions without 'static' storage class have external linkage by default,
 * making them globally accessible (equivalent to exported). Only functions explicitly
 * marked 'static' are file-scoped (not exported). C++ anonymous namespaces
 * (namespace { ... }) also give internal linkage.
 */
export const cCppExportChecker = (node, _name) => {
    let cur = node;
    while (cur) {
        if (cur.type === 'function_definition' || cur.type === 'declaration') {
            // Check for 'static' storage class specifier as a direct child node.
            // This avoids reading the full function text (which can be very large).
            for (let i = 0; i < cur.childCount; i++) {
                const child = cur.child(i);
                if (child?.type === 'storage_class_specifier' && child.text === 'static')
                    return false;
            }
        }
        // C++ anonymous namespace: namespace_definition with no name child = internal linkage
        if (cur.type === 'namespace_definition') {
            const hasName = cur.childForFieldName?.('name');
            if (!hasName)
                return false;
        }
        cur = cur.parent;
    }
    return true; // Top-level C/C++ functions default to external linkage
};
/** PHP: check for visibility modifier or top-level scope. */
export const phpExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        if (current.type === 'class_declaration' ||
            current.type === 'interface_declaration' ||
            current.type === 'trait_declaration' ||
            current.type === 'enum_declaration') {
            return true;
        }
        if (current.type === 'visibility_modifier') {
            return current.text === 'public';
        }
        current = current.parent;
    }
    // Top-level functions are globally accessible
    return true;
};
/**
 * Swift: treat symbols as exported unless explicitly marked private/fileprivate.
 *
 * Swift's default access level is `internal`, which means visible to all files
 * in the same module/target. Since CodraGraph indexes at the target level,
 * `internal` symbols should be treated as exported (cross-file visible).
 * Only `private` and `fileprivate` symbols are truly file-scoped.
 */
export const swiftExportChecker = (node, _name) => {
    let current = node;
    while (current) {
        if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
            const text = current.text || '';
            // Exclude private(set)/fileprivate(set) — only the setter is restricted,
            // the symbol itself is still readable cross-file.
            if (/\b(private|fileprivate)\b(?!\s*\()/.test(text))
                return false;
        }
        current = current.parent;
    }
    // Default (internal), public, and open are all cross-file visible
    return true;
};
/** Ruby: all top-level definitions are public (no export syntax). */
export const rubyExportChecker = (_node, _name) => true;
/** Dart: public if no leading underscore (convention, same as Python). */
export const dartExportChecker = (_node, name) => !name.startsWith('_');
