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 } from '../../scope-resolution/scope/walkers.js';
import { kotlinProvider } from '../kotlin.js';
import { kotlinArityCompatibility, kotlinMergeBindings, populateKotlinOwners, resolveKotlinImportTarget, } from './index.js';
import { clearCompanionScopes } from './companion-scopes.js';
import { isKotlinStaticOnly } from './owners.js';
/**
 * Kotlin scope resolver for RFC #909 Ring 3.
 *
 * **Migration status:** Kotlin is in `MIGRATED_LANGUAGES`. Default
 * production resolution flows through the scope-resolution pipeline;
 * the legacy DAG is consulted only when the per-language env var
 * (`REGISTRY_PRIMARY_KOTLIN=0`) explicitly forces the legacy parity
 * run for CI comparison.
 *
 * **Forced-mode parity (`REGISTRY_PRIMARY_KOTLIN=1`):** 208/208
 * fixtures pass after the migration sub-issues #1758–#1763, the
 * companion/instance dispatch fix #1756, and the lambda scopes
 * fix #1757. Covers core import, receiver, companion, default-param,
 * vararg, constructor, local assignment-chain, collection-iteration,
 * smart casts (`when (x) { is T -> … }` and `if (x is T)` — #1758),
 * cross-file iterable return propagation (#1759), single-level
 * method-chain fixpoint receiver types (#1760), parameter-type-narrowed
 * overload target-id selection (#1761), virtual dispatch via constructor
 * RHS (`val x: Animal = Dog()` — #1762), interface default-method
 * dispatch via implements-split MRO (#1763), companion-object vs
 * instance member dispatch (#1756) via the `isStaticOnly` hook
 * (including named companions and MRO-shadow / chain-typebinding /
 * value-receiver crossover cases), and lambda-body Block scopes
 * with scoped type-bindings for explicit parameters and implicit
 * `it` (#1757) via `synthesizeKotlinLambdaBindings` plus the
 * `(lambda_literal) @scope.block` query rule.
 *
 * **Legacy parity skip list:** `LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.kotlin`
 * in `test/integration/resolvers/helpers.ts` records scope-resolver-only
 * correctness wins that the legacy DAG cannot replicate. As of #1756 /
 * #1757 there are 8 entries covering: the bare companion-vs-instance
 * crossover, three MRO-shadow / standalone-chain cases, the chained-
 * forEach lambda-scope case, the named-companion crossover, and the
 * Case-0 / Case-3b / Case-5 companion crossovers under
 * `kotlin-companion-other-cases`. Each entry is documented inline with
 * its issue ref and rationale.
 */
export const kotlinScopeResolver = {
    language: SupportedLanguages.Kotlin,
    languageProvider: kotlinProvider,
    importEdgeReason: 'kotlin-scope: import',
    loadResolutionConfig: () => {
        // Drop the module-level `companionScopesByFile` table from any
        // prior workspace pass before this run populates it via
        // `emitKotlinScopeCaptures`. Mirrors the C resolver's
        // `clearStaticNames()` call in `loadResolutionConfig` — the
        // orchestrator awaits this hook exactly once per workspace pass
        // (see `pipeline/phase.ts`), making it the right lifecycle seam
        // for clearing per-language side-channel state. Returns
        // `undefined` because Kotlin has no external resolution config
        // to load.
        clearCompanionScopes();
        return undefined;
    },
    resolveImportTarget: (targetRaw, fromFile, allFilePaths) => {
        const ws = { fromFile, allFilePaths };
        return resolveKotlinImportTarget({ kind: 'named', localName: '_', importedName: '_', targetRaw }, ws);
    },
    mergeBindings: (existing, incoming) => [...kotlinMergeBindings([...existing, ...incoming])],
    arityCompatibility: (callsite, def) => kotlinArityCompatibility(def, callsite),
    buildMro: (graph, parsedFiles, nodeLookup) => buildKotlinMro(graph, parsedFiles, nodeLookup),
    populateOwners: (parsed) => populateKotlinOwners(parsed),
    isSuperReceiver: (text) => text.trim() === 'super',
    isStaticOnly: isKotlinStaticOnly,
    fieldFallbackOnMethodLookup: false,
    propagatesReturnTypesAcrossImports: true,
    collapseMemberCallsByCallerTarget: false,
    hoistTypeBindingsToModule: true,
};
/**
 * Kotlin MRO builder — extends `defaultLinearize` (EXTENDS-only) with
 * interface ancestors discovered via `IMPLEMENTS` edges. Interface
 * default methods (`interface Validator { fun validate(): Boolean = true }`)
 * are inherited by implementing classes without an explicit override;
 * the generic MRO would not surface them because the implementor has
 * no `EXTENDS` link to the interface (#1763).
 *
 * Interfaces are appended after the EXTENDS chain (Kotlin resolves
 * conflicts by requiring an explicit override, so first-seen-in-MRO
 * ordering is a reasonable approximation for method lookup). Transitive
 * interface inheritance (`interface A : B`) is closed via BFS.
 */
function buildKotlinMro(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);
        }
    }
    // Direct IMPLEMENTS targets per class-like def.
    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 each class, append the transitive closure of interfaces reachable
    // through its own + ancestor classes' IMPLEMENTS edges. Walking
    // ancestors picks up interfaces inherited via the EXTENDS chain
    // (e.g. `class C : B; class B : A; interface A` — C inherits A's
    // interface methods through B).
    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))]);
    }
    // Classes with no EXTENDS still need an MRO entry when they implement
    // interfaces (e.g. `class User : Validator` — no `mro` entry from the
    // EXTENDS-only pass because no EXTENDS edges exist).
    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];
    while (queue.length > 0) {
        const cur = queue.shift();
        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;
}
