/**
 * `resolveTypeRef` — strict single-return resolver for `TypeRef`s
 * (RFC §4.6; Ring 2 SHARED #916).
 *
 * Narrower contract than `Registry.lookup`: no name-only global fallback, no
 * confidence ranking, no arity check. Used by `Registry.lookup` Step 2 (type-
 * binding propagation) and by any caller that wants the single best type-
 * target for an annotation without paying for the full evidence pipeline.
 *
 * **Algorithm (strict).** Walk the scope chain from `ref.declaredAtScope`:
 *
 *   1. At each scope, inspect `bindings.get(ref.rawName)`:
 *      - If one of the bindings is a **type-kind** def with a **strict origin**
 *        (`'local' | 'import' | 'namespace' | 'reexport'`), return it.
 *      - If any binding for this name exists at this scope but none qualifies
 *        (e.g., a local variable named `User` shadows an outer import of class
 *        `User`), return `null`. The nearer binding shadows; we do NOT fall
 *        through to the global qualified-name index.
 *      - Otherwise continue to the parent scope.
 *   2. If the raw name is a dotted path (e.g., `'models.User'`) and the scope
 *      walk produced no match, consult `QualifiedNameIndex.byQualifiedName`.
 *      Only accept **exactly one** type-kind hit — anything ambiguous returns
 *      `null` rather than a guess.
 *   3. Return `null`.
 *
 * **What `'strict' origins' means.** `'wildcard'` is intentionally excluded.
 * A wildcard-expanded name (`from x import *`) is too loose to use as an
 * anchor for type resolution — it gives no signal about whether the name was
 * actually imported. `Registry.lookup` may accept wildcard bindings at its
 * own discretion (with lower evidence weight); `resolveTypeRef` does not.
 *
 * **What 'type-kind' means.** The subset of `NodeLabel` that a type annotation
 * may legitimately reference: class-like, interface-like, enum-like, and
 * alias-like kinds. See `TYPE_KINDS` below.
 *
 * Pure function — safe to call repeatedly; no side effects.
 */
// ─── Strict policy constants ────────────────────────────────────────────────
/** `'wildcard'` is deliberately absent. See file header. */
const STRICT_ORIGINS = new Set([
    'local',
    'import',
    'namespace',
    'reexport',
]);
/**
 * `NodeLabel` values that may appear on the RHS of a type annotation.
 *
 * Includes the usual class-like and interface-like kinds plus the alias-like
 * ones (`TypeAlias`, `Typedef`). `Namespace` is excluded — it is a scope
 * container, not a value type. `Function` / `Method` / `Variable` are
 * excluded by design: a `rawName` bound to them at a strict origin is a
 * *shadowing* binding, which the algorithm short-circuits to `null`.
 *
 * `'Type'` (the generic `NodeLabel` value) is also excluded — verified
 * against `@codragraph/cli/src/core/ingestion/` at the time of writing, no
 * production extractor emits `type: 'Type'` for annotation-relevant
 * symbols. Should a future extractor start emitting it, add `'Type'`
 * here and add a test asserting the new path.
 */
const TYPE_KINDS = new Set([
    'Class',
    'Interface',
    'Enum',
    'Struct',
    'Union',
    'Trait',
    'TypeAlias',
    'Typedef',
    'Record',
    'Delegate',
    'Annotation',
    'Template',
]);
// ─── Main entry point ──────────────────────────────────────────────────────
export function resolveTypeRef(ref, ctx) {
    // Phase 1: scope-chain walk anchored at the declaration site.
    let currentId = ref.declaredAtScope;
    const visited = new Set();
    while (currentId !== null) {
        // Cycle guard — a well-formed scope tree never loops, but a bug in the
        // construction path should fail fast here rather than hanging.
        if (visited.has(currentId))
            return null;
        visited.add(currentId);
        const scope = ctx.scopes.getScope(currentId);
        if (scope === undefined)
            return null; // broken chain = unresolvable
        const bindings = scope.bindings.get(ref.rawName);
        if (bindings !== undefined && bindings.length > 0) {
            // At least one binding exists at this scope → it is the shadowing site.
            // Either one of them qualifies, or the name is shadowed by a non-type.
            for (const binding of bindings) {
                if (!STRICT_ORIGINS.has(binding.origin))
                    continue;
                if (TYPE_KINDS.has(binding.def.type)) {
                    return binding.def;
                }
            }
            // Shadowed by a non-type / non-strict-origin binding. Fail fast — no
            // global fallback, no walk to the parent.
            return null;
        }
        currentId = scope.parent;
    }
    // Phase 2: dotted fallback via `QualifiedNameIndex`. Only accept a unique
    // type-kind hit; anything ambiguous returns null (strict: no guesses).
    if (ref.rawName.includes('.')) {
        const candidates = ctx.qualifiedNameIndex.get(ref.rawName);
        let onlyTypeDef = null;
        for (const defId of candidates) {
            const def = ctx.defIndex.get(defId);
            if (def === undefined)
                continue;
            if (!TYPE_KINDS.has(def.type))
                continue;
            if (onlyTypeDef !== null)
                return null; // ambiguous
            onlyTypeDef = def;
        }
        if (onlyTypeDef !== null)
            return onlyTypeDef;
    }
    return null;
}
//# sourceMappingURL=resolve-type-ref.js.map