/**
 * Binding Registry - loading logic for addBindings.
 *
 * This module extracts the `addBindings` body into a free function that
 * mutates the maps owned by the BindingRegistry class via a mutable state
 * parameter.  All pure utility helpers used only during loading live here too.
 */
import { tsbindgenClrTypeNameToTsTypeName } from "../tsbindgen/names.js";
// eslint-disable-next-line no-restricted-imports -- binding loader needs the raw CLR signature parser during manifest ingestion.
import { parseMethodSignature } from "../ir/type-system/internal/universe/clr-raw-converter.js";
import { isFullBindingManifest } from "./binding-types.js";
import { getDotnetBindingPayload } from "./dotnet-binding-payload.js";
// ---------------------------------------------------------------------------
// Pure utility helpers
// ---------------------------------------------------------------------------
export const makeClrMemberKey = (assembly, clrType, clrMember) => `${assembly}:${clrType}::${clrMember}`;
/**
 * Extract CLR namespace key ('.' -> '_') from a full CLR type name.
 * Example: "System.Linq.Enumerable" -> "System_Linq"
 */
export const extractNamespaceKey = (clrType) => {
    const lastDot = clrType.lastIndexOf(".");
    if (lastDot <= 0)
        return undefined;
    return clrType.slice(0, lastDot).replace(/\./g, "_");
};
/**
 * Extract the extension receiver TS type name from a tsbindgen normalized signature.
 *
 * Format: "Name|(ParamTypes):ReturnType|static=true"
 * Example: "Where|(IEnumerable_1,Func_2):IEnumerable_1|static=true"
 *
 * Returns the first parameter type name (stripped of byref suffix and namespace prefix).
 */
export const extractExtensionReceiverType = (normalizedSignature) => {
    const paramsMatch = normalizedSignature.match(/\|\(([^)]*)\):/);
    const paramsStr = paramsMatch?.[1]?.trim();
    if (!paramsStr)
        return undefined;
    const [first] = splitSignatureTypeList(paramsStr);
    if (!first)
        return undefined;
    let receiver = first.trim();
    if (receiver.endsWith("&"))
        receiver = receiver.slice(0, -1);
    if (receiver.endsWith("[]"))
        receiver = receiver.slice(0, -2);
    const lastDot = receiver.lastIndexOf(".");
    if (lastDot >= 0)
        receiver = receiver.slice(lastDot + 1);
    return receiver || undefined;
};
/**
 * Split a comma-delimited type list, respecting nested bracket depth.
 * tsbindgen signatures use CLR-style nested generic brackets in some contexts.
 */
