/// Synor DEX SDK Types for Flutter/Dart
library;

/// Pool type
enum PoolType { constantProduct, stable, concentrated }

/// Position side for perpetuals
enum PositionSide { long, short }

/// Order type
enum OrderType { market, limit, stopMarket, stopLimit, takeProfit, takeProfitLimit }

/// Margin type
enum MarginType { cross, isolated }

/// Time in force
enum TimeInForce { GTC, IOC, FOK, GTD }

/// Order status
enum OrderStatus { pending, open, partiallyFilled, filled, cancelled, expired }

/// DEX exception
class DexException implements Exception {
  final String message;
  final String? code;
  final int status;

  DexException(this.message, this.code, this.status);

  @override
  String toString() => 'DexException: $message (code: $code, status: $status)';
}

/// DEX configuration
class DexConfig {
  final String apiKey;
  final String endpoint;
  final String wsEndpoint;
  final int timeout;
  final int retries;
  final bool debug;

  DexConfig({
    required this.apiKey,
    this.endpoint = 'https://dex.synor.io/v1',
    this.wsEndpoint = 'wss://dex.synor.io/v1/ws',
    this.timeout = 30000,
    this.retries = 3,
    this.debug = false,
  });
}

/// Token
class Token {
  final String address;
  final String symbol;
  final String name;
  final int decimals;
  final BigInt totalSupply;
  final double? priceUsd;
  final String? logoUrl;
  final bool verified;

  Token({
    required this.address,
    required this.symbol,
    required this.name,
    required this.decimals,
    required this.totalSupply,
    this.priceUsd,
    this.logoUrl,
    this.verified = false,
  });

  factory Token.fromJson(Map<String, dynamic> json) => Token(
    address: json['address'] as String,
    symbol: json['symbol'] as String,
    name: json['name'] as String,
    decimals: json['decimals'] as int,
    totalSupply: BigInt.parse(json['total_supply'] as String),
    priceUsd: json['price_usd'] as double?,
    logoUrl: json['logo_url'] as String?,
    verified: json['verified'] as bool? ?? false,
  );
}

/// Pool
class Pool {
  final String id;
  final Token tokenA;
  final Token tokenB;
  final PoolType poolType;
  final BigInt reserveA;
  final BigInt reserveB;
  final double fee;
  final double tvlUsd;
  final double volume24h;
  final double apr;
  final String lpTokenAddress;

  Pool({
    required this.id,
    required this.tokenA,
    required this.tokenB,
    required this.poolType,
    required this.reserveA,
    required this.reserveB,
    required this.fee,
    required this.tvlUsd,
    required this.volume24h,
    required this.apr,
    required this.lpTokenAddress,
  });

  factory Pool.fromJson(Map<String, dynamic> json) => Pool(
    id: json['id'] as String,
    tokenA: Token.fromJson(json['token_a'] as Map<String, dynamic>),
    tokenB: Token.fromJson(json['token_b'] as Map<String, dynamic>),
    poolType: PoolType.values.firstWhere((e) => e.name == json['pool_type']),
    reserveA: BigInt.parse(json['reserve_a'] as String),
    reserveB: BigInt.parse(json['reserve_b'] as String),
    fee: json['fee'] as double,
    tvlUsd: json['tvl_usd'] as double,
    volume24h: json['volume_24h'] as double,
    apr: json['apr'] as double,
    lpTokenAddress: json['lp_token_address'] as String,
  );
}

/// Pool filter
class PoolFilter {
  final List<String>? tokens;
  final double? minTvl;
  final double? minVolume24h;
  final bool? verified;
  final int? limit;
  final int? offset;

  PoolFilter({this.tokens, this.minTvl, this.minVolume24h, this.verified, this.limit, this.offset});
}

/// Quote
class Quote {
  final String tokenIn;
  final String tokenOut;
  final BigInt amountIn;
  final BigInt amountOut;
  final double priceImpact;
  final List<String> route;
  final BigInt fee;
  final BigInt minimumReceived;
  final int expiresAt;

  Quote({
    required this.tokenIn,
    required this.tokenOut,
    required this.amountIn,
    required this.amountOut,
    required this.priceImpact,
    required this.route,
    required this.fee,
    required this.minimumReceived,
    required this.expiresAt,
  });

