import type {
  TypedData,
  TransactionRequest,
  SignedTypedData,
  ExecutionType,
} from './api.js'
import type { Token } from './tokens/index.js'

/**
 * How a fee was calculated / the role of the recipient in the split.
 *
 * - `FIXED` — a fixed LI.FI cut applied alongside the integrator's own fee.
 * - `SHARED` — a single shared pool divided between LI.FI and the integrator.
 * - `DYNAMIC` — fee resolved at quote time from configured rules (e.g. dynamic
 *   stablecoin pricing).
 * - `INTERMEDIARY` — a third-party recipient that carves a configured share
 *   from the integrator pool (e.g. a widget or aggregator).
 * - `DISTRIBUTION` — a partner-specified extra recipient added in parallel to
 *   the integrator/intermediary split via the `distributionFees` request param.
 *
 * The union is expected to grow over time. When pattern-matching with a
 * `switch`, always include a `default` branch so adding a new value remains
 * a non-breaking minor for consumers.
 */
export type FeeSplitType =
  | 'FIXED'
  | 'SHARED'
  | 'DYNAMIC'
  | 'INTERMEDIARY'
  | 'DISTRIBUTION'

export interface FeeRecipient {
  /** Recipient identifier. Polymorphic — interpret in conjunction with `type`:
   * - `'lifi'` for the LI.FI platform recipient,
   * - the integrator id (e.g. `'jumper'`) for the integrator recipient,
   * - the intermediary id (e.g. `'mesh'`) when `type === 'INTERMEDIARY'`,
   * - the recipient wallet address when `type === 'DISTRIBUTION'`.
   *
   * Do not display this value directly in user-facing UI without
   * `type`-aware formatting — for `DISTRIBUTION` rows the value is a
   * raw hex address. */
  name: string

  /** Absolute fee amount, a big number in source token base units (string-encoded). */
  fee: string

  /** Fee calculation type. Absent when not statically determinable (e.g.
   * an integrator entry whose `feeType` could not be resolved from the
   * matching planned fee cost). Consumers should null-check before reading. */
  type?: FeeSplitType

  /** Recipient wallet address on the source chain. */
  walletAddress?: string
}

export interface FeeSplit {
  /** LI.FI's slice of THIS `FeeCost.amount`. */
  lifiFee: string

  /** The integrator's slice of THIS `FeeCost.amount` — the integrator's
   * portion only, NOT including any intermediary or distribution amounts. */
  integratorFee: string

  /** The intermediary's slice of THIS `FeeCost.amount` when an intermediary
   * participates in the split. Absent otherwise. */
  intermediaryFee?: string

  /** Per-recipient breakdown of THIS `FeeCost.amount`. Source of truth for
   * new consumers — the aggregate fields above are disjoint slices kept
   * for backward compatibility with 2-recipient consumers.
   *
   * Note: partner-specified distribution recipients are emitted as
   * separate `FeeCost` entries (one per receiver, `name: "Fee Forward"`)
   * at quote/route estimation time. At status-response time the same
   * recipients may appear merged on the integrator's `FeeCost` for
   * compactness. Iterate `estimate.feeCosts[]` to discover all
   * recipients across both shapes. */
  recipients?: FeeRecipient[]
}

export interface FeeCost {
  name: string
  description: string
  percentage: string
  token: Token
  amount: string
  amountUSD: string
  included: boolean

  feeSplit?: FeeSplit
}

export interface GasCost {
  type: 'SUM' | 'APPROVE' | 'SEND' | 'FEE'
  price: string // suggested current standard price for chain
  estimate: string // estimate how much gas will be needed
  limit: string // suggested gas limit (estimate +25%)
  amount: string // estimate * price = amount of tokens that will be needed
  amountUSD: string // usd value of token amount
  token: Token // the used gas token
}

// ACTION
export interface Action {
  fromChainId: number
  fromAmount: string
  fromToken: Token
  fromAddress?: string

  toChainId: number
  toToken: Token
  toAddress?: string

  slippage?: number
}

// ESTIMATE
export interface Estimate {
  tool: string
  fromAmount: string
  fromAmountUSD?: string
  toAmount: string
  toAmountMin: string
  toAmountUSD?: string
  approvalAddress: string
  feeCosts?: FeeCost[]
  /** This is a list to account for approval gas costs and transaction gas costs. However, approval gas costs are not integrated yet. */
  gasCosts?: GasCost[]
  /** Estimated duration in seconds */
  executionDuration: number
  /** Optional flag to indicate approval reset requirement for legacy ERC-20 tokens */
  approvalReset?: boolean
  /** Optional flag to skip approval in the cases where we only need to sign the message (e.g. Hyperliquid)*/
  skipApproval?: boolean
  /** Optional flag to skip permit usage when the tx doesn't include diamond interaction (some HyperEVM txs) */
  skipPermit?: boolean
}

// STEP
export const _StepType = [
  'lifi',
  'swap',
  'cross',
  'protocol',
  'custom',
] as const
export type StepType = (typeof _StepType)[number]
export type StepTool = string
export type StepToolDetails = {
  key: string
  name: string
  logoURI: string
}

type StepInformationBase = {
  tool: string
  type: string
  action: Action
  estimate: Estimate
}

export type StepInformation = StepInformationBase & {
  createdAt: Date
  gasLimit: string
  stepId: string
  transactionId: string
  intermediateActions: StepInformationBase[]
  integrator?: string
  relatedLifiSteps?: string[]
}

export interface StepBase {
  id: string
  type: StepType
  tool: StepTool
  toolDetails: StepToolDetails
  integrator?: string
  /** Intermediary identifier set when the route was generated with a multi-party fee split.
   *  Threaded through to /stepTransaction so the same split can be reproduced at tx-generation time. */
  intermediary?: string
  referrer?: string
  action: Action
  estimate?: Estimate
  executionType?: ExecutionType
  transactionRequest?: TransactionRequest
  transactionId?: string
  /**
   * EIP-712 Typed Data
   * @link https://eips.ethereum.org/EIPS/eip-712
   */
  typedData?: TypedData[]
}

export interface DestinationCallInfo {
  toContractAddress: string
  toContractCallData: string
  toFallbackAddress: string
  callDataGasLimit: string
}

export type CallAction = Action & DestinationCallInfo

export interface SwapStep extends StepBase {
  type: 'swap'
  action: Action
  estimate: Estimate
}

export interface CrossStep extends StepBase {
  type: 'cross'
  action: Action
  estimate: Estimate
}

export interface ProtocolStep extends StepBase {
  type: 'protocol'
  action: Action
  estimate: Estimate
}

export interface CustomStep extends StepBase {
  type: 'custom'
  action: CallAction
  estimate: Estimate
}

export type Step = SwapStep | CrossStep | CustomStep | ProtocolStep

export interface LiFiStep extends Omit<Step, 'type'> {
  type: 'lifi'
  includedSteps: Step[]
}

export interface SignedLiFiStep extends LiFiStep {
  typedData: SignedTypedData[]
}

export function isSwapStep(step: Step): step is SwapStep {
  return step.type === 'swap'
}

export function isCrossStep(step: Step): step is CrossStep {
  return step.type === 'cross'
}

export function isProtocolStep(step: Step): step is ProtocolStep {
  return step.type === 'protocol'
}

export function isCustomStep(step: Step): step is CustomStep {
  return step.type === 'custom'
}
