/**
 * New expression converter
 *
 * Phase 15 (Alice's spec): Two-pass resolution for deterministic constructor typing.
 */
import * as ts from "typescript";
import { getSourceSpan, extractTypeArguments, checkIfRequiresSpecialization, } from "../helpers.js";
import { convertExpression } from "../../../expression-converter.js";
import { unwrapCallSiteArgumentModifier, applyCallSiteArgumentModifiers, extractArgumentPassing, } from "./call-site-analysis.js";
import { finalizeInvocationMetadata, normalizeFinalizedInvocationArguments, } from "./invocation-finalization.js";
import { buildSourceBackedConstructorParameterTypes } from "./source-backed-constructor-metadata.js";
const attachConstructorCalleeIdentity = (resolvedReturnType, calleeType) => {
    if (!resolvedReturnType ||
        resolvedReturnType.kind !== "referenceType" ||
        resolvedReturnType.typeId ||
        resolvedReturnType.resolvedClrType) {
        return resolvedReturnType;
    }
    if (!calleeType ||
        calleeType.kind !== "referenceType" ||
        (!calleeType.typeId && !calleeType.resolvedClrType)) {
        return resolvedReturnType;
    }
    return {
        ...resolvedReturnType,
        name: calleeType.name,
        ...(calleeType.typeId ? { typeId: calleeType.typeId } : {}),
        ...(calleeType.resolvedClrType
            ? { resolvedClrType: calleeType.resolvedClrType }
            : {}),
    };
};
// DELETED: getConstructedType - Phase 15 uses resolveCall.returnType instead
/**
 * Convert new expression
 *
 * Phase 15 (Alice's spec): Two-pass resolution for deterministic constructor typing.
 * 1) Resolve once (without argTypes) to get parameter types for expected-type threading.
 * 2) Convert non-lambda arguments first, collecting argTypes for inference.
 * 3) Re-resolve with argTypes to infer constructor type parameters.
 * 4) Convert lambda arguments using instantiated parameter types.
 * 5) Final resolve with full argTypes.
 * 6) inferredType MUST be finalResolved.returnType.
 */