  factory Quote.fromJson(Map<String, dynamic> json) => Quote(
    tokenIn: json['token_in'] as String,
    tokenOut: json['token_out'] as String,
    amountIn: BigInt.parse(json['amount_in'] as String),
    amountOut: BigInt.parse(json['amount_out'] as String),
    priceImpact: json['price_impact'] as double,
    route: (json['route'] as List).cast<String>(),
    fee: BigInt.parse(json['fee'] as String),
    minimumReceived: BigInt.parse(json['minimum_received'] as String),
    expiresAt: json['expires_at'] as int,
  );
}

/// Quote params
class QuoteParams {
  final String tokenIn;
  final String tokenOut;
  final BigInt amountIn;
  final double? slippage;

  QuoteParams({required this.tokenIn, required this.tokenOut, required this.amountIn, this.slippage});
}

/// Swap params
class SwapParams {
  final String tokenIn;
  final String tokenOut;
  final BigInt amountIn;
  final BigInt minAmountOut;
  final int? deadline;
  final String? recipient;

  SwapParams({
    required this.tokenIn,
    required this.tokenOut,
    required this.amountIn,
    required this.minAmountOut,
    this.deadline,
    this.recipient,
  });
}

/// Swap result
class SwapResult {
  final String transactionHash;
  final BigInt amountIn;
  final BigInt amountOut;
  final double effectivePrice;
  final BigInt feePaid;
  final List<String> route;

  SwapResult({
    required this.transactionHash,
    required this.amountIn,
    required this.amountOut,
    required this.effectivePrice,
    required this.feePaid,
    required this.route,
  });

  factory SwapResult.fromJson(Map<String, dynamic> json) => SwapResult(
    transactionHash: json['transaction_hash'] as String,
    amountIn: BigInt.parse(json['amount_in'] as String),
    amountOut: BigInt.parse(json['amount_out'] as String),
    effectivePrice: json['effective_price'] as double,
    feePaid: BigInt.parse(json['fee_paid'] as String),
    route: (json['route'] as List).cast<String>(),
  );
}

/// Add liquidity params
class AddLiquidityParams {
  final String tokenA;
  final String tokenB;
  final BigInt amountA;
  final BigInt amountB;
  final BigInt? minAmountA;
  final BigInt? minAmountB;
  final int? deadline;

  AddLiquidityParams({
    required this.tokenA,
    required this.tokenB,
    required this.amountA,
    required this.amountB,
    this.minAmountA,
    this.minAmountB,
    this.deadline,
  });
}

/// Remove liquidity params
class RemoveLiquidityParams {
  final String pool;
  final BigInt lpAmount;
  final BigInt? minAmountA;
  final BigInt? minAmountB;
  final int? deadline;

  RemoveLiquidityParams({
    required this.pool,
    required this.lpAmount,
    this.minAmountA,
    this.minAmountB,
    this.deadline,
  });
}

/// Liquidity result
class LiquidityResult {
  final String transactionHash;
  final BigInt amountA;
  final BigInt amountB;
  final BigInt lpTokens;
  final double poolShare;

  LiquidityResult({
    required this.transactionHash,
    required this.amountA,
    required this.amountB,
    required this.lpTokens,
    required this.poolShare,
  });

  factory LiquidityResult.fromJson(Map<String, dynamic> json) => LiquidityResult(
    transactionHash: json['transaction_hash'] as String,
    amountA: BigInt.parse(json['amount_a'] as String),
    amountB: BigInt.parse(json['amount_b'] as String),
    lpTokens: BigInt.parse(json['lp_tokens'] as String),
    poolShare: json['pool_share'] as double,
  );
}

/// LP position
class LPPosition {
  final String poolId;
  final BigInt lpTokens;
  final BigInt tokenAAmount;
  final BigInt tokenBAmount;
  final double valueUsd;
  final BigInt unclaimedFeesA;
  final BigInt unclaimedFeesB;
  final double impermanentLoss;

  LPPosition({
    required this.poolId,
    required this.lpTokens,
    required this.tokenAAmount,
    required this.tokenBAmount,
    required this.valueUsd,
    required this.unclaimedFeesA,
    required this.unclaimedFeesB,
    required this.impermanentLoss,
  });

