/**
 * Anonymous Type Lower Types
 *
 * Core type, parameter, type-parameter, interface-member, and pattern
 * lowering functions. These replace IrObjectType nodes with IrReferenceType
 * nodes in type positions throughout the IR tree.
 *
 * Note: This module and anon-type-ir-rewriting.ts form a mutual recursion
 * group (e.g. lowerParameter -> lowerExpression -> lowerBlockStatement ->
 * lowerStatement -> lowerExpression). The circular import is safe because
 * all exports are const arrow functions that are not invoked at import time.
 */
import { getReferenceLoweringStableKey } from "./anon-type-shape-analysis.js";
import { getOrCreateObjectTypeReference } from "./anon-type-declaration-synthesis.js";
// Circular import: lowerParameter/lowerPattern call lowerExpression.
// Safe because all exports are const arrow functions (no top-level execution).
import { lowerExpression } from "./anon-type-ir-rewriting.js";
const canonicalClrNameFromReference = (type) => type?.resolvedClrType ?? type?.typeId?.clrName;
const enrichReferenceIdentityMetadata = (reference, source, localDeclaredReference) => {
    const resolvedClrType = reference.resolvedClrType ??
        canonicalClrNameFromReference(source) ??
        canonicalClrNameFromReference(localDeclaredReference);
    const typeId = reference.typeId ?? source.typeId ?? localDeclaredReference?.typeId;
    if (reference.resolvedClrType === resolvedClrType &&
        reference.typeId === typeId) {
        return reference;
    }
    return {
        ...reference,
        ...(resolvedClrType !== undefined ? { resolvedClrType } : {}),
        ...(typeId !== undefined ? { typeId } : {}),
    };
};
/**
 * Lower a type, replacing objectType with referenceType
 */
export const lowerType = (type, ctx, _nameHint) => {
    switch (type.kind) {
        case "objectType": {
            // First, recursively lower any nested object types in members
            const loweredMembers = type.members.map((m) => {
                if (m.kind === "propertySignature") {
                    return {
                        ...m,
                        type: lowerType(m.type, ctx, m.name),
                    };
                }
                else if (m.kind === "methodSignature") {
                    return {
                        ...m,
                        parameters: m.parameters.map((p) => lowerParameter(p, ctx)),
                        returnType: m.returnType ? lowerType(m.returnType, ctx) : undefined,
                    };
                }
                return m;
            });
            const loweredObjectType = {
                ...type,
                members: loweredMembers,
            };
            return getOrCreateObjectTypeReference(loweredObjectType, ctx);
        }
        case "arrayType":
            return (() => {
                const cached = ctx.loweredTypeByIdentity.get(type);
                if (cached)
                    return cached;
                const loweredArray = {
                    ...type,
                };
                ctx.loweredTypeByIdentity.set(type, loweredArray);
                loweredArray.elementType = lowerType(type.elementType, ctx);
                return loweredArray;
            })();
        case "tupleType":
            return (() => {
                const cached = ctx.loweredTypeByIdentity.get(type);
                if (cached)
                    return cached;
                const loweredTuple = {
                    ...type,
                };
                ctx.loweredTypeByIdentity.set(type, loweredTuple);
                loweredTuple.elementTypes = type.elementTypes.map((et) => lowerType(et, ctx));
                return loweredTuple;
            })();
        case "functionType":
            return (() => {
                const cached = ctx.loweredTypeByIdentity.get(type);
                if (cached)
                    return cached;
                const loweredFunction = {
                    ...type,
                };
                ctx.loweredTypeByIdentity.set(type, loweredFunction);
                loweredFunction.parameters = type.parameters.map((p) => lowerParameter(p, ctx));
                loweredFunction.returnType = lowerType(type.returnType, ctx);
                return loweredFunction;
            })();
        case "unionType":
            return (() => {
                const cached = ctx.loweredTypeByIdentity.get(type);
                if (cached)
                    return cached;
                const loweredUnion = {
                    ...type,
                };
                ctx.loweredTypeByIdentity.set(type, loweredUnion);
                loweredUnion.types = type.types.map((t) => lowerType(t, ctx));
                return loweredUnion;
            })();
        case "intersectionType":
            return (() => {
                const cached = ctx.loweredTypeByIdentity.get(type);
                if (cached)
                    return cached;
                const loweredIntersection = {
                    ...type,
                };
                ctx.loweredTypeByIdentity.set(type, loweredIntersection);
                loweredIntersection.types = type.types.map((t) => lowerType(t, ctx));
                return loweredIntersection;
            })();
        case "dictionaryType":
            return (() => {
                const cached = ctx.loweredTypeByIdentity.get(type);
                if (cached)
                    return cached;
                const loweredDictionary = {
                    ...type,
                };
                ctx.loweredTypeByIdentity.set(type, loweredDictionary);
                loweredDictionary.keyType = lowerType(type.keyType, ctx);
                loweredDictionary.valueType = lowerType(type.valueType, ctx);
                return loweredDictionary;
            })();
        case "referenceType": {
            const localDeclaredReference = type.resolvedClrType === undefined
                ? ctx.localDeclaredTypeReferences.get(type.name)
                : undefined;
            const cachedByIdentity = ctx.loweredTypeByIdentity.get(type);
            if (cachedByIdentity) {
                return cachedByIdentity;
            }
            const stableKey = getReferenceLoweringStableKey(type);
            if (stableKey) {
                const cachedByStableKey = ctx.loweredReferenceByStableKey.get(stableKey);
                if (cachedByStableKey) {
                    const enrichedCachedReference = enrichReferenceIdentityMetadata(cachedByStableKey, type, localDeclaredReference);
                    ctx.loweredTypeByIdentity.set(type, enrichedCachedReference);
                    if (enrichedCachedReference !== cachedByStableKey) {
                        ctx.loweredReferenceByStableKey.set(stableKey, enrichedCachedReference);
                    }
                    return enrichedCachedReference;
                }
            }
            // Lower both typeArguments and structuralMembers
            const typeArgs = type.typeArguments;
            const structuralMembers = type.structuralMembers;
            const hasTypeArgs = typeArgs !== undefined && typeArgs.length > 0;
            const hasStructuralMembers = structuralMembers !== undefined && structuralMembers.length > 0;
            if (!hasTypeArgs && !hasStructuralMembers && !localDeclaredReference) {
                return type;
            }
            const loweredReference = {
                ...type,
                typeArguments: hasTypeArgs
                    ? typeArgs.map((ta) => lowerType(ta, ctx))
                    : undefined,
            };
            const enrichedLoweredReference = enrichReferenceIdentityMetadata(loweredReference, type, localDeclaredReference);
            ctx.loweredTypeByIdentity.set(type, enrichedLoweredReference);
            if (stableKey) {
                ctx.loweredReferenceByStableKey.set(stableKey, enrichedLoweredReference);
            }
            if (hasStructuralMembers) {
                enrichedLoweredReference.structuralMembers = structuralMembers.map((m) => lowerInterfaceMember(m, ctx));
            }
            return enrichedLoweredReference;
        }
        // These types don't contain nested types
        case "primitiveType":
        case "literalType":
        case "typeParameterType":
        case "voidType":
        case "anyType":
        case "unknownType":
        case "neverType":
            return type;
    }
};
/**
 * Lower a parameter
 */
