///|
// A managed Abstract Relational Comparison operation. The runtime owns the
// source-order ToPrimitive(number) sequence and the canonical IsLessThan
// result; the VM only transfers already-evaluated operands.

///|
pub(all) enum ExecutorRelationalOperator {
  ExecutorRelationalLessThan
  ExecutorRelationalGreaterThan
  ExecutorRelationalGreaterThanOrEqual
  ExecutorRelationalLessThanOrEqual
}

///|
pub enum ExecutorCoercingRelationalStart {
  ExecutorCoercingRelationalCompleted(Value)
  ExecutorCoercingRelationalSuspended(ExecutorCoercingRelationalRequest)
}

///|
pub struct ExecutorCoercingRelationalRequest {
  priv left : Value
  priv right : Value
  priv operator : ExecutorRelationalOperator
  priv loc : @token.Loc
}

///|
fn ExecutorCoercingRelationalRequest::ExecutorCoercingRelationalRequest(
  left~ : Value,
  right~ : Value,
  operator~ : ExecutorRelationalOperator,
  loc~ : @token.Loc,
) -> ExecutorCoercingRelationalRequest {
  { left, right, operator, loc, }
}

///|
fn executor_relational_primitive_result(
  operator : ExecutorRelationalOperator,
  left : Value,
  right : Value,
  interp? : Interpreter? = None,
) -> Value raise Error {
  let comparison = match operator {
    ExecutorRelationalLessThan | ExecutorRelationalGreaterThanOrEqual =>
      relational_is_less_than_primitives(left, right, interp~)
    ExecutorRelationalGreaterThan | ExecutorRelationalLessThanOrEqual =>
      relational_is_less_than_primitives(right, left, interp~)
  }
  match (operator, comparison) {
    (ExecutorRelationalLessThan, RelationalLess)
    | (ExecutorRelationalGreaterThan, RelationalLess)
    | (ExecutorRelationalGreaterThanOrEqual, RelationalNotLess)
    | (ExecutorRelationalLessThanOrEqual, RelationalNotLess) => Bool(true)
    _ => Bool(false)
  }
}

///|
pub fn begin_executor_coercing_relational(
  left : Value,
  right : Value,
  loc : @token.Loc,
  operator : ExecutorRelationalOperator,
) -> ExecutorCoercingRelationalStart raise Error {
  if is_js_object(left) || is_js_object(right) {
    ExecutorCoercingRelationalSuspended(
      ExecutorCoercingRelationalRequest(left~, right~, operator~, loc~),
    )
  } else {
    ExecutorCoercingRelationalCompleted(
      executor_relational_primitive_result(operator, left, right),
    )
  }
}

///|
priv enum ExecutorCoercingRelationalPhase {
  ExecutorCoercingRelationalNeedLeft
  ExecutorCoercingRelationalAwaitLeft(ExecutorToPrimitiveOperation)
  ExecutorCoercingRelationalHaveLeft(Value)
  ExecutorCoercingRelationalAwaitRight(Value, ExecutorToPrimitiveOperation)
  ExecutorCoercingRelationalComplete(Value)
}

///|
priv struct ExecutorCoercingRelationalFrame {
  request : ExecutorCoercingRelationalRequest
  mut phase : ExecutorCoercingRelationalPhase
}

///|
fn ExecutorCoercingRelationalFrame::ExecutorCoercingRelationalFrame(
  request : ExecutorCoercingRelationalRequest,
) -> ExecutorCoercingRelationalFrame {
  { request, phase: ExecutorCoercingRelationalNeedLeft, }
}

///|
fn ExecutorCoercingRelationalFrame::finish_right(
  self : ExecutorCoercingRelationalFrame,
  interp : Interpreter,
  left : Value,
  right : Value,
) -> ExecutorActivationStep raise Error {
  let result = executor_relational_primitive_result(
    self.request.operator,
    left,
    right,
    interp=Some(interp),
  )
  self.phase = ExecutorCoercingRelationalComplete(result)
  ExecutorActivationNormal(result)
}

