import { surroundingAgent } from '../host-defined/engine.mts';
import { ObjectValue } from '../value.mts';
import { Q, X } from '../completion.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { ArgumentListEvaluation } from './all.mts';
import {
  Assert,
  Construct,
  GetNewTarget,
  GetThisEnvironment,
  IsConstructor,
  InitializeInstanceElements,
  isECMAScriptFunctionObject,
  type FunctionObject,
} from '#self';
import { FunctionEnvironmentRecord } from '#self';

/** https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation */
// SuperCall : `super` Arguments
export function* Evaluate_SuperCall({ Arguments }: ParseNode.SuperCall) {
  // 1. Let newTarget be GetNewTarget().
  const newTarget = GetNewTarget();
  // 2. Assert: Type(newTarget) is Object.
  Assert(newTarget instanceof ObjectValue);
  // 3. Let func be ! GetSuperConstructor().
  const func = X(GetSuperConstructor());
  // 4. Let argList be ? ArgumentListEvaluation of Arguments.
  const argList = Q(yield* ArgumentListEvaluation(Arguments));
  // 5. If IsConstructor(func) is false, throw a TypeError exception.
  if (!IsConstructor(func)) {
    return surroundingAgent.Throw('TypeError', 'NotAConstructor', func);
  }
  // 6. Let result be ? Construct(func, argList, newTarget).
  const result = Q(yield* Construct(func, argList, newTarget as FunctionObject));
  // 7. Let thisER be GetThisEnvironment().
  const thisER = GetThisEnvironment();
  // 8. Assert: thisER is a Function Environment Record.
  Assert(thisER instanceof FunctionEnvironmentRecord);
  // 8. Perform ? thisER.BindThisValue(result).
  Q(thisER.BindThisValue(result));
  // 9. Let F be thisER.[[FunctionObject]].
  const F = thisER.FunctionObject;
  // 10. Assert: F is an ECMAScript function object.
  Assert(isECMAScriptFunctionObject(F));
  // 11. Perform ? InitializeInstanceElements(result, F).
  Q(yield* InitializeInstanceElements(result, F));
  // 12. Return result.
  return result;
}

/** https://tc39.es/ecma262/#sec-getsuperconstructor */
function GetSuperConstructor() {
  // 1. Let envRec be GetThisEnvironment().
  const envRec = GetThisEnvironment();
  // 2. Assert: envRec is a function Environment Record.
  Assert(envRec instanceof FunctionEnvironmentRecord);
  // 3. Let activeFunction be envRec.[[FunctionObject]].
  const activeFunction = envRec.FunctionObject;
  // 4. Assert: activeFunction is an ECMAScript function object.
  Assert(isECMAScriptFunctionObject(activeFunction));
  // 5. Let superConstructor be ! activeFunction.[[GetPrototypeOf]]().
  const superConstructor = X(activeFunction.GetPrototypeOf());
  // 6. Return superConstructor.
  return superConstructor;
}
