import type { VerificationDescriptor } from './descriptor.js';
import type { InputSpec, Precondition } from './flow.js';
import type { Flow } from './flowSchema.js';
import type { Outputs } from './output.js';
import type { ContinuationPlanWire, Phase1ArtifactWire } from './zodSchemas.js';

export type SimulationPolicy = 'strict' | 'allow-revert';

/**
 * Destination for sweeping proxy-held terminal resources after execution.
 * Either a literal EVM address or a `{ $ref }` context reference
 * (e.g. `{ $ref: "context.sender" }`).
 */
export type SweepTo = string | { readonly $ref: string };

export interface ComposeRun {
  readonly inputs: Record<string, InputSpec>;
  readonly preconditions?: readonly Precondition[];
  readonly signer: string;
  readonly assumptions?: Record<string, bigint>;
  readonly referrer?: string;
  readonly integratorFeeBps?: number;
  readonly maxPriceImpactBps?: number;
  readonly sweepTo?: SweepTo;
  readonly simulationPolicy?: SimulationPolicy;
  /**
   * Also simulate the final user-facing transaction. Defaults to `false`;
   * reverts honour `simulationPolicy` and a success is echoed back as
   * `userProgramSimulation`.
   */
  readonly simulateUserProgram?: boolean;
  readonly checkOnChainAllowances?: boolean;
}

export interface ComposeCompileRequest {
  readonly flow: Flow;
  readonly run: ComposeRun;
}

export interface PriceImpact {
  readonly inputValueUsd: number;
  readonly outputValueUsd: number;
  readonly impactBps: number;
  readonly unpricedInputs: readonly string[];
  readonly unpricedOutputs: readonly string[];
}

export interface ApprovalEntry {
  readonly token: string;
  readonly spender: string;
  readonly amount: string;
  readonly transactionRequest: {
    readonly to: string;
    readonly data: string;
    readonly value: '0';
  };
}

export interface SimulationRevert {
  readonly code: number;
  readonly rawErrorBytes: string;
  readonly decodeResult?: {
    readonly errorCandidates?: readonly {
      readonly decodedErrorSignature: string;
      readonly decodedParams: readonly string[];
    }[];
    readonly error?: string;
  };
}

export interface ComposeTransactionRequest {
  readonly to: string;
  readonly chainId: number;
  readonly data: string;
  readonly value: string;
  readonly gasLimit?: string;
}

// Hand-mirrored from `ComposeActionSchema`, the OpenAPI source of truth in
// `@lifi/api-schemas`.
export interface ComposeAction {
  readonly kind: 'transaction';
  readonly purpose: 'approval' | 'execute' | 'fundEscrow';
  readonly submitter: 'signer' | 'anyone';
  readonly transactionRequest: ComposeTransactionRequest;
  readonly nodeId?: string;
}

// Continuation fields shared by the success and partial-error responses, so the
// two shapes cannot drift. `phase1ByNode` / `continuationPlans` reuse the
// Zod-inferred wire types from `zodSchemas.ts`, pinning these SDK-facing types
// to the server contract rather than hand-transcribing them.
interface ComposeContinuationData {
  // Phase-1 action to sign/fund now, keyed by settle node. Absent for an atomic flow.
  readonly phase1ByNode?: Record<string, Phase1ArtifactWire>;
  // Self-serve second leg to re-submit later, keyed by settle node. Present only
  // for providers that hand the next leg back to the caller (manual-relay).
  readonly continuationPlans?: Record<string, ContinuationPlanWire>;
}

// One funding leg the compiled fill consumes: the resource input's name, its
// token (native → zero address) and chain, and the statically resolved amount
// as a decimal string (verified continuations, plan GD-8, seam C5). `nodeId`
// names the `continuation.settle` node this input funds (the `phase1ByNode` /
// `actions` key); absent when the input funds no settle leg (e.g. a plain
// swap's input).
export interface SpendEntry {
  readonly input: string;
  readonly token: string;
  readonly chainId: number;
  readonly amount: string;
  readonly nodeId?: string;
}

