import { CONFIG } from "./config"
import {
  QuoteResult,
  QuoteApiParams,
  QuoteApiResponse,
  QuoteRequest,
} from "./types"
import { WidoError } from "./WidoError"

/**
 * Get an easy-to-consume quote and a unsigned transaction for zapping any ERC20 token,
 * on behalf of `user`, spending `fromToken` and receiving `toToken` in exchange.
 *
 * Note: `user` and `amount` are required to receive a valid unsigned tx.
 */
export async function quote(request: QuoteRequest): Promise<QuoteResult> {
  const {
    user,
    fromChainId,
    fromToken,
    toChainId,
    toToken,
    amount,
    slippagePercentage = 0.01,
    varsOverride = {},
    partner,
    lowerTick,
    upperTick,
    tokenId,
    recipient,
    gaslessType,
    providers,
  } = request

  const endpoint = "quote_v2"
  const paramsObj: QuoteApiParams = {
    from_chain_id: String(fromChainId),
    from_token: fromToken,
    to_chain_id: String(toChainId),
    to_token: toToken,
    ...varsOverride,
  }

  if (slippagePercentage) {
    paramsObj.slippage_percentage = String(slippagePercentage)
  }
  if (amount) {
    paramsObj.amount = amount
  }
  if (user) {
    paramsObj.user = user
  }
  if (partner) {
    paramsObj.partner = partner
  }
  if (lowerTick) {
    paramsObj.lower_tick = lowerTick
  }
  if (upperTick) {
    paramsObj.upper_tick = upperTick
  }
  if (tokenId) {
    paramsObj.token_id = tokenId
  }
  if (recipient) {
    paramsObj.recipient = recipient
  }
  if (gaslessType) {
    paramsObj.gasless_type = gaslessType
  }

  const params = new URLSearchParams(paramsObj)

  if (providers) {
    for (const provider of providers) {
      params.append("providers", provider)
    }
  }

  const url = `${CONFIG.WIDO_API_URL}/${endpoint}?${params}`

  const res = await fetch(url)

  if (!res.ok) {
    throw WidoError.from_api_response(await res.json())
  }

  const body: QuoteApiResponse = await res.json()
  const { is_supported, steps, steps_count, messages } = body

  const baseQuoteResult: QuoteResult = {
    isSupported: is_supported,
    steps,
    stepsCount: steps_count,
    messages: messages || [],
  }

  if (!amount && !user) {
    return baseQuoteResult
  }

  const {
    price,
    min_price,
    from_token_usd_price,
    from_token_amount,
    from_token_amount_usd_value,
    to_token_usd_price,
    to_token_amount,
    to_token_amount_usd_value,
    expected_slippage,
    min_to_token_amount,
    from,
    to,
    data,
    value,
    gas_fee,
    gas_fee_usd,
    fee_in_from_token,
    bridge_fee_usd_value,
    fee_usd_value,
  } = body

  return {
    ...baseQuoteResult,
    price,
    minPrice: min_price,
    fromTokenUsdPrice: from_token_usd_price,
    fromTokenAmount: from_token_amount,
    fromTokenAmountUsdValue: from_token_amount_usd_value,
    toTokenUsdPrice: to_token_usd_price,
    toTokenAmount: to_token_amount,
    toTokenAmountUsdValue: to_token_amount_usd_value,
    expectedSlippage: expected_slippage,
    minToTokenAmount: min_to_token_amount,
    from,
    to,
    data,
    value,
    gasFee: gas_fee,
    gasFeeUsdValue: gas_fee_usd,
    feeBps: body.fee_bps,
    feeInFromToken: fee_in_from_token,
    bridgeFeeUsdValue: bridge_fee_usd_value,
    feeUsdValue: fee_usd_value,
  } as QuoteResult
}