const splitSignatureTypeList = (str) => {
    const result = [];
    let depth = 0;
    let current = "";
    for (const char of str) {
        if (char === "[") {
            depth++;
            current += char;
        }
        else if (char === "]") {
            depth--;
            current += char;
        }
        else if (char === "," && depth === 0) {
            result.push(current.trim());
            current = "";
        }
        else {
            current += char;
        }
    }
    if (current.trim()) {
        result.push(current.trim());
    }
    return result;
};
const stableSerialize = (value) => {
    if (value === null || typeof value !== "object") {
        return JSON.stringify(value);
    }
    if (Array.isArray(value)) {
        return `[${value.map((entry) => stableSerialize(entry)).join(",")}]`;
    }
    const record = value;
    return `{${Object.keys(record)
        .filter((key) => record[key] !== undefined)
        .sort()
        .map((key) => `${JSON.stringify(key)}:${stableSerialize(record[key])}`)
        .join(",")}}`;
};
const getMemberBindingOverloadDiscriminator = (member) => {
    if (member.signature !== undefined) {
        return `signature:${member.signature}`;
    }
    const parts = [];
    if (member.semanticSignature !== undefined) {
        parts.push(`semanticSignature:${stableSerialize(member.semanticSignature)}`);
    }
    if (member.semanticType !== undefined) {
        parts.push(`semanticType:${stableSerialize(member.semanticType)}`);
    }
    if (member.parameterCount !== undefined) {
        parts.push(`parameterCount:${member.parameterCount}`);
    }
    if (member.parameterModifiers !== undefined) {
        parts.push(`parameterModifiers:${stableSerialize(member.parameterModifiers)}`);
    }
    return parts.join("|");
};
const getMemberBindingIdentityKey = (member) => `${member.kind}:${member.alias}:${member.binding.assembly}:${member.binding.type}:${member.binding.member}:${getMemberBindingOverloadDiscriminator(member)}`;
const throwMemberBindingConflict = (context, alias, field) => {
    throw new Error(`Conflicting member binding for ${context}:${alias}.${field}`);
};
const mergeOptionalField = (existing, incoming, context, alias, field) => {
    if (incoming === undefined) {
        return existing;
    }
    if (existing === undefined) {
        return incoming;
    }
    if (stableSerialize(existing) !== stableSerialize(incoming)) {
        throwMemberBindingConflict(context, alias, field);
    }
    return existing;
};
const mergeOptionalMetadataObject = (existing, incoming, context, alias, field) => {
    if (incoming === undefined) {
        return existing;
    }
    if (existing === undefined) {
        return incoming;
    }
    const merged = {
        ...existing,
    };
    for (const [key, incomingValue] of Object.entries(incoming)) {
        if (incomingValue === undefined) {
            continue;
        }
        const existingValue = merged[key];
        if (existingValue === undefined) {
            merged[key] = incomingValue;
            continue;
        }
        if (stableSerialize(existingValue) !== stableSerialize(incomingValue)) {
            throwMemberBindingConflict(context, alias, `${field}.${key}`);
        }
    }
    return merged;
};
const mergeMemberBinding = (existing, incoming, context) => {
    const alias = mergeOptionalField(existing.alias, incoming.alias, context, incoming.alias, "alias");
    return {
        kind: mergeOptionalField(existing.kind, incoming.kind, context, alias, "kind"),
        signature: mergeOptionalField(existing.signature, incoming.signature, context, alias, "signature"),
        semanticType: mergeOptionalField(existing.semanticType, incoming.semanticType, context, alias, "semanticType"),
        semanticOptional: mergeOptionalField(existing.semanticOptional, incoming.semanticOptional, context, alias, "semanticOptional"),
        semanticSignature: mergeOptionalField(existing.semanticSignature, incoming.semanticSignature, context, alias, "semanticSignature"),
        receiverExpectedType: mergeOptionalField(existing.receiverExpectedType, incoming.receiverExpectedType, context, alias, "receiverExpectedType"),
        overloadFamily: mergeOptionalField(existing.overloadFamily, incoming.overloadFamily, context, alias, "overloadFamily"),
        parameterCount: mergeOptionalField(existing.parameterCount, incoming.parameterCount, context, alias, "parameterCount"),
        name: mergeOptionalField(existing.name, incoming.name, context, alias, "name"),
        alias,
        binding: mergeOptionalMetadataObject(existing.binding, incoming.binding, context, alias, "binding"),
        parameterModifiers: mergeOptionalField(existing.parameterModifiers, incoming.parameterModifiers, context, alias, "parameterModifiers"),
        isExtensionMethod: mergeOptionalField(existing.isExtensionMethod, incoming.isExtensionMethod, context, alias, "isExtensionMethod"),
        emitSemantics: mergeOptionalMetadataObject(existing.emitSemantics, incoming.emitSemantics, context, alias, "emitSemantics"),
        sourceOrigin: mergeOptionalMetadataObject(existing.sourceOrigin, incoming.sourceOrigin, context, alias, "sourceOrigin"),
    };
};
const upsertMemberBinding = (members, member, context) => {
    const identityKey = getMemberBindingIdentityKey(member);
    const index = members.findIndex((candidate) => getMemberBindingIdentityKey(candidate) === identityKey);
    if (index >= 0) {
        const existingMember = members[index];
        if (existingMember === undefined) {
            throw new Error(`Internal binding overload index missing for ${context}`);
        }
        members[index] = mergeMemberBinding(existingMember, member, context);
        return;
    }
    members.push(member);
};
const shouldPreferIncomingSimpleBinding = (existing, incoming) => {
    if (!existing) {
        return true;
    }
    if (incoming.kind === "global" && existing.kind !== "global") {
        return true;
    }
    if (incoming.kind !== "global" && existing.kind === "global") {
        return false;
    }
    return false;
};
const mergeMemberBindings = (existing, incoming, context) => {
    const merged = [];
    const seen = new Map();
    const add = (member) => {
        const key = getMemberBindingIdentityKey(member);
        const previous = seen.get(key);
        if (previous !== undefined) {
            const nextMember = mergeMemberBinding(previous.member, member, context);
            merged[previous.index] = nextMember;
            seen.set(key, { index: previous.index, member: nextMember });
            return;
        }
        seen.set(key, { index: merged.length, member });
        merged.push(member);
    };
    for (const member of existing) {
        add(member);
    }
    for (const member of incoming) {
        add(member);
    }
    return merged;
};
const mergeTypeBinding = (existing, incoming, context) => {
    if (!existing) {
        return incoming;
    }
    if (existing.name !== incoming.name || existing.kind !== incoming.kind) {
        throw new Error(`Conflicting type binding for ${context}:${incoming.alias}`);
    }
    return {
        ...existing,
        members: mergeMemberBindings(existing.members, incoming.members, `${context}.${incoming.alias}`),
    };
};
const mergeNamespaceBinding = (existing, incoming) => {
    if (!existing) {
        return incoming;
    }
    if (existing.name !== incoming.name) {
        throw new Error(`Conflicting namespace binding for ${incoming.alias}`);
    }
    const typeOrder = [];
    const mergedTypes = new Map();
    for (const type of existing.types) {
        typeOrder.push(type.alias);
        mergedTypes.set(type.alias, type);
    }
    for (const type of incoming.types) {
        if (!mergedTypes.has(type.alias)) {
            typeOrder.push(type.alias);
        }
        mergedTypes.set(type.alias, mergeTypeBinding(mergedTypes.get(type.alias), type, `${incoming.alias}`));
    }
    return {
        ...existing,
        types: typeOrder
            .map((alias) => mergedTypes.get(alias))
            .filter((type) => type !== undefined),
    };
};
// ---------------------------------------------------------------------------
// Supertype / base-type mutation helpers
// ---------------------------------------------------------------------------
const addSupertype = (state, typeAlias, superAlias) => {
    if (!typeAlias || !superAlias)
        return;
    if (typeAlias === superAlias)
        return;
    const set = state.tsSupertypes.get(typeAlias) ?? new Set();
    set.add(superAlias);
    state.tsSupertypes.set(typeAlias, set);
};
const setBaseType = (state, typeAlias, baseAlias) => {
    if (!typeAlias || !baseAlias)
        return;
    if (typeAlias === baseAlias)
        return;
    state.tsBaseTypes.set(typeAlias, baseAlias);
};
const isSyntheticSourceTypeManifestPath = (filePath) => filePath.endsWith("::synthetic-source-types");
const getQualifiedTypeAlias = (namespaceName, type) => type.name === type.alias ? `${namespaceName}.${type.alias}` : type.name;
const remapRegisteredTypeAlias = (state, fromAlias, toAlias, expectedTypeName) => {
    if (fromAlias === toAlias) {
        return;
    }
    const existing = state.types.get(fromAlias);
    if (!existing) {
        return;
    }
    if (existing.name !== expectedTypeName) {
        return;
    }
    const renamedExisting = {
        ...existing,
        alias: toAlias,
    };
    const existingAtDestination = state.types.get(toAlias);
    if (existingAtDestination &&
        (existingAtDestination.name !== renamedExisting.name ||
            existingAtDestination.kind !== renamedExisting.kind)) {
        throw new Error(`Conflicting type binding for ${toAlias}`);
    }
    state.types.delete(fromAlias);
    state.types.set(toAlias, mergeTypeBinding(existingAtDestination, renamedExisting, toAlias));
    for (const [lookupAlias, canonicalAlias] of Array.from(state.typeLookupAliasMap.entries())) {
        if (canonicalAlias !== fromAlias) {
            continue;
        }
        if (lookupAlias === fromAlias) {
            state.typeLookupAliasMap.delete(lookupAlias);
            continue;
        }
        state.typeLookupAliasMap.set(lookupAlias, toAlias);
    }
    const clrNames = state.clrTypeNamesByAlias.get(fromAlias);
    if (clrNames) {
        const destinationNames = state.clrTypeNamesByAlias.get(toAlias) ?? new Set();
        for (const clrName of clrNames) {
            destinationNames.add(clrName);
        }
        state.clrTypeNamesByAlias.set(toAlias, destinationNames);
        state.clrTypeNamesByAlias.delete(fromAlias);
    }
    const directBaseType = state.tsBaseTypes.get(fromAlias);
    if (directBaseType !== undefined) {
        state.tsBaseTypes.set(toAlias, directBaseType === fromAlias ? toAlias : directBaseType);
        state.tsBaseTypes.delete(fromAlias);
    }
    for (const [typeAlias, baseAlias] of Array.from(state.tsBaseTypes.entries())) {
        if (baseAlias === fromAlias) {
            state.tsBaseTypes.set(typeAlias, toAlias);
        }
    }
    const directSupertypes = state.tsSupertypes.get(fromAlias);
    if (directSupertypes) {
        state.tsSupertypes.set(toAlias, new Set(Array.from(directSupertypes, (candidate) => candidate === fromAlias ? toAlias : candidate)));
        state.tsSupertypes.delete(fromAlias);
    }
    for (const [typeAlias, supertypes] of Array.from(state.tsSupertypes.entries())) {
        if (!supertypes.has(fromAlias)) {
            continue;
        }
        const remapped = new Set();
        for (const candidate of supertypes) {
            remapped.add(candidate === fromAlias ? toAlias : candidate);
        }
        state.tsSupertypes.set(typeAlias, remapped);
    }
    for (const member of existing.members) {
        const oldKey = `${fromAlias}.${member.alias}`;
        const newKey = `${toAlias}.${member.alias}`;
        const directMember = state.members.get(oldKey);
        if (directMember) {
            state.members.delete(oldKey);
            state.members.set(newKey, directMember);
        }
        const overloads = state.memberOverloads.get(oldKey);
        if (overloads) {
            const destination = state.memberOverloads.get(newKey) ?? [];
            for (const overload of overloads) {
                const serialized = stableSerialize(overload);
                if (!destination.some((candidate) => stableSerialize(candidate) === serialized)) {
                    destination.push(overload);
                }
            }
            state.memberOverloads.delete(oldKey);
            state.memberOverloads.set(newKey, destination);
        }
    }
    for (const [namespaceAlias, namespace] of Array.from(state.namespaces.entries())) {
        let changed = false;
        const remappedTypes = namespace.types.map((type) => {
            if (type.alias !== fromAlias || type.name !== expectedTypeName) {
                return type;
            }
            changed = true;
            return renamedExisting;
        });
        if (changed) {
            state.namespaces.set(namespaceAlias, {
                ...namespace,
                types: remappedTypes,
            });
        }
    }
    for (const [, receiverMapByNamespace] of Array.from(state.extensionMethods.entries())) {
        const receiverMap = receiverMapByNamespace.get(fromAlias);
        if (!receiverMap) {
            continue;
        }
        const destination = receiverMapByNamespace.get(toAlias) ?? new Map();
        for (const [memberAlias, overloads] of Array.from(receiverMap.entries())) {
            const existingOverloads = destination.get(memberAlias) ?? [];
            for (const overload of overloads) {
                const serialized = stableSerialize(overload);
                if (!existingOverloads.some((candidate) => stableSerialize(candidate) === serialized)) {
                    existingOverloads.push(overload);
                }
            }
            destination.set(memberAlias, existingOverloads);
        }
        receiverMapByNamespace.set(toAlias, destination);
        receiverMapByNamespace.delete(fromAlias);
    }
};
const canonicalizeTypeBinding = (state, namespaceName, type, preferSimpleAlias) => {
    const existing = state.types.get(type.alias);
    if (!existing) {
        return type;
    }
    if (existing.name === type.name && existing.kind === type.kind) {
        return type;
    }
    if (preferSimpleAlias) {
        if (state.sourceOwnedTypeAliases.has(type.alias)) {
            const qualifiedAlias = getQualifiedTypeAlias(namespaceName, type);
            return qualifiedAlias === type.alias
                ? type
                : {
                    ...type,
                    alias: qualifiedAlias,
                };
        }
        const existingQualifiedAlias = getQualifiedTypeAlias(namespaceName, existing);
        if (existingQualifiedAlias === existing.alias) {
            throw new Error(`Conflicting type binding for ${namespaceName}:${type.alias}`);
        }
        remapRegisteredTypeAlias(state, type.alias, existingQualifiedAlias, existing.name);
        return type;
    }
    const qualifiedAlias = getQualifiedTypeAlias(namespaceName, type);
    return {
        ...type,
        alias: qualifiedAlias,
    };
};
const throwOnExplicitAliasConflict = (state, namespaceName, type) => {
    const existing = state.types.get(type.alias);
    if (!existing) {
        return;
    }
    if (existing.name === type.name && existing.kind === type.kind) {
        return;
    }
    const existingNamespace = extractNamespaceKey(existing.name)?.replace(/_/g, ".");
    if (existingNamespace !== namespaceName) {
        return;
    }
    throw new Error(`Conflicting type binding for ${namespaceName}:${type.alias}`);
};
// ---------------------------------------------------------------------------
// addBindings – the main loading entry point
// ---------------------------------------------------------------------------
/**
 * Load a binding manifest file and add its bindings to the registry state.
 * Supports simple, full, and tsbindgen formats.
 */