export const lowerParameter = (param, ctx) => {
    return {
        ...param,
        type: param.type ? lowerType(param.type, ctx) : undefined,
        pattern: lowerPattern(param.pattern, ctx),
        initializer: param.initializer
            ? lowerExpression(param.initializer, ctx)
            : undefined,
    };
};
/**
 * Lower a type parameter
 */
export const lowerTypeParameter = (tp, ctx) => {
    return {
        ...tp,
        constraint: tp.constraint ? lowerType(tp.constraint, ctx) : undefined,
        default: tp.default ? lowerType(tp.default, ctx) : undefined,
        structuralMembers: tp.structuralMembers?.map((m) => lowerInterfaceMember(m, ctx)),
    };
};
/**
 * Lower an interface member
 *
 * IMPORTANT: We MUST lower objectType in all type positions before the emitter.
 * The emitter is not allowed to see IrObjectType nodes (soundness gate enforces this).
 */
export const lowerInterfaceMember = (member, ctx) => {
    switch (member.kind) {
        case "propertySignature": {
            return {
                ...member,
                type: lowerType(member.type, ctx, member.name),
            };
        }
        case "methodSignature":
            return {
                ...member,
                typeParameters: member.typeParameters?.map((tp) => lowerTypeParameter(tp, ctx)),
                parameters: member.parameters.map((p) => lowerParameter(p, ctx)),
                returnType: member.returnType
                    ? lowerType(member.returnType, ctx)
                    : undefined,
            };
    }
};
/**
 * Lower a pattern
 */
export const lowerPattern = (pattern, ctx) => {
    switch (pattern.kind) {
        case "identifierPattern":
            return {
                ...pattern,
                type: pattern.type ? lowerType(pattern.type, ctx) : undefined,
            };
        case "arrayPattern":
            return {
                ...pattern,
                elements: pattern.elements.map((e) => e
                    ? {
                        ...e,
                        pattern: lowerPattern(e.pattern, ctx),
                        defaultExpr: e.defaultExpr
                            ? lowerExpression(e.defaultExpr, ctx)
                            : undefined,
                    }
                    : undefined),
            };
        case "objectPattern":
            return {
                ...pattern,
                properties: pattern.properties.map((p) => {
                    if (p.kind === "property") {
                        return {
                            ...p,
                            value: lowerPattern(p.value, ctx),
                            defaultExpr: p.defaultExpr
                                ? lowerExpression(p.defaultExpr, ctx)
                                : undefined,
                        };
                    }
                    else {
                        return {
                            ...p,
                            pattern: lowerPattern(p.pattern, ctx),
                        };
                    }
                }),
            };
    }
};
//# sourceMappingURL=anon-type-lower-types.js.map