// One fee row per (input, recipient), as the compiled fill charges it.
// Hand-mirrored from api-schemas' `ComposeFeeSchema` (the OpenAPI source of
// truth; this package cannot depend on it). `recipient` names WHO is charged
// — never an address; the address is the sibling `recipientAddress`. `amount`
// is absent when the gross is only known on-chain at execution time (a
// balance-materialiser input).
export interface ComposeFee {
  readonly input: string;
  // Zero address for native, otherwise the ERC-20 contract.
  readonly token: string;
  readonly recipient: 'lifi' | 'integrator';
  readonly recipientAddress: string;
  readonly bps: number;
  readonly amount?: string;
}

// The optional user-program simulation result, present only when the request
// opted in via `run.simulateUserProgram` and the simulation succeeded.
// Hand-mirrored from api-schemas' `ComposeUserProgramSimulationOkSchema`.
export interface ComposeUserProgramSimulation {
  readonly status: 'ok';
  readonly returnData: string;
}

// The solver-extractable fill (verified continuations, GD-8/GD-6, seam C5),
// present on every compiled success. Hand-mirrored to api-schemas' `fill`
// object (the OpenAPI source of truth; this package cannot depend on it).
// `spends` is what the fill consumes per funding input; `validUntilMs` is the
// fill freshness bound (earliest prepared-node expiry) — ABSENT = no expiry.
// The fill is transaction-scoped; the leg-scoped C4 verification echo lives in
// the sibling `verificationByNode` record.
export interface ComposeFill {
  readonly spends: readonly SpendEntry[];
  readonly validUntilMs?: number;
}

export interface ComposeCompileSuccessData extends ComposeContinuationData {
  // Every linear resource output port, consumed or terminal, keyed
  // `<nodeId>.<portName>`. See `Output` for the per-entry rules. Hand-mirrored
  // from api-schemas' `OutputSchema` (the OpenAPI source of truth; this
  // package cannot depend on it); the SDK decodes the wire strings to `bigint`.
  readonly outputs: Outputs;
  // The OpenAPI source of truth documents the optionality and funding modes.
  readonly transactionRequest?: ComposeTransactionRequest;
  readonly actions?: readonly ComposeAction[];
  readonly userProxy: string;
  readonly priceImpact?: PriceImpact;
  readonly approvals?: readonly ApprovalEntry[];
  // The per-(input, recipient) fee rows the compiled fill charges. Absent when
  // no fee applies.
  readonly fees?: readonly ComposeFee[];
  // The optional user-program simulation echo, present only when the request
  // opted in via `run.simulateUserProgram` and the simulation succeeded.
  readonly userProgramSimulation?: ComposeUserProgramSimulation;
  // GD-8 settlement-fill surface (seam C5): the solver-extractable `fill`,
  // present on every success response — the funding legs it consumes and the
  // freshness bound (absent = no expiry).
  readonly fill?: ComposeFill;
  // The C4 verification echo, keyed by the flow node that carried the
  // commitment (the `continuation.commitment` node on a settlement
  // re-submission, the `continuation.settle` node on authoring-time provider
  // verification). Present only when the compile committed a validation
  // program; today at most one entry (the compiler rejects multi-node
  // committed verification).
  readonly verificationByNode?: Record<string, VerificationDescriptor>;
}

export interface ComposeCompilePartialData extends ComposeContinuationData {
  // Mirrors `ComposeCompileSuccessData.outputs`. A revert measured nothing, so
  // estimates are quotes and minimums are op-sourced.
  readonly outputs: Outputs;
  readonly transactionRequest: ComposeTransactionRequest;
  readonly userProxy: string;
  readonly simulationRevert: SimulationRevert;
  readonly approvals?: readonly ApprovalEntry[];
  readonly actions?: readonly ComposeAction[];
  // The per-(input, recipient) fee rows the compiled fill charges. Absent when
  // no fee applies.
  readonly fees?: readonly ComposeFee[];
  // The optional user-program simulation echo, present only when the request
  // opted in via `run.simulateUserProgram` and the simulation succeeded.
  readonly userProgramSimulation?: ComposeUserProgramSimulation;
}

export type ComposeCompileResult =
  | (ComposeCompileSuccessData & { readonly status: 'success' })
  | (ComposeCompilePartialData & {
      readonly status: 'partial';
      readonly error: { readonly kind: string; readonly message: string };
    });