  factory LPPosition.fromJson(Map<String, dynamic> json) => LPPosition(
    poolId: json['pool_id'] as String,
    lpTokens: BigInt.parse(json['lp_tokens'] as String),
    tokenAAmount: BigInt.parse(json['token_a_amount'] as String),
    tokenBAmount: BigInt.parse(json['token_b_amount'] as String),
    valueUsd: json['value_usd'] as double,
    unclaimedFeesA: BigInt.parse(json['unclaimed_fees_a'] as String),
    unclaimedFeesB: BigInt.parse(json['unclaimed_fees_b'] as String),
    impermanentLoss: json['impermanent_loss'] as double,
  );
}

/// Perp market
class PerpMarket {
  final String symbol;
  final String baseAsset;
  final String quoteAsset;
  final double indexPrice;
  final double markPrice;
  final double fundingRate;
  final int nextFundingTime;
  final BigInt openInterest;
  final double volume24h;
  final int maxLeverage;

  PerpMarket({
    required this.symbol,
    required this.baseAsset,
    required this.quoteAsset,
    required this.indexPrice,
    required this.markPrice,
    required this.fundingRate,
    required this.nextFundingTime,
    required this.openInterest,
    required this.volume24h,
    required this.maxLeverage,
  });

  factory PerpMarket.fromJson(Map<String, dynamic> json) => PerpMarket(
    symbol: json['symbol'] as String,
    baseAsset: json['base_asset'] as String,
    quoteAsset: json['quote_asset'] as String,
    indexPrice: json['index_price'] as double,
    markPrice: json['mark_price'] as double,
    fundingRate: json['funding_rate'] as double,
    nextFundingTime: json['next_funding_time'] as int,
    openInterest: BigInt.parse(json['open_interest'] as String),
    volume24h: json['volume_24h'] as double,
    maxLeverage: json['max_leverage'] as int,
  );
}

/// Open position params
class OpenPositionParams {
  final String market;
  final PositionSide side;
  final BigInt size;
  final int leverage;
  final OrderType orderType;
  final double? limitPrice;
  final double? stopLoss;
  final double? takeProfit;
  final MarginType marginType;
  final bool reduceOnly;

  OpenPositionParams({
    required this.market,
    required this.side,
    required this.size,
    required this.leverage,
    required this.orderType,
    this.limitPrice,
    this.stopLoss,
    this.takeProfit,
    this.marginType = MarginType.cross,
    this.reduceOnly = false,
  });
}

/// Close position params
class ClosePositionParams {
  final String market;
  final BigInt? size;
  final OrderType orderType;
  final double? limitPrice;

  ClosePositionParams({
    required this.market,
    this.size,
    this.orderType = OrderType.market,
    this.limitPrice,
  });
}

/// Perp position
class PerpPosition {
  final String id;
  final String market;
  final PositionSide side;
  final BigInt size;
  final double entryPrice;
  final double markPrice;
  final double liquidationPrice;
  final BigInt margin;
  final int leverage;
  final BigInt unrealizedPnl;

  PerpPosition({
    required this.id,
    required this.market,
    required this.side,
    required this.size,
    required this.entryPrice,
    required this.markPrice,
    required this.liquidationPrice,
    required this.margin,
    required this.leverage,
    required this.unrealizedPnl,
  });

  factory PerpPosition.fromJson(Map<String, dynamic> json) => PerpPosition(
    id: json['id'] as String,
    market: json['market'] as String,
    side: PositionSide.values.firstWhere((e) => e.name == json['side']),
    size: BigInt.parse(json['size'] as String),
    entryPrice: json['entry_price'] as double,
    markPrice: json['mark_price'] as double,
    liquidationPrice: json['liquidation_price'] as double,
    margin: BigInt.parse(json['margin'] as String),
    leverage: json['leverage'] as int,
    unrealizedPnl: BigInt.parse(json['unrealized_pnl'] as String),
  );
}

/// Perp order
class PerpOrder {
  final String id;
  final String market;
  final PositionSide side;
  final OrderType orderType;
  final BigInt size;
  final double? price;
  final OrderStatus status;

  PerpOrder({
    required this.id,
    required this.market,
    required this.side,
    required this.orderType,
    required this.size,
    this.price,
    required this.status,
  });

