/**
 * Java `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed by
 * the generic `runScopeResolution` orchestrator (RFC #909 Ring 3).
 *
 * ## Registry-primary parity status
 *
 * Java is in `MIGRATED_LANGUAGES` — the scope-resolution registry is
 * the primary call-resolution path.  Parity: 178/178 (100%).
 *
 * **CI visibility:** The parity CI workflow (`ci-scope-parity.yml`)
 * runs Java tests in both `REGISTRY_PRIMARY_JAVA=0` and `=1` modes
 * automatically.
 */
import { SupportedLanguages } from '../../../../_shared/index.js';
import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js';
import { resolveDefGraphId } from '../../scope-resolution/graph-bridge/ids.js';
import { isClassLike, lookupBindingsAt, namesAtScope, populateClassOwnedMembers, } from '../../scope-resolution/scope/walkers.js';
import { followChainPostFinalize } from '../../scope-resolution/passes/imported-return-types.js';
import { javaProvider } from '../java.js';
import { javaArityCompatibility, javaMergeBindings, resolveJavaImportTarget, } from './index.js';
import { populateJavaPackageSiblings } from './package-siblings.js';
const javaScopeResolver = {
    language: SupportedLanguages.Java,
    languageProvider: javaProvider,
    importEdgeReason: 'java-scope: import',
    resolveImportTarget: (targetRaw, fromFile, allFilePaths) => {
        const ws = { fromFile, allFilePaths };
        return resolveJavaImportTarget({ kind: 'named', localName: '_', importedName: '_', targetRaw }, ws);
    },
    mergeBindings: (existing, incoming) => [...javaMergeBindings([...existing, ...incoming])],
    arityCompatibility: (callsite, def) => javaArityCompatibility(def, callsite),
    buildMro: buildJavaMro,
    populateOwners: (parsed) => populateClassOwnedMembers(parsed),
    isSuperReceiver: (text) => text.trim() === 'super',
    fieldFallbackOnMethodLookup: false,
    propagatesReturnTypesAcrossImports: true,
    collapseMemberCallsByCallerTarget: true,
    hoistTypeBindingsToModule: true,
    populateNamespaceSiblings: populateJavaPackageSiblings,
    populateRangeBindings: populateJavaCrossFileReturnTypes,
};
export { javaScopeResolver };
function populateJavaCrossFileReturnTypes(parsedFiles, indexes) {
    const moduleScopeByFile = new Map();
    const classScopesByFile = new Map();
    for (const parsed of parsedFiles) {
        const ms = parsed.scopes.find((s) => s.kind === 'Module');
        if (ms !== undefined)
            moduleScopeByFile.set(parsed.filePath, ms);
        const cs = parsed.scopes.filter((s) => s.kind === 'Class');
        if (cs.length > 0)
            classScopesByFile.set(parsed.filePath, cs);
    }
    for (const parsed of parsedFiles) {
        const importerModule = moduleScopeByFile.get(parsed.filePath);
        if (importerModule === undefined)
            continue;
        const ambiguousMirrors = new Set();
        for (const name of namesAtScope(importerModule.id, indexes)) {
            const refs = lookupBindingsAt(importerModule.id, name, indexes);
            for (const ref of refs) {
                if (ref.origin !== 'import' && ref.origin !== 'reexport')
                    continue;
                if (!isClassLike(ref.def.type))
                    continue;
                const sourceModule = moduleScopeByFile.get(ref.def.filePath);
                if (sourceModule === undefined)
                    continue;
                const tb = importerModule.typeBindings;
                for (const [srcName, srcRef] of sourceModule.typeBindings) {
                    if (srcRef.source !== 'return-annotation')
                        continue;
                    if (ambiguousMirrors.has(srcName))
                        continue;
                    const existing = tb.get(srcName);
                    if (existing !== undefined && existing.rawName !== srcRef.rawName) {
                        ambiguousMirrors.add(srcName);
                        tb.delete(srcName);
                        continue;
                    }
                    if (existing === undefined)
                        tb.set(srcName, srcRef);
                }
                for (const classScope of classScopesByFile.get(ref.def.filePath) ?? []) {
                    for (const [srcName, srcRef] of classScope.typeBindings) {
                        if (srcRef.source === 'self' || srcRef.source === 'parameter-annotation')
                            continue;
                        if (ambiguousMirrors.has(srcName))
                            continue;
                        const existing = tb.get(srcName);
                        if (existing !== undefined && existing.rawName !== srcRef.rawName) {
                            ambiguousMirrors.add(srcName);
                            tb.delete(srcName);
                            continue;
                        }
                        if (existing === undefined)
                            tb.set(srcName, srcRef);
                    }
                }
            }
        }
        for (const [name, ref] of importerModule.typeBindings) {
            const resolved = followChainPostFinalize(ref, importerModule.id, indexes);
            if (resolved !== ref) {
                importerModule.typeBindings.set(name, resolved);
            }
        }
    }
    for (const parsed of parsedFiles) {
        const moduleScopeId = moduleScopeByFile.get(parsed.filePath)?.id;
        for (const scope of parsed.scopes) {
            if (scope.id === moduleScopeId)
                continue;
            for (const [name, ref] of scope.typeBindings) {
                const resolved = followChainPostFinalize(ref, scope.id, indexes);
                if (resolved !== ref) {
                    scope.typeBindings.set(name, resolved);
                }
            }
        }
    }
}
function buildJavaMro(graph, parsedFiles, nodeLookup) {
    const mro = buildMro(graph, parsedFiles, nodeLookup, defaultLinearize);
    const defIdByGraphId = new Map();
    for (const parsed of parsedFiles) {
        for (const def of parsed.localDefs) {
            if (!isClassLike(def.type))
                continue;
            const graphId = resolveDefGraphId(parsed.filePath, def, nodeLookup);
            if (graphId !== undefined)
                defIdByGraphId.set(graphId, def.nodeId);
        }
    }
    const directImpls = new Map();
    for (const rel of graph.iterRelationshipsByType('IMPLEMENTS')) {
        const source = defIdByGraphId.get(rel.sourceId);
        const target = defIdByGraphId.get(rel.targetId);
        if (source === undefined || target === undefined)
            continue;
        let list = directImpls.get(source);
        if (list === undefined) {
            list = [];
            directImpls.set(source, list);
        }
        if (!list.includes(target))
            list.push(target);
    }
    for (const [classDefId, extendsMro] of mro) {
        const ancestorChain = [classDefId, ...extendsMro];
        const seeds = [];
        for (const ancestorId of ancestorChain) {
            for (const ifaceId of directImpls.get(ancestorId) ?? []) {
                seeds.push(ifaceId);
            }
        }
        if (seeds.length === 0)
            continue;
        const interfaces = closeInterfaces(seeds, directImpls);
        mro.set(classDefId, [...extendsMro, ...interfaces.filter((i) => !extendsMro.includes(i))]);
    }
    for (const [classDefId, ifaces] of directImpls) {
        if (mro.has(classDefId))
            continue;
        mro.set(classDefId, closeInterfaces([...ifaces], directImpls));
    }
    return mro;
}
function closeInterfaces(seeds, directImpls) {
    const out = [];
    const seen = new Set();
    const queue = [...seeds];
    let head = 0;
    while (head < queue.length) {
        const cur = queue[head++];
        if (seen.has(cur))
            continue;
        seen.add(cur);
        out.push(cur);
        for (const next of directImpls.get(cur) ?? []) {
            if (!seen.has(next))
                queue.push(next);
        }
    }
    return out;
}