///|
fn ExecutorCoercingRelationalFrame::step_frame(
  self : ExecutorCoercingRelationalFrame,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  match self.phase {
    ExecutorCoercingRelationalNeedLeft => {
      let operation = ExecutorToPrimitiveOperation(
        self.request.left,
        ExecutorToPrimitiveNumber,
        self.request.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(value) => {
          self.phase = ExecutorCoercingRelationalHaveLeft(value)
          self.step_frame(interp)
        }
        step => {
          self.phase = ExecutorCoercingRelationalAwaitLeft(operation)
          step
        }
      }
    }
    ExecutorCoercingRelationalAwaitLeft(operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(value) => {
          self.phase = ExecutorCoercingRelationalHaveLeft(value)
          self.step_frame(interp)
        }
        step => {
          self.phase = ExecutorCoercingRelationalAwaitLeft(operation)
          step
        }
      }
    ExecutorCoercingRelationalHaveLeft(left) => {
      let operation = ExecutorToPrimitiveOperation(
        self.request.right,
        ExecutorToPrimitiveNumber,
        self.request.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(value) =>
          self.finish_right(interp, left, value)
        step => {
          self.phase = ExecutorCoercingRelationalAwaitRight(left, operation)
          step
        }
      }
    }
    ExecutorCoercingRelationalAwaitRight(left, operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(value) =>
          self.finish_right(interp, left, value)
        step => {
          self.phase = ExecutorCoercingRelationalAwaitRight(left, operation)
          step
        }
      }
    ExecutorCoercingRelationalComplete(value) => {
      ignore(value)
      raise @errors.InternalError(
        message="coercing relational frame stepped after completion",
      )
    }
  }
}

///|
impl ExecutorActivationFrame for ExecutorCoercingRelationalFrame with fn step(
  self,
  interp,
) {
  self.step_frame(interp)
}

///|
impl ExecutorActivationFrame for ExecutorCoercingRelationalFrame with fn deliver_activation_completion(
  self,
  completion,
) {
  match self.phase {
    ExecutorCoercingRelationalAwaitLeft(operation)
    | ExecutorCoercingRelationalAwaitRight(_, operation) =>
      operation.deliver_activation_completion(completion)
    ExecutorCoercingRelationalNeedLeft
    | ExecutorCoercingRelationalHaveLeft(_)
    | ExecutorCoercingRelationalComplete(_) =>
      raise @errors.InternalError(
        message="coercing relational received an unexpected child completion",
      )
  }
}

///|
// This narrow start/request wrapper preserves the existing <= runtime seam for
// focused tests while delegating execution to the shared relational operation.
pub enum ExecutorCoercingLessThanOrEqualStart {
  ExecutorCoercingLessThanOrEqualCompleted(Value)
  ExecutorCoercingLessThanOrEqualSuspended(
    ExecutorCoercingLessThanOrEqualRequest
  )
}

///|
pub struct ExecutorCoercingLessThanOrEqualRequest {
  priv left : Value
  priv right : Value
  priv loc : @token.Loc
}

///|
fn ExecutorCoercingLessThanOrEqualRequest::ExecutorCoercingLessThanOrEqualRequest(
  left~ : Value,
  right~ : Value,
  loc~ : @token.Loc,
) -> ExecutorCoercingLessThanOrEqualRequest {
  { left, right, loc, }
}

///|
pub fn begin_executor_coercing_less_than_or_equal(
  left : Value,
  right : Value,
  loc : @token.Loc,
) -> ExecutorCoercingLessThanOrEqualStart raise Error {
  match
    begin_executor_coercing_relational(
      left,
      right,
      loc,
      ExecutorRelationalLessThanOrEqual,
    ) {
    ExecutorCoercingRelationalCompleted(value) =>
      ExecutorCoercingLessThanOrEqualCompleted(value)
    ExecutorCoercingRelationalSuspended(request) =>
      ExecutorCoercingLessThanOrEqualSuspended(
        ExecutorCoercingLessThanOrEqualRequest(
          left=request.left,
          right=request.right,
          loc=request.loc,
        ),
      )
  }
}

///|
fn executor_coercing_relational_request_from_less_than_or_equal(
  request : ExecutorCoercingLessThanOrEqualRequest,
) -> ExecutorCoercingRelationalRequest {
  ExecutorCoercingRelationalRequest(
    left=request.left,
    right=request.right,
    operator=ExecutorRelationalLessThanOrEqual,
    loc=request.loc,
  )
}