export const convertNewExpression = (node, ctx, expectedType) => {
    // Extract explicit type arguments (for IR output, not inference)
    const typeArguments = extractTypeArguments(node, ctx);
    const requiresSpecialization = checkIfRequiresSpecialization(node, ctx);
    // Convert callee (the constructor expression)
    const callee = convertExpression(node.expression, ctx, undefined);
    // Two-pass resolution (matching convertCallExpression pattern)
    const typeSystem = ctx.typeSystem;
    const sigId = ctx.binding.resolveConstructorSignature(node);
    const argumentCount = node.arguments?.length ?? 0;
    const callSiteArgModifiers = new Array(argumentCount).fill(undefined);
    // Extract explicit type arguments from call site
    const explicitTypeArgs = node.typeArguments
        ? node.typeArguments.map((ta) => typeSystem.typeFromSyntax(ctx.binding.captureTypeSyntax(ta)))
        : undefined;
    // Initial resolution (without argTypes) for parameter type threading
    const initialResolved = sigId
        ? typeSystem.resolveCall({
            sigId,
            argumentCount,
            explicitTypeArgs,
            expectedReturnType: expectedType,
        })
        : undefined;
    const initialParameterTypes = initialResolved?.parameterTypes;
    const isLambdaArg = (expr) => {
        if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr))
            return true;
        if (ts.isParenthesizedExpression(expr))
            return isLambdaArg(expr.expression);
        return false;
    };
    // Pass 1: convert non-lambda arguments and collect argTypes for inference
    const argsWorking = new Array(argumentCount);
    const argTypesForInference = Array(argumentCount).fill(undefined);
    const args = node.arguments ?? [];
    for (let index = 0; index < args.length; index++) {
        const arg = args[index];
        if (!arg)
            continue;
        const expectedType = initialParameterTypes?.[index];
        if (ts.isSpreadElement(arg)) {
            const spreadExpr = convertExpression(arg.expression, ctx, undefined);
            argsWorking[index] = {
                kind: "spread",
                expression: spreadExpr,
                inferredType: spreadExpr.inferredType,
                sourceSpan: getSourceSpan(arg),
            };
            continue;
        }
        const unwrapped = unwrapCallSiteArgumentModifier(arg);
        if (unwrapped.modifier) {
            callSiteArgModifiers[index] = unwrapped.modifier;
        }
        if (isLambdaArg(unwrapped.expression)) {
            // Defer lambda conversion until after generic type arg inference
            continue;
        }
        const converted = convertExpression(unwrapped.expression, ctx, expectedType);
        argsWorking[index] = converted;
        argTypesForInference[index] = converted.inferredType;
    }
    // Re-resolve with argTypes to infer constructor type parameters
    const lambdaContextResolved = sigId
        ? typeSystem.resolveCall({
            sigId,
            argumentCount,
            explicitTypeArgs,
            argTypes: argTypesForInference,
            expectedReturnType: expectedType,
        })
        : initialResolved;
    const parameterTypesForLambdaContext = lambdaContextResolved?.parameterTypes ?? initialParameterTypes;
    // Pass 2: convert lambda arguments with inferred parameter types
    for (let index = 0; index < args.length; index++) {
        if (argsWorking[index])
            continue;
        const arg = args[index];
        if (!arg)
            continue;
        if (ts.isSpreadElement(arg))
            continue;
        const unwrapped = unwrapCallSiteArgumentModifier(arg);
        if (unwrapped.modifier) {
            callSiteArgModifiers[index] = unwrapped.modifier;
        }
        if (!isLambdaArg(unwrapped.expression))
            continue;
        const expectedType = parameterTypesForLambdaContext?.[index];
        const lambdaExpectedType = expectedType?.kind === "functionType"
            ? expectedType
            : expectedType
                ? (typeSystem.delegateToFunctionType(expectedType) ?? expectedType)
                : undefined;
        argsWorking[index] = convertExpression(unwrapped.expression, ctx, lambdaExpectedType);
    }
    // Fill any remaining undefined slots (shouldn't happen, but be safe)
    const convertedArgs = argsWorking.map((a, index) => {
        if (a)
            return a;
        const arg = args[index];
        if (!arg) {
            throw new Error("ICE: new expression argument conversion produced a hole");
        }
        if (ts.isSpreadElement(arg)) {
            const spreadExpr = convertExpression(arg.expression, ctx, undefined);
            return {
                kind: "spread",
                expression: spreadExpr,
                inferredType: spreadExpr.inferredType,
                sourceSpan: getSourceSpan(arg),
            };
        }
        const unwrapped = unwrapCallSiteArgumentModifier(arg);
        if (unwrapped.modifier) {
            callSiteArgModifiers[index] = unwrapped.modifier;
        }
        return convertExpression(unwrapped.expression, ctx, undefined);
    });
    // Collect final argTypes
    const argTypes = convertedArgs.map((a) => a.kind === "spread" ? undefined : a.inferredType);
    // Final resolution with full argTypes
    const finalResolved = sigId
        ? typeSystem.resolveCall({
            sigId,
            argumentCount,
            declaringClrType: callee.kind === "identifier" ? callee.resolvedClrType : undefined,
            explicitTypeArgs,
            argTypes,
            expectedReturnType: expectedType,
        })
        : lambdaContextResolved;
    const finalReturnType = attachConstructorCalleeIdentity(finalResolved?.returnType, callee.inferredType);
    // Phase 15: inferredType MUST be finalResolved.returnType
    // If sigId is missing, use unknownType (do not fabricate a nominal type)
    const sourceBackedConstructorParameterTypes = buildSourceBackedConstructorParameterTypes({
        sourceNode: node,
        constructorExpression: node.expression,
        callee,
        constructedType: finalResolved?.returnType,
        argumentCount,
        actualArgTypes: argTypes,
        ctx,
    });
    const finalizedInvocationMetadata = finalizeInvocationMetadata({
        ctx,
        callee,
        receiverType: callee.kind === "memberAccess" ? callee.object.inferredType : undefined,
        callableType: callee.inferredType?.kind === "functionType"
            ? callee.inferredType
            : undefined,
        argumentCount,
        argTypes,
        explicitTypeArgs,
        expectedType,
        boundGlobalParameterTypes: undefined,
        authoritativeBoundGlobalSurfaceParameterTypes: undefined,
        authoritativeBoundGlobalReturnType: undefined,
        sourceBackedParameterTypes: sourceBackedConstructorParameterTypes?.parameterTypes,
        sourceBackedSurfaceParameterTypes: sourceBackedConstructorParameterTypes?.surfaceParameterTypes,
        sourceBackedReturnType: finalReturnType,
        ambientBoundGlobalSurfaceParameterTypes: undefined,
        authoritativeDirectParameterTypes: undefined,
        resolvedParameterTypes: finalResolved?.parameterTypes,
        resolvedSurfaceParameterTypes: finalResolved?.surfaceParameterTypes,
        resolvedReturnType: finalReturnType,
        fallbackParameterTypes: initialParameterTypes,
        fallbackSurfaceParameterTypes: initialParameterTypes,
        exactParameterCandidates: [],
        exactSurfaceParameterCandidates: [],
        exactReturnCandidates: [],
        preserveDirectSurfaceIdentity: false,
    });
    const inferredType = finalizedInvocationMetadata.sourceBackedReturnType ??
        finalReturnType ?? {
        kind: "unknownType",
    };
    const parameterTypes = finalizedInvocationMetadata.parameterTypes ?? initialParameterTypes;
    const surfaceParameterTypes = finalizedInvocationMetadata.surfaceParameterTypes ?? parameterTypes;
    const finalizedArguments = normalizeFinalizedInvocationArguments(convertedArgs, parameterTypes, surfaceParameterTypes, ctx);
    const argumentPassingBase = finalResolved
        ? finalResolved.parameterModes.slice(0, argumentCount)
        : extractArgumentPassing(node, ctx);
    const argumentPassing = applyCallSiteArgumentModifiers(argumentPassingBase, callSiteArgModifiers, argumentCount, ctx, node);
    // Phase 18: IrNewExpression.typeArguments must include inferred type arguments.
    // The emitter relies on this field to emit generic constructor calls (e.g., new Box<int>(...)).
    const inferredTypeArguments = inferredType.kind === "referenceType"
        ? inferredType.typeArguments
        : undefined;
    const typeArgumentsForIr = typeArguments ??
        (inferredTypeArguments && inferredTypeArguments.length > 0
            ? inferredTypeArguments
            : undefined);
    return {
        kind: "new",
        callee,
        arguments: finalizedArguments,
        inferredType,
        sourceSpan: getSourceSpan(node),
        signatureId: sigId,
        resolutionExpectedReturnType: expectedType,
        argumentPassing,
        parameterTypes,
        surfaceParameterTypes,
        sourceBackedParameterTypes: finalizedInvocationMetadata.sourceBackedParameterTypes,
        sourceBackedSurfaceParameterTypes: finalizedInvocationMetadata.sourceBackedSurfaceParameterTypes,
        sourceBackedRestParameter: sourceBackedConstructorParameterTypes?.restParameter,
        sourceBackedReturnType: finalizedInvocationMetadata.sourceBackedReturnType,
        typeArguments: typeArgumentsForIr,
        requiresSpecialization,
        surfaceRestParameter: sourceBackedConstructorParameterTypes?.restParameter ??
            finalResolved?.surfaceRestParameter,
    };
};
//# sourceMappingURL=new-converter.js.map