package io.synor.dex

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.math.BigInteger

enum class PoolType {
    @SerialName("constant_product") CONSTANT_PRODUCT,
    @SerialName("stable") STABLE,
    @SerialName("concentrated") CONCENTRATED
}

enum class PositionSide {
    @SerialName("long") LONG,
    @SerialName("short") SHORT
}

enum class OrderType {
    @SerialName("market") MARKET,
    @SerialName("limit") LIMIT,
    @SerialName("stop_market") STOP_MARKET,
    @SerialName("stop_limit") STOP_LIMIT,
    @SerialName("take_profit") TAKE_PROFIT,
    @SerialName("take_profit_limit") TAKE_PROFIT_LIMIT
}

enum class MarginType {
    @SerialName("cross") CROSS,
    @SerialName("isolated") ISOLATED
}

enum class TimeInForce {
    GTC, IOC, FOK, GTD
}

enum class OrderStatus {
    @SerialName("pending") PENDING,
    @SerialName("open") OPEN,
    @SerialName("partially_filled") PARTIALLY_FILLED,
    @SerialName("filled") FILLED,
    @SerialName("cancelled") CANCELLED,
    @SerialName("expired") EXPIRED
}

@Serializable
data class Token(
    val address: String,
    val symbol: String,
    val name: String,
    val decimals: Int,
    @SerialName("total_supply") val totalSupply: String,
    @SerialName("price_usd") val priceUsd: Double? = null,
    @SerialName("logo_url") val logoUrl: String? = null,
    val verified: Boolean = false
)

@Serializable
data class Pool(
    val id: String,
    @SerialName("token_a") val tokenA: Token,
    @SerialName("token_b") val tokenB: Token,
    @SerialName("pool_type") val poolType: PoolType,
    @SerialName("reserve_a") val reserveA: String,
    @SerialName("reserve_b") val reserveB: String,
    val fee: Double,
    @SerialName("tvl_usd") val tvlUsd: Double,
    @SerialName("volume_24h") val volume24h: Double,
    val apr: Double,
    @SerialName("lp_token_address") val lpTokenAddress: String,
    @SerialName("tick_spacing") val tickSpacing: Int? = null,
    @SerialName("sqrt_price") val sqrtPrice: String? = null
)

data class PoolFilter(
    val tokens: List<String>? = null,
    val minTvl: Double? = null,
    val minVolume24h: Double? = null,
    val verified: Boolean? = null,
    val limit: Int? = null,
    val offset: Int? = null
)

@Serializable
data class Quote(
    @SerialName("token_in") val tokenIn: String,
    @SerialName("token_out") val tokenOut: String,
    @SerialName("amount_in") val amountIn: String,
    @SerialName("amount_out") val amountOut: String,
    @SerialName("price_impact") val priceImpact: Double,
    val route: List<String>,
    val fee: String,
    @SerialName("minimum_received") val minimumReceived: String,
    @SerialName("expires_at") val expiresAt: Long
)

data class QuoteParams(
    val tokenIn: String,
    val tokenOut: String,
    val amountIn: BigInteger,
    val slippage: Double? = null
)

data class SwapParams(
    val tokenIn: String,
    val tokenOut: String,
    val amountIn: BigInteger,
    val minAmountOut: BigInteger,
    val deadline: Long? = null,
    val recipient: String? = null
)

@Serializable
data class SwapResult(
    @SerialName("transaction_hash") val transactionHash: String,
    @SerialName("amount_in") val amountIn: String,
    @SerialName("amount_out") val amountOut: String,
    @SerialName("effective_price") val effectivePrice: Double,
    @SerialName("fee_paid") val feePaid: String,
    val route: List<String>
)

data class AddLiquidityParams(
    val tokenA: String,
    val tokenB: String,
    val amountA: BigInteger,
    val amountB: BigInteger,
    val minAmountA: BigInteger? = null,
    val minAmountB: BigInteger? = null,
    val deadline: Long? = null
)

data class RemoveLiquidityParams(
    val pool: String,
    val lpAmount: BigInteger,
    val minAmountA: BigInteger? = null,
    val minAmountB: BigInteger? = null,
    val deadline: Long? = null
)

@Serializable
data class LiquidityResult(
    @SerialName("transaction_hash") val transactionHash: String,
    @SerialName("amount_a") val amountA: String,
    @SerialName("amount_b") val amountB: String,
    @SerialName("lp_tokens") val lpTokens: String,
    @SerialName("pool_share") val poolShare: Double
)

@Serializable
data class LPPosition(
    @SerialName("pool_id") val poolId: String,
    @SerialName("lp_tokens") val lpTokens: String,
    @SerialName("token_a_amount") val tokenAAmount: String,
    @SerialName("token_b_amount") val tokenBAmount: String,
    @SerialName("value_usd") val valueUsd: Double,
    @SerialName("unclaimed_fees_a") val unclaimedFeesA: String,
    @SerialName("unclaimed_fees_b") val unclaimedFeesB: String,
    @SerialName("impermanent_loss") val impermanentLoss: Double,
    @SerialName("tick_lower") val tickLower: Int? = null,
    @SerialName("tick_upper") val tickUpper: Int? = null,
    @SerialName("in_range") val inRange: Boolean? = null
)

@Serializable
data class PerpMarket(
    val symbol: String,
    @SerialName("base_asset") val baseAsset: String,
    @SerialName("quote_asset") val quoteAsset: String,
    @SerialName("index_price") val indexPrice: Double,
    @SerialName("mark_price") val markPrice: Double,
    @SerialName("funding_rate") val fundingRate: Double,
    @SerialName("next_funding_time") val nextFundingTime: Long,
    @SerialName("open_interest") val openInterest: String,
    @SerialName("volume_24h") val volume24h: Double,
    @SerialName("price_change_24h") val priceChange24h: Double,
    @SerialName("max_leverage") val maxLeverage: Int,
    @SerialName("min_order_size") val minOrderSize: String,
    @SerialName("tick_size") val tickSize: Double,
    @SerialName("maintenance_margin") val maintenanceMargin: Double,
    @SerialName("initial_margin") val initialMargin: Double
)

data class OpenPositionParams(
    val market: String,
    val side: PositionSide,
    val size: BigInteger,
    val leverage: Int,
    val orderType: OrderType,
    val limitPrice: Double? = null,
    val stopLoss: Double? = null,
    val takeProfit: Double? = null,
    val marginType: MarginType = MarginType.CROSS,
    val reduceOnly: Boolean = false
)

data class ClosePositionParams(
    val market: String,
    val size: BigInteger? = null,
    val orderType: OrderType = OrderType.MARKET,
    val limitPrice: Double? = null
)

data class ModifyPositionParams(
    val positionId: String,
    val newLeverage: Int? = null,
    val newMargin: BigInteger? = null,
    val newStopLoss: Double? = null,
    val newTakeProfit: Double? = null
)

@Serializable
data class PerpPosition(
    val id: String,
    val market: String,
    val side: PositionSide,
    val size: String,
    @SerialName("entry_price") val entryPrice: Double,
    @SerialName("mark_price") val markPrice: Double,
    @SerialName("liquidation_price") val liquidationPrice: Double,
    val margin: String,
    val leverage: Int,
    @SerialName("unrealized_pnl") val unrealizedPnl: String,
    @SerialName("realized_pnl") val realizedPnl: String,
    @SerialName("margin_ratio") val marginRatio: Double,
    @SerialName("stop_loss") val stopLoss: Double? = null,
    @SerialName("take_profit") val takeProfit: Double? = null,
    @SerialName("created_at") val createdAt: Long = 0,
    @SerialName("updated_at") val updatedAt: Long = 0
)

@Serializable
data class PerpOrder(
    val id: String,
    val market: String,
    val side: PositionSide,
    @SerialName("order_type") val orderType: OrderType,
    val size: String,
    val price: Double? = null,
    @SerialName("filled_size") val filledSize: String = "0",
    val status: OrderStatus,
    @SerialName("reduce_only") val reduceOnly: Boolean = false,
    @SerialName("created_at") val createdAt: Long = 0
)

@Serializable
data class FundingPayment(
    val market: String,
    val amount: String,
    val rate: Double,
    @SerialName("position_size") val positionSize: String,
    val timestamp: Long
)

@Serializable
data class FundingRateInfo(
    val rate: Double,
    @SerialName("next_time") val nextTime: Long
)

@Serializable
data class OrderBookEntry(
    val price: Double,
    val size: String,
    val orders: Int
)

@Serializable
data class OrderBook(
    val market: String,
    val bids: List<OrderBookEntry>,
    val asks: List<OrderBookEntry>,
    val timestamp: Long
)

data class LimitOrderParams(
    val market: String,
    val side: String,
    val price: Double,
    val size: BigInteger,
    val timeInForce: TimeInForce = TimeInForce.GTC,
    val postOnly: Boolean = false
)

@Serializable
data class Order(
    val id: String,
    val market: String,
    val side: String,
    val price: Double,
    val size: String,
    @SerialName("filled_size") val filledSize: String,
    val status: OrderStatus,
    @SerialName("time_in_force") val timeInForce: TimeInForce,
    @SerialName("post_only") val postOnly: Boolean,
    @SerialName("created_at") val createdAt: Long,
    @SerialName("updated_at") val updatedAt: Long
)

@Serializable
data class Farm(
    val id: String,
    val name: String,
    @SerialName("stake_token") val stakeToken: Token,
    @SerialName("reward_tokens") val rewardTokens: List<Token>,
    @SerialName("tvl_usd") val tvlUsd: Double,
    val apr: Double,
    @SerialName("daily_rewards") val dailyRewards: List<String>,
    @SerialName("lockup_period") val lockupPeriod: Long? = null,
    @SerialName("min_stake") val minStake: String? = null
)

data class StakeParams(
    val farm: String,
    val amount: BigInteger
)

@Serializable
data class FarmPosition(
    @SerialName("farm_id") val farmId: String,
    @SerialName("staked_amount") val stakedAmount: String,
    @SerialName("pending_rewards") val pendingRewards: List<String>,
    @SerialName("staked_at") val stakedAt: Long,
    @SerialName("unlock_at") val unlockAt: Long? = null
)

@Serializable
data class OHLCV(
    val timestamp: Long,
    val open: Double,
    val high: Double,
    val low: Double,
    val close: Double,
    val volume: Double
)

@Serializable
data class TradeHistory(
    val id: String,
    val market: String,
    val side: String,
    val price: Double,
    val size: String,
    val timestamp: Long,
    val maker: String,
    val taker: String
)

@Serializable
data class VolumeStats(
    @SerialName("volume_24h") val volume24h: Double,
    @SerialName("volume_7d") val volume7d: Double,
    @SerialName("volume_30d") val volume30d: Double,
    @SerialName("trades_24h") val trades24h: Int,
    @SerialName("unique_traders_24h") val uniqueTraders24h: Int
)

@Serializable
data class TVLStats(
    @SerialName("total_tvl") val totalTvl: Double,
    @SerialName("pools_tvl") val poolsTvl: Double,
    @SerialName("farms_tvl") val farmsTvl: Double,
    @SerialName("perps_tvl") val perpsTvl: Double
)

@Serializable
data class ClaimRewardsResult(
    val amount: String,
    @SerialName("transaction_hash") val transactionHash: String
)
