/**
 * Registration Dispatch Table
 *
 * Behavior-grouped O(1) dispatch table for routing `SymbolTable.add()`
 * registrations into the semantic registries. Replaces the cascading
 * `if/else` ladder in `symbol-table.ts` with a `Map<NodeLabel, RoutingDecision>`
 * whose entries point to closure-captured hooks.
 *
 * ## Ownership diagram
 *
 *     SemanticModel
 *       ├── types   (TypeRegistry)    ← classLikeHook / implHook write here
 *       ├── methods (MethodRegistry)  ← methodHook writes here
 *       ├── fields  (FieldRegistry)   ← propertyHook writes here
 *       └── symbols (SymbolTable)     ← owns fileIndex + callableByName,
 *                                       calls dispatch() in add()
 *
 * ## Behavior groups (5 hooks, 13 table entries)
 *
 * | Group         | NodeLabel values                                  | Hook         | Skip callable? |
 * |---------------|---------------------------------------------------|--------------|----------------|
 * | class-like    | Class, Struct, Interface, Enum, Record, Trait     | classLikeHook    | no             |
 * | method-like   | Method, Constructor                               | methodHook   | no             |
 * | property      | Property                                          | propertyHook | YES            |
 * | impl-block    | Impl                                              | implHook     | no             |
 * | callable-only | Function, Macro, Delegate                         | (no entry)   | no             |
 *
 * Every other `NodeLabel` is "inert" — reached by `fileIndex` only. No
 * specialized registry, no callable index append.
 *
 * ## How to add a new NodeLabel
 *
 * 1. Add the variant to the `NodeLabel` union in `@codragraph/shared/src/graph/types.ts`.
 * 2. Decide which behavior group it belongs to by asking "which lookups must
 *    return this symbol?" (not "what language feature is it?"). A new Swift
 *    `Extension` is class-like if you want owner-scoped method lookup on it;
 *    a new Kotlin `Object` is class-like for the same reason.
 * 3. Either:
 *    - Add a table entry here pointing at one of the existing hooks, OR
 *    - Add it to `CALLABLE_ONLY_LABELS` if it is a free callable, OR
 *    - Add it to `INERT_LABELS` if it's metadata-only (File, Folder, Decorator,
 *      etc.) — never queried via owner/class lookups.
 * 4. If none of the above fit — the new kind needs a brand-new registry —
 *    design the registry first in `model/`, then add a new hook closure
 *    and table entries. Update `DISPATCH_LABELS` / the exhaustiveness guard
 *    accordingly.
 *
 * The runtime exhaustiveness guard in `symbol-table.ts` will warn if a
 * `NodeLabel` is missing from all three sets.
 */
import { FREE_CALLABLE_TYPES } from './symbol-table.js';
/**
 * **Single source of truth** for NodeLabel classification. Every NodeLabel
 * has exactly one behavior category — enforced at compile time by the
 * `as const satisfies Record<NodeLabel, LabelBehavior>` combo:
 *
 *   - **Completeness** — `Record<NodeLabel, LabelBehavior>` requires every
 *     NodeLabel to be a key. Missing a label fails to compile with
 *     "Property 'X' is missing in type ..." naming the drifted label.
 *   - **No extras** — `satisfies` performs excess-property checking on
 *     object literals, so a non-NodeLabel string key fails to compile.
 *   - **No duplicates** — object keys are unique by construction. A label
 *     cannot be classified into two categories by accident.
 *   - **Valid values** — `LabelBehavior` is a narrow union, so a typo in
 *     the category name fails to compile.
 *
 * Adding a new NodeLabel to `@codragraph/shared`: TypeScript will flag this
 * file as incomplete. Add the new label with its behavior category and
 * the three `*_LABELS` Sets + `ALL_NODE_LABELS` array below are derived
 * automatically — no separate list to update, no runtime drift detection
 * needed.
 *
 * NOTE: `Type` and `CodeElement` are inert wrappers for language features
 * that don't yet have a dedicated registry (typedefs, synthesized dynamic
 * calls). If future work needs owner-scoped lookup for them, change their
 * category to `'dispatch'` and add a hook in `createRegistrationTable`.
 * Do not special-case them inside `SymbolTable.add()`.
 */
const LABEL_BEHAVIOR = {
    // dispatch — owner-scoped registry writes
    Class: 'dispatch',
    Struct: 'dispatch',
    Interface: 'dispatch',
    Enum: 'dispatch',
    Record: 'dispatch',
    Trait: 'dispatch',
    Method: 'dispatch',
    Constructor: 'dispatch',
    Property: 'dispatch',
    Impl: 'dispatch',
    // callable-only — file index + callableByName, no owner scope
    Function: 'callable-only',
    Macro: 'callable-only',
    Delegate: 'callable-only',
    // inert — file index only
    Project: 'inert',
    Package: 'inert',
    Module: 'inert',
    Folder: 'inert',
    File: 'inert',
    Variable: 'inert',
    Decorator: 'inert',
    Import: 'inert',
    Type: 'inert',
    CodeElement: 'inert',
    Community: 'inert',
    Process: 'inert',
    FeatureCluster: 'inert',
    Typedef: 'inert',
    Union: 'inert',
    Namespace: 'inert',
    TypeAlias: 'inert',
    Const: 'inert',
    Static: 'inert',
    Annotation: 'inert',
    Template: 'inert',
    Section: 'inert',
    Route: 'inert',
    Tool: 'inert',
};
// ---------------------------------------------------------------------------
// Derived runtime collections — all keyed off LABEL_BEHAVIOR
// ---------------------------------------------------------------------------
/**
 * All known NodeLabels, derived from the keys of `LABEL_BEHAVIOR`. The
 * `satisfies Record<NodeLabel, LabelBehavior>` bijection above proves
 * that `Object.keys(LABEL_BEHAVIOR)` is exactly the NodeLabel set —
 * the cast to `NodeLabel[]` is sound, not a type-system bypass.
 *
 * Consumers (e.g., the semantic-model barrel re-export for tests) can
 * rely on this list being complete by construction. No runtime drift
 * check is needed or possible — the type system is the proof.
 */