  factory PerpOrder.fromJson(Map<String, dynamic> json) => PerpOrder(
    id: json['id'] as String,
    market: json['market'] as String,
    side: PositionSide.values.firstWhere((e) => e.name == json['side']),
    orderType: OrderType.values.firstWhere((e) => e.name == json['order_type']),
    size: BigInt.parse(json['size'] as String),
    price: json['price'] as double?,
    status: OrderStatus.values.firstWhere((e) => e.name == json['status']),
  );
}

/// Funding payment
class FundingPayment {
  final String market;
  final BigInt amount;
  final double rate;
  final BigInt positionSize;
  final int timestamp;

  FundingPayment({
    required this.market,
    required this.amount,
    required this.rate,
    required this.positionSize,
    required this.timestamp,
  });

  factory FundingPayment.fromJson(Map<String, dynamic> json) => FundingPayment(
    market: json['market'] as String,
    amount: BigInt.parse(json['amount'] as String),
    rate: json['rate'] as double,
    positionSize: BigInt.parse(json['position_size'] as String),
    timestamp: json['timestamp'] as int,
  );
}

/// Funding rate info
class FundingRateInfo {
  final double rate;
  final int nextTime;

  FundingRateInfo({required this.rate, required this.nextTime});

  factory FundingRateInfo.fromJson(Map<String, dynamic> json) => FundingRateInfo(
    rate: json['rate'] as double,
    nextTime: json['next_time'] as int,
  );
}

/// Order book entry
class OrderBookEntry {
  final double price;
  final BigInt size;
  final int orders;

  OrderBookEntry({required this.price, required this.size, required this.orders});

  factory OrderBookEntry.fromJson(Map<String, dynamic> json) => OrderBookEntry(
    price: json['price'] as double,
    size: BigInt.parse(json['size'] as String),
    orders: json['orders'] as int,
  );
}

/// Order book
class OrderBook {
  final String market;
  final List<OrderBookEntry> bids;
  final List<OrderBookEntry> asks;
  final int timestamp;

  OrderBook({required this.market, required this.bids, required this.asks, required this.timestamp});

  factory OrderBook.fromJson(Map<String, dynamic> json) => OrderBook(
    market: json['market'] as String,
    bids: (json['bids'] as List).map((e) => OrderBookEntry.fromJson(e as Map<String, dynamic>)).toList(),
    asks: (json['asks'] as List).map((e) => OrderBookEntry.fromJson(e as Map<String, dynamic>)).toList(),
    timestamp: json['timestamp'] as int,
  );
}

/// Limit order params
class LimitOrderParams {
  final String market;
  final String side;
  final double price;
  final BigInt size;
  final TimeInForce timeInForce;
  final bool postOnly;

  LimitOrderParams({
    required this.market,
    required this.side,
    required this.price,
    required this.size,
    this.timeInForce = TimeInForce.GTC,
    this.postOnly = false,
  });
}

/// Order
class Order {
  final String id;
  final String market;
  final String side;
  final double price;
  final BigInt size;
  final BigInt filledSize;
  final OrderStatus status;
  final TimeInForce timeInForce;
  final bool postOnly;

  Order({
    required this.id,
    required this.market,
    required this.side,
    required this.price,
    required this.size,
    required this.filledSize,
    required this.status,
    required this.timeInForce,
    required this.postOnly,
  });

  factory Order.fromJson(Map<String, dynamic> json) => Order(
    id: json['id'] as String,
    market: json['market'] as String,
    side: json['side'] as String,
    price: json['price'] as double,
    size: BigInt.parse(json['size'] as String),
    filledSize: BigInt.parse(json['filled_size'] as String),
    status: OrderStatus.values.firstWhere((e) => e.name == json['status']),
    timeInForce: TimeInForce.values.firstWhere((e) => e.name == json['time_in_force']),
    postOnly: json['post_only'] as bool,
  );
}

/// Farm
class Farm {
  final String id;
  final String name;
  final Token stakeToken;
  final List<Token> rewardTokens;
  final double tvlUsd;
  final double apr;

  Farm({
    required this.id,
    required this.name,
    required this.stakeToken,
    required this.rewardTokens,
    required this.tvlUsd,
    required this.apr,
  });

