import type {
  ComposeCompileResult,
  SimulateRequest,
  SlotFinderRequirement,
  TrackedBalance,
} from '@lifi/compose-spec';

/**
 * Input for {@link buildSimulateRequest}: a compile result plus the fields a
 * simulation needs that a compiled flow does not carry.
 */
export interface BuildSimulateRequestInput {
  /**
   * A result from `builder.compile(...)` / `client.compile(...)`. Both
   * `success` and `partial` results carry a `transactionRequest`, so either is
   * valid input — the helper does not discriminate on `status`.
   */
  readonly result: ComposeCompileResult;
  /** EVM chain id. A compile result does not carry the target chain. */
  readonly chainId: number;
  /**
   * The transaction sender (`from`). A compile result does not carry the
   * signer, so it must be supplied here.
   */
  readonly signer: string;
  /**
   * Balances to watch across the simulation. These cannot be inferred from a
   * flow and must be supplied by the caller.
   */
  readonly trackedBalances: readonly TrackedBalance[];
  /** Optional funding instructions applied before simulation. */
  readonly requirements?: readonly SlotFinderRequirement[];
  /** Optional block to pin the simulation to. Omitted ⇒ chain head. */
  readonly block?: number;
  /**
   * Optional native value override. Defaults to the compiled
   * `transactionRequest.value` when omitted.
   */
  readonly value?: bigint | string;
}

/**
 * Assembles a {@link SimulateRequest} from a compile result and the
 * caller-supplied chain, signer, and tracked balances. Pure: performs no
 * network I/O, does not mutate its input, and returns a fresh object.
 *
 * Bridges the common case — simulate the transaction you just compiled —
 * without coupling raw-transaction simulation to the flow builder. The
 * `value` defaults to the compiled transaction's value unless overridden.
 *
 * @example
 * ```ts
 * const compiled = await builder.compile({ inputs: { ... }, signer: '0x1111...' });
 * const req = buildSimulateRequest({
 *   result: compiled,
 *   chainId: 1,
 *   signer: '0x1111111111111111111111111111111111111111',
 *   trackedBalances: [{ token: '0xA0b8...', owner: '0x1111...' }],
 * });
 * const sim = await sdk.client.simulate(req);
 * ```
 */
export const buildSimulateRequest = (
  input: BuildSimulateRequestInput,
): SimulateRequest => {
  const { result, chainId, signer, trackedBalances, requirements, block } =
    input;
  return {
    chainId,
    from: signer,
    to: result.transactionRequest.to,
    data: result.transactionRequest.data,
    value: input.value ?? result.transactionRequest.value,
    block,
    requirements,
    trackedBalances,
  };
};