export const ALL_NODE_LABELS = Object.keys(LABEL_BEHAVIOR);
const labelsWithBehavior = (behavior) => ALL_NODE_LABELS.filter((label) => LABEL_BEHAVIOR[label] === behavior);
/**
 * NodeLabel values that are free callables — appear in `callableByName`
 * but have no owner-scoped specialized registry. Alias of
 * {@link FREE_CALLABLE_TYPES} exported here for taxonomy-test use. The
 * compile-time cross-invariant on `LABEL_BEHAVIOR` above guarantees the
 * alias and the LABEL_BEHAVIOR `callable-only` classification cannot
 * drift.
 */
export const CALLABLE_ONLY_LABELS = FREE_CALLABLE_TYPES;
/**
 * NodeLabel values that touch only the file index — no specialized
 * registry, no callable index.
 */
export const INERT_LABELS = new Set(labelsWithBehavior('inert'));
/**
 * NodeLabel values that have a dispatch table entry. `createRegistrationTable`
 * below must provide a hook for exactly this set — the test file's behavior-
 * group tests and the integration tests pin the hook↔label correspondence.
 */
export const DISPATCH_LABELS = new Set(labelsWithBehavior('dispatch'));
// ---------------------------------------------------------------------------
// Factory
// ---------------------------------------------------------------------------
/**
 * Build the dispatch table. Must be called once per `createSymbolTable`
 * invocation so each hook closes over that SymbolTable's injected
 * registries. Reusing a single module-level instance would cause hooks
 * to write into the wrong SemanticModel.
 */
export const createRegistrationTable = (deps) => {
    const { types, methods, fields } = deps;
    // Hook 1: class-like — Class, Struct, Interface, Enum, Record, Trait.
    // Shared reference — six table entries point at this one closure.
    const classLikeHook = (name, def) => {
        const qualifiedKey = def.qualifiedName ?? name;
        types.registerClass(name, qualifiedKey, def);
    };
    // Hook 2: method-like — Method, Constructor. Silently skipped if the
    // caller did not provide an ownerId (Property without ownerId is
    // treated the same way).
    const methodHook = (name, def) => {
        if (def.ownerId) {
            methods.register(def.ownerId, name, def);
        }
    };
    // Hook 3: property — Property. Silently skipped without ownerId.
    // Property is not in `FREE_CALLABLE_TYPES`, so `SymbolTable.add()` already
    // excludes it from `callableByName`; common property names like
    // `id` / `name` / `type` never pollute the callable index.
    const propertyHook = (name, def) => {
        if (def.ownerId) {
            fields.register(def.ownerId, name, def);
        }
    };
    // Hook 4: impl-block — Rust `impl` blocks. Kept separate from classLikeHook
    // because heritage resolution must not treat Impls as class candidates
    // (an Impl is not a parent type, it's an ancillary dispatch table).
    const implHook = (name, def) => {
        types.registerImpl(name, def);
    };
    // Single source of truth for the label → hook mapping. The
    // `satisfies Record<DispatchLabel, RegistrationHook>` intersection
    // fails at build time if (a) any label classified as 'dispatch' in
    // `LABEL_BEHAVIOR` is missing here, or (b) any key here is not
    // classified as 'dispatch'. This is the compile-time twin of the
    // runtime taxonomy — no drift possible.
    const dispatchByLabel = {
        // class-like — six labels share the single `classLikeHook` closure,
        // kept in lockstep with `CLASS_TYPES_TUPLE` via the
        // `Record<ClassLikeLabel, 'dispatch'>` cross-invariant on
        // `LABEL_BEHAVIOR`.
        Class: classLikeHook,
        Struct: classLikeHook,
        Interface: classLikeHook,
        Enum: classLikeHook,
        Record: classLikeHook,
        Trait: classLikeHook,
        // method-like — routed via dispatch-key normalization in
        // `wrappedAdd` so Function+ownerId also reaches `methodHook`.
        Method: methodHook,
        Constructor: methodHook,
        // property — callable-index exclusion is enforced by
        // `SymbolTable.add()` (Property is not in `FREE_CALLABLE_TYPES`).
        Property: propertyHook,
        // impl-block — Rust `impl` blocks. Separate from classLikeHook because
        // heritage resolution must not treat Impls as class candidates.
        Impl: implHook,
    };
    return new Map(Object.entries(dispatchByLabel));
};