  factory Farm.fromJson(Map<String, dynamic> json) => Farm(
    id: json['id'] as String,
    name: json['name'] as String,
    stakeToken: Token.fromJson(json['stake_token'] as Map<String, dynamic>),
    rewardTokens: (json['reward_tokens'] as List).map((e) => Token.fromJson(e as Map<String, dynamic>)).toList(),
    tvlUsd: json['tvl_usd'] as double,
    apr: json['apr'] as double,
  );
}

/// Stake params
class StakeParams {
  final String farm;
  final BigInt amount;

  StakeParams({required this.farm, required this.amount});
}

/// Farm position
class FarmPosition {
  final String farmId;
  final BigInt stakedAmount;
  final List<BigInt> pendingRewards;
  final int stakedAt;

  FarmPosition({
    required this.farmId,
    required this.stakedAmount,
    required this.pendingRewards,
    required this.stakedAt,
  });

  factory FarmPosition.fromJson(Map<String, dynamic> json) => FarmPosition(
    farmId: json['farm_id'] as String,
    stakedAmount: BigInt.parse(json['staked_amount'] as String),
    pendingRewards: (json['pending_rewards'] as List).map((e) => BigInt.parse(e as String)).toList(),
    stakedAt: json['staked_at'] as int,
  );
}

/// Claim rewards result
class ClaimRewardsResult {
  final BigInt amount;
  final String transactionHash;

  ClaimRewardsResult({required this.amount, required this.transactionHash});

  factory ClaimRewardsResult.fromJson(Map<String, dynamic> json) => ClaimRewardsResult(
    amount: BigInt.parse(json['amount'] as String),
    transactionHash: json['transaction_hash'] as String,
  );
}

/// OHLCV
class OHLCV {
  final int timestamp;
  final double open;
  final double high;
  final double low;
  final double close;
  final double volume;

  OHLCV({
    required this.timestamp,
    required this.open,
    required this.high,
    required this.low,
    required this.close,
    required this.volume,
  });

  factory OHLCV.fromJson(Map<String, dynamic> json) => OHLCV(
    timestamp: json['timestamp'] as int,
    open: json['open'] as double,
    high: json['high'] as double,
    low: json['low'] as double,
    close: json['close'] as double,
    volume: json['volume'] as double,
  );
}

/// Trade history
class TradeHistory {
  final String id;
  final String market;
  final String side;
  final double price;
  final BigInt size;
  final int timestamp;
  final String maker;
  final String taker;

  TradeHistory({
    required this.id,
    required this.market,
    required this.side,
    required this.price,
    required this.size,
    required this.timestamp,
    required this.maker,
    required this.taker,
  });

  factory TradeHistory.fromJson(Map<String, dynamic> json) => TradeHistory(
    id: json['id'] as String,
    market: json['market'] as String,
    side: json['side'] as String,
    price: json['price'] as double,
    size: BigInt.parse(json['size'] as String),
    timestamp: json['timestamp'] as int,
    maker: json['maker'] as String,
    taker: json['taker'] as String,
  );
}

/// Volume stats
class VolumeStats {
  final double volume24h;
  final double volume7d;
  final double volume30d;
  final int trades24h;

  VolumeStats({required this.volume24h, required this.volume7d, required this.volume30d, required this.trades24h});

  factory VolumeStats.fromJson(Map<String, dynamic> json) => VolumeStats(
    volume24h: json['volume_24h'] as double,
    volume7d: json['volume_7d'] as double,
    volume30d: json['volume_30d'] as double,
    trades24h: json['trades_24h'] as int,
  );
}

/// TVL stats
class TVLStats {
  final double totalTvl;
  final double poolsTvl;
  final double farmsTvl;
  final double perpsTvl;

  TVLStats({required this.totalTvl, required this.poolsTvl, required this.farmsTvl, required this.perpsTvl});

  factory TVLStats.fromJson(Map<String, dynamic> json) => TVLStats(
    totalTvl: json['total_tvl'] as double,
    poolsTvl: json['pools_tvl'] as double,
    farmsTvl: json['farms_tvl'] as double,
    perpsTvl: json['perps_tvl'] as double,
  );
}