export const addBindingsToState = (state, _filePath, manifest) => {
    const preferSimpleAlias = isSyntheticSourceTypeManifestPath(_filePath);
    // Airplane-grade: a given bindings file must be loaded exactly once per
    // ProgramContext. Some converters perform on-demand bindings.json loading
    // based on Binding-resolved MemberIds; without this guard, overload sets
    // can silently duplicate and become ambiguous.
    if (state.loadedBindingFiles.has(_filePath))
        return;
    state.loadedBindingFiles.add(_filePath);
    const addMemberOverload = (key, member) => {
        const existing = state.memberOverloads.get(key) ?? [];
        upsertMemberBinding(existing, member, key);
        state.memberOverloads.set(key, existing);
    };
    const addClrMemberOverload = (member) => {
        if (member.kind !== "method")
            return;
        const clrTargetKey = makeClrMemberKey(member.binding.assembly, member.binding.type, member.binding.member);
        const existing = state.clrMemberOverloads.get(clrTargetKey) ?? [];
        upsertMemberBinding(existing, member, clrTargetKey);
        state.clrMemberOverloads.set(clrTargetKey, existing);
    };
    const recordClrTypeAlias = (alias, clrName) => {
        const names = state.clrTypeNamesByAlias.get(alias) ?? new Set();
        names.add(clrName);
        state.clrTypeNamesByAlias.set(alias, names);
    };
    const setPreferredSimpleBinding = (name, descriptor) => {
        const existing = state.simpleBindings.get(name);
        if (shouldPreferIncomingSimpleBinding(existing, descriptor)) {
            state.simpleBindings.set(name, descriptor);
        }
    };
    if (isFullBindingManifest(manifest)) {
        // Full format: hierarchical namespace/type/member structure
        // Index by alias (TS identifier) for quick lookup
        for (const ns of manifest.namespaces) {
            const canonicalTypes = ns.types.map((type) => canonicalizeTypeBinding(state, ns.name, type, preferSimpleAlias));
            state.namespaces.set(ns.alias, mergeNamespaceBinding(state.namespaces.get(ns.alias), {
                ...ns,
                types: canonicalTypes,
            }));
            // Index types for quick lookup by TS alias
            for (const type of canonicalTypes) {
                if (preferSimpleAlias && !type.alias.includes(".")) {
                    state.sourceOwnedTypeAliases.add(type.alias);
                }
                const existing = state.types.get(type.alias);
                const merged = mergeTypeBinding(existing, type, `${ns.alias}`);
                state.clrTypeNames.add(merged.name);
                state.types.set(merged.alias, merged);
                if (!state.typeLookupAliasMap.has(type.name)) {
                    state.typeLookupAliasMap.set(type.name, merged.alias);
                }
                if (!merged.alias.includes(".")) {
                    state.typeLookupAliasMap.set(`${ns.name}.${type.alias}`, merged.alias);
                }
                const simpleClrName = merged.name.split(".").pop();
                if (simpleClrName &&
                    simpleClrName !== merged.alias &&
                    !state.types.has(simpleClrName) &&
                    !state.typeLookupAliasMap.has(simpleClrName)) {
                    state.typeLookupAliasMap.set(simpleClrName, merged.alias);
                }
                recordClrTypeAlias(merged.alias, merged.name);
                // Index members for quick lookup (keyed by "typeAlias.memberAlias")
                for (const member of merged.members) {
                    const key = `${merged.alias}.${member.alias}`;
                    state.members.set(key, member);
                    addMemberOverload(key, member);
                    addClrMemberOverload(member);
                }
            }
        }
    }
    else {
        const dotnetPayload = getDotnetBindingPayload(manifest);
        if (!dotnetPayload) {
            if (!("bindings" in manifest)) {
                return;
            }
            // Simple format: global/module bindings
            for (const [name, descriptor] of Object.entries(manifest.bindings)) {
                if (descriptor.kind === "global") {
                    if (shouldPreferIncomingSimpleBinding(state.simpleGlobalBindings.get(name), descriptor)) {
                        state.simpleGlobalBindings.set(name, descriptor);
                    }
                }
                else {
                    if (shouldPreferIncomingSimpleBinding(state.simpleModuleBindings.get(name), descriptor)) {
                        state.simpleModuleBindings.set(name, descriptor);
                    }
                }
                setPreferredSimpleBinding(name, descriptor);
            }
            return;
        }
        const manifestNamespace = dotnetPayload.namespace;
        // tsbindgen format: convert to internal format
        const namespaceTypes = [];
        const derivedAliasCounts = new Map();
        for (const tsbType of dotnetPayload.types) {
            const derivedAlias = tsbindgenClrTypeNameToTsTypeName(tsbType.clrName);
            derivedAliasCounts.set(derivedAlias, (derivedAliasCounts.get(derivedAlias) ?? 0) + 1);
        }
        for (const tsbType of dotnetPayload.types) {
            // Create members from methods, properties, and fields
            const members = [];
            for (const method of tsbType.methods) {
                const parsedSemanticSignature = method.semanticSignature ??
                    (method.normalizedSignature
                        ? (() => {
                            const parsed = parseMethodSignature(method.normalizedSignature, method);
                            return {
                                ...(parsed.typeParameters.length > 0
                                    ? {
                                        typeParameters: parsed.typeParameters.map((parameter) => parameter.name),
                                    }
                                    : {}),
                                parameters: parsed.parameters.map((parameter) => ({
                                    kind: "parameter",
                                    pattern: {
                                        kind: "identifierPattern",
                                        name: parameter.name,
                                    },
                                    type: parameter.type,
                                    isOptional: parameter.isOptional,
                                    isRest: parameter.isRest,
                                    passing: parameter.mode,
                                })),
                                ...(parsed.returnType.kind !== "voidType"
                                    ? { returnType: parsed.returnType }
                                    : {}),
                            };
                        })()
                        : undefined);
                const memberBinding = {
                    kind: "method",
                    name: method.clrName,
                    // No naming policy: TS member names are the CLR names as authored.
                    alias: method.clrName,
                    signature: method.normalizedSignature,
                    semanticSignature: parsedSemanticSignature,
                    overloadFamily: method.overloadFamily,
                    parameterCount: method.parameterCount,
                    binding: {
                        assembly: method.declaringAssemblyName,
                        type: method.declaringClrType,
                        // member = clrName (what C# emits, e.g., "Add")
                        member: method.clrName,
                    },
                    // Include parameter modifiers for ref/out/in parameters
                    parameterModifiers: method.parameterModifiers,
                    isExtensionMethod: method.isExtensionMethod ?? false,
                    emitSemantics: method.emitSemantics,
                    receiverExpectedType: method.isExtensionMethod === true
                        ? parsedSemanticSignature?.parameters[0]?.type
                        : undefined,
                };
                members.push(memberBinding);
                addClrMemberOverload(memberBinding);
                // Index extension methods by (declaring namespace, receiver type, method name).
                if (method.isExtensionMethod && method.normalizedSignature) {
                    const receiverTypeName = extractExtensionReceiverType(method.normalizedSignature);
                    const namespaceKey = extractNamespaceKey(method.declaringClrType);
                    if (receiverTypeName && namespaceKey) {
                        const nsMap = state.extensionMethods.get(namespaceKey) ??
                            new Map();
                        if (!state.extensionMethods.has(namespaceKey)) {
                            state.extensionMethods.set(namespaceKey, nsMap);
                        }
                        const receiverMap = nsMap.get(receiverTypeName) ?? new Map();
                        if (!nsMap.has(receiverTypeName)) {
                            nsMap.set(receiverTypeName, receiverMap);
                        }
                        const list = receiverMap.get(memberBinding.alias) ?? [];
                        upsertMemberBinding(list, memberBinding, `${namespaceKey}.${receiverTypeName}.${memberBinding.alias}`);
                        receiverMap.set(memberBinding.alias, list);
                    }
                }
            }
            for (const prop of tsbType.properties) {
                members.push({
                    kind: "property",
                    signature: prop.normalizedSignature,
                    semanticType: prop.semanticType,
                    semanticOptional: prop.semanticOptional,
                    name: prop.clrName,
                    alias: prop.clrName,
                    binding: {
                        assembly: prop.declaringAssemblyName,
                        type: prop.declaringClrType,
                        member: prop.clrName,
                    },
                    emitSemantics: prop.emitSemantics,
                });
            }
            for (const field of tsbType.fields) {
                // Fields are treated as properties for binding purposes
                members.push({
                    kind: "property",
                    signature: field.normalizedSignature,
                    semanticType: field.semanticType,
                    semanticOptional: field.semanticOptional,
                    name: field.clrName,
                    alias: field.clrName,
                    binding: {
                        assembly: field.declaringAssemblyName,
                        type: field.declaringClrType,
                        member: field.clrName,
                    },
                    emitSemantics: field.emitSemantics,
                });
            }
            const derivedAlias = tsbindgenClrTypeNameToTsTypeName(tsbType.clrName);
            const tsAlias = tsbType.alias ?? derivedAlias;
            const uniqueDerivedAlias = (derivedAliasCounts.get(derivedAlias) ?? 0) === 1;
            // Record CLR inheritance relationships (base type + interfaces) so extension-method
            // binding lookup can follow the CLR graph deterministically.
            const baseAlias = tsbType.baseType?.clrName
                ? tsbindgenClrTypeNameToTsTypeName(tsbType.baseType.clrName)
                : undefined;
            if (baseAlias) {
                setBaseType(state, tsAlias, baseAlias);
                addSupertype(state, tsAlias, baseAlias);
            }
            for (const iface of tsbType.interfaces ?? []) {
                if (!iface?.clrName)
                    continue;
                const ifaceAlias = tsbindgenClrTypeNameToTsTypeName(iface.clrName);
                addSupertype(state, tsAlias, ifaceAlias);
            }
            const kindFromBindings = (() => {
                switch (tsbType.kind) {
                    case "Interface":
                        return "interface";
                    case "Struct":
                        return "struct";
                    case "Enum":
                        return "enum";
                    case "Class":
                    default:
                        return "class";
                }
            })();
            // Create TypeBinding - TS alias is derived deterministically from CLR name.
            const rawTypeBinding = {
                name: tsbType.clrName,
                alias: tsAlias,
                kind: kindFromBindings,
                members,
            };
            if (tsbType.alias) {
                throwOnExplicitAliasConflict(state, manifestNamespace, rawTypeBinding);
            }
            const typeBinding = canonicalizeTypeBinding(state, manifestNamespace, rawTypeBinding, preferSimpleAlias);
            const mergedTypeBinding = mergeTypeBinding(state.types.get(typeBinding.alias), typeBinding, `${manifestNamespace}`);
            state.clrTypeNames.add(tsbType.clrName);
            if (!state.typeLookupAliasMap.has(tsbType.clrName)) {
                state.typeLookupAliasMap.set(tsbType.clrName, mergedTypeBinding.alias);
            }
            if (!mergedTypeBinding.alias.includes(".")) {
                state.typeLookupAliasMap.set(`${manifestNamespace}.${tsAlias}`, mergedTypeBinding.alias);
            }
            namespaceTypes.push(mergedTypeBinding);
            // Index the type by its TS name.
            state.types.set(mergedTypeBinding.alias, mergedTypeBinding);
            recordClrTypeAlias(mergedTypeBinding.alias, mergedTypeBinding.name);
            if (tsAlias !== mergedTypeBinding.alias) {
                recordClrTypeAlias(tsAlias, mergedTypeBinding.name);
            }
            const registeredDerivedAlias = uniqueDerivedAlias &&
                derivedAlias !== mergedTypeBinding.alias &&
                !state.types.has(derivedAlias) &&
                !state.typeLookupAliasMap.has(derivedAlias);
            if (registeredDerivedAlias) {
                state.types.set(derivedAlias, mergedTypeBinding);
                state.typeLookupAliasMap.set(derivedAlias, mergedTypeBinding.alias);
                recordClrTypeAlias(derivedAlias, mergedTypeBinding.name);
            }
            // Also index by simple name if ts alias has arity suffix (e.g., "List_1" -> also index as "List")
            // This is needed because TS exports both List_1 and List as aliases, and TS code uses List<T>
            // IMPORTANT: Only set if not already present - non-generic versions should take precedence
            // (e.g., Action should resolve to System.Action, not System.Action`9)
            const arityMatch = derivedAlias.match(/^(.+)_(\d+)$/);
            const simpleAlias = arityMatch ? arityMatch[1] : null;
            const registeredSimpleAlias = simpleAlias &&
                simpleAlias !== mergedTypeBinding.alias &&
                !state.types.has(simpleAlias);
            if (registeredSimpleAlias) {
                state.types.set(simpleAlias, mergedTypeBinding);
            }
            if (registeredSimpleAlias && simpleAlias) {
                recordClrTypeAlias(simpleAlias, mergedTypeBinding.name);
            }
            // Index members for direct lookup.
            for (const member of mergedTypeBinding.members) {
                // Key by canonical TS alias.
                const tsKey = `${mergedTypeBinding.alias}.${member.alias}`;
                state.members.set(tsKey, member);
                addMemberOverload(tsKey, member);
                // Also key by the derived/simple alias when it is uniquely owned.
                if (registeredDerivedAlias) {
                    const derivedKey = `${derivedAlias}.${member.alias}`;
                    state.members.set(derivedKey, member);
                    addMemberOverload(derivedKey, member);
                }
                if (tsAlias !== mergedTypeBinding.alias) {
                    const authoredAliasKey = `${tsAlias}.${member.alias}`;
                    state.members.set(authoredAliasKey, member);
                    addMemberOverload(authoredAliasKey, member);
                }
                // Also key by simple alias if applicable (e.g., "List.Add")
                if (registeredSimpleAlias && simpleAlias) {
                    const simpleKey = `${simpleAlias}.${member.alias}`;
                    state.members.set(simpleKey, member);
                    addMemberOverload(simpleKey, member);
                }
                if (member.binding.type !== mergedTypeBinding.name) {
                    const explicitOwnerAlias = state.typeLookupAliasMap.get(member.binding.type) ??
                        tsbindgenClrTypeNameToTsTypeName(member.binding.type);
                    if (explicitOwnerAlias &&
                        explicitOwnerAlias !== mergedTypeBinding.alias &&
                        explicitOwnerAlias !== tsAlias &&
                        explicitOwnerAlias !== derivedAlias &&
                        explicitOwnerAlias !== simpleAlias) {
                        const explicitOwnerKey = `${explicitOwnerAlias}.${member.alias}`;
                        state.members.set(explicitOwnerKey, member);
                        addMemberOverload(explicitOwnerKey, member);
                        recordClrTypeAlias(explicitOwnerAlias, member.binding.type);
                        if (!state.typeLookupAliasMap.has(member.binding.type)) {
                            state.typeLookupAliasMap.set(member.binding.type, explicitOwnerAlias);
                        }
                    }
                }
            }
        }
        // Optional flattened named exports.
        // These are stable value exports for CLR namespace facade modules and are
        // resolved by Tsonic during import binding (so `import { x }` maps to
        // `global::<DeclaringType>.<member>` in C#).
        if (dotnetPayload.exports) {
            const nsExports = state.tsbindgenExports.get(manifestNamespace) ??
                new Map();
            for (const [exportName, exp] of Object.entries(dotnetPayload.exports)) {
                nsExports.set(exportName, exp);
            }
            state.tsbindgenExports.set(manifestNamespace, nsExports);
        }
        state.namespaces.set(manifestNamespace, mergeNamespaceBinding(state.namespaces.get(manifestNamespace), {
            name: manifestNamespace,
            alias: manifestNamespace,
            types: namespaceTypes,
        }));
    }
};
//# sourceMappingURL=binding-registry-loading.js.map