package io.synor.examples;

import io.synor.dex.*;
import io.synor.dex.types.*;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * Synor DEX SDK Examples for Java
 *
 * Demonstrates decentralized exchange operations:
 * - Spot trading (limit/market orders)
 * - Perpetual futures trading
 * - Liquidity provision (AMM pools)
 * - Order book management
 * - Portfolio tracking
 */
public class DexExample {

    public static void main(String[] args) throws Exception {
        // Initialize client
        DexConfig config = DexConfig.builder()
            .apiKey(System.getenv("SYNOR_API_KEY") != null ?
                    System.getenv("SYNOR_API_KEY") : "your-api-key")
            .endpoint("https://dex.synor.io/v1")
            .timeout(30000)
            .retries(3)
            .debug(false)
            .defaultMarket("SYN-USDC")
            .build();

        SynorDex dex = new SynorDex(config);

        try {
            // Check service health
            boolean healthy = dex.healthCheck().get();
            System.out.println("Service healthy: " + healthy + "\n");

            // Run examples
            marketsExample(dex);
            spotTradingExample(dex);
            perpsTradingExample(dex);
            liquidityExample(dex);
            orderbookExample(dex);
            portfolioExample(dex);
        } finally {
            dex.close();
        }
    }

    static void marketsExample(SynorDex dex) throws Exception {
        System.out.println("=== Markets ===");

        // Get all markets
        List<Market> markets = dex.markets().list().get();
        System.out.println("Available markets: " + markets.size());

        for (Market market : markets.subList(0, Math.min(5, markets.size()))) {
            System.out.println("\n  " + market.getSymbol() + ":");
            System.out.println("    Base: " + market.getBaseAsset() + ", Quote: " + market.getQuoteAsset());
            System.out.println("    Price: " + market.getLastPrice());
            System.out.println("    24h Volume: " + market.getVolume24h());
            System.out.println("    24h Change: " + market.getChange24h() + "%");
            System.out.println("    Status: " + market.getStatus());
        }

        // Get specific market
        Market market = dex.markets().get("SYN-USDC").get();
        System.out.println("\nSYN-USDC details:");
        System.out.println("  Min order size: " + market.getMinOrderSize());
        System.out.println("  Tick size: " + market.getTickSize());
        System.out.println("  Maker fee: " + market.getMakerFee() + "%");
        System.out.println("  Taker fee: " + market.getTakerFee() + "%");

        // Get market statistics
        MarketStats stats = dex.markets().getStats("SYN-USDC").get();
        System.out.println("\nMarket statistics:");
        System.out.println("  High 24h: " + stats.getHigh24h());
        System.out.println("  Low 24h: " + stats.getLow24h());
        System.out.println("  Open interest: " + stats.getOpenInterest());
        System.out.println("  Funding rate: " + stats.getFundingRate() + "%");

        System.out.println();
    }

    static void spotTradingExample(SynorDex dex) throws Exception {
        System.out.println("=== Spot Trading ===");

        // Place a limit order
        System.out.println("Placing limit buy order...");
        OrderResult limitOrder = dex.spot().placeOrder(OrderRequest.builder()
            .market("SYN-USDC")
            .side(OrderSide.BUY)
            .orderType(OrderType.LIMIT)
            .price("1.50")
            .quantity("100")
            .timeInForce(TimeInForce.GTC)
            .build()).get();

        System.out.println("Limit order placed:");
        System.out.println("  Order ID: " + limitOrder.getOrderId());
        System.out.println("  Status: " + limitOrder.getStatus());
        System.out.println("  Price: " + limitOrder.getPrice());
        System.out.println("  Quantity: " + limitOrder.getQuantity());

        // Place a market order
        System.out.println("\nPlacing market sell order...");
        OrderResult marketOrder = dex.spot().placeOrder(OrderRequest.builder()
            .market("SYN-USDC")
            .side(OrderSide.SELL)
            .orderType(OrderType.MARKET)
            .quantity("50")
            .build()).get();

        System.out.println("Market order executed:");
        System.out.println("  Order ID: " + marketOrder.getOrderId());
        System.out.println("  Status: " + marketOrder.getStatus());
        System.out.println("  Filled: " + marketOrder.getFilledQuantity());
        System.out.println("  Avg price: " + marketOrder.getAveragePrice());

        // Get order status
        OrderStatus status = dex.spot().getOrder(limitOrder.getOrderId()).get();
        System.out.println("\nOrder status:");
        System.out.println("  Status: " + status.getStatus());
        System.out.println("  Filled: " + status.getFilledQuantity() + " / " + status.getQuantity());
        System.out.println("  Remaining: " + status.getRemainingQuantity());

        // Cancel order
        System.out.println("\nCancelling order...");
        dex.spot().cancelOrder(limitOrder.getOrderId()).get();
        System.out.println("Order cancelled successfully");

        // Get open orders
        List<Order> openOrders = dex.spot().getOpenOrders("SYN-USDC").get();
        System.out.println("\nOpen orders: " + openOrders.size());

        // Get trade history
        List<Trade> trades = dex.spot().getTradeHistory("SYN-USDC", 10).get();
        System.out.println("\nRecent trades: " + trades.size());
        for (Trade trade : trades.subList(0, Math.min(3, trades.size()))) {
            System.out.println("  " + trade.getSide() + " " + trade.getQuantity() +
                             " @ " + trade.getPrice() + " (" + trade.getTimestamp() + ")");
        }

        System.out.println();
    }

    static void perpsTradingExample(SynorDex dex) throws Exception {
        System.out.println("=== Perpetual Futures Trading ===");

        // Get available perps markets
        List<PerpsMarket> perpsMarkets = dex.perps().listMarkets().get();
        System.out.println("Available perps markets: " + perpsMarkets.size());

        for (PerpsMarket market : perpsMarkets.subList(0, Math.min(3, perpsMarkets.size()))) {
            System.out.println("  " + market.getSymbol() + ": " + market.getMarkPrice() +
                             " (funding: " + market.getFundingRate() + "%)");
        }

        // Open a long position
        System.out.println("\nOpening long position...");
        Position position = dex.perps().openPosition(PerpsOrderRequest.builder()
            .market("SYN-USDC-PERP")
            .side(OrderSide.BUY)
            .orderType(OrderType.LIMIT)
            .price("1.50")
            .size("1000")
            .leverage(10)
            .reduceOnly(false)
            .build()).get();

        System.out.println("Position opened:");
        System.out.println("  Position ID: " + position.getPositionId());
        System.out.println("  Size: " + position.getSize());
        System.out.println("  Entry price: " + position.getEntryPrice());
        System.out.println("  Leverage: " + position.getLeverage() + "x");
        System.out.println("  Liquidation price: " + position.getLiquidationPrice());

        // Get position details
        PositionDetails details = dex.perps().getPosition(position.getPositionId()).get();
        System.out.println("\nPosition details:");
        System.out.println("  Unrealized PnL: " + details.getUnrealizedPnL());
        System.out.println("  Margin: " + details.getMargin());
        System.out.println("  Margin ratio: " + details.getMarginRatio() + "%");

        // Set stop loss and take profit
        System.out.println("\nSetting stop loss and take profit...");
        dex.perps().setStopLoss(position.getPositionId(), "1.40").get();
        System.out.println("Stop loss set at 1.40");

        dex.perps().setTakeProfit(position.getPositionId(), "1.80").get();
        System.out.println("Take profit set at 1.80");

        // Close position
        System.out.println("\nClosing position...");
        CloseResult closeResult = dex.perps().closePosition(position.getPositionId()).get();
        System.out.println("Position closed:");
        System.out.println("  Realized PnL: " + closeResult.getRealizedPnL());
        System.out.println("  Close price: " + closeResult.getClosePrice());

        // Get all positions
        List<Position> positions = dex.perps().getPositions().get();
        System.out.println("\nOpen positions: " + positions.size());

        // Get funding history
        List<FundingPayment> funding = dex.perps().getFundingHistory("SYN-USDC-PERP", 10).get();
        System.out.println("Funding payments: " + funding.size());

        System.out.println();
    }

    static void liquidityExample(SynorDex dex) throws Exception {
        System.out.println("=== Liquidity Provision ===");

        // Get available pools
        List<Pool> pools = dex.liquidity().listPools().get();
        System.out.println("Available pools: " + pools.size());

        for (Pool pool : pools.subList(0, Math.min(3, pools.size()))) {
            System.out.println("\n  " + pool.getName() + ":");
            System.out.println("    TVL: $" + pool.getTvl());
            System.out.println("    APR: " + pool.getApr() + "%");
            System.out.println("    Volume 24h: $" + pool.getVolume24h());
            System.out.println("    Fee tier: " + pool.getFeeTier() + "%");
        }

        // Get pool details
        PoolDetails pool = dex.liquidity().getPool("SYN-USDC").get();
        System.out.println("\nSYN-USDC pool details:");
        System.out.println("  Token0: " + pool.getToken0().getSymbol() + " (" + pool.getToken0Reserve() + ")");
        System.out.println("  Token1: " + pool.getToken1().getSymbol() + " (" + pool.getToken1Reserve() + ")");
        System.out.println("  Price: " + pool.getPrice());
        System.out.println("  Total LP tokens: " + pool.getTotalLPTokens());

        // Add liquidity
        System.out.println("\nAdding liquidity...");
        AddLiquidityResult addResult = dex.liquidity().addLiquidity(AddLiquidityRequest.builder()
            .pool("SYN-USDC")
            .amount0("100")
            .amount1("150")
            .slippageBps(50) // 0.5%
            .build()).get();

        System.out.println("Liquidity added:");
        System.out.println("  LP tokens received: " + addResult.getLpTokens());
        System.out.println("  Position ID: " + addResult.getPositionId());
        System.out.println("  Share of pool: " + addResult.getShareOfPool() + "%");

        // Get LP positions
        List<LPPosition> lpPositions = dex.liquidity().getPositions().get();
        System.out.println("\nLP positions: " + lpPositions.size());
        for (LPPosition pos : lpPositions) {
            System.out.println("  " + pos.getPool() + ": " + pos.getLpTokens() +
                             " LP tokens (value: $" + pos.getValue() + ")");
        }

        // Claim fees
        System.out.println("\nClaiming fees...");
        ClaimFeesResult fees = dex.liquidity().claimFees(addResult.getPositionId()).get();
        System.out.println("Fees claimed:");
        System.out.println("  Token0: " + fees.getAmount0());
        System.out.println("  Token1: " + fees.getAmount1());

        // Remove liquidity
        System.out.println("\nRemoving liquidity...");
        RemoveLiquidityResult removeResult = dex.liquidity().removeLiquidity(RemoveLiquidityRequest.builder()
            .positionId(addResult.getPositionId())
            .lpTokens(addResult.getLpTokens())
            .slippageBps(50)
            .build()).get();

        System.out.println("Liquidity removed:");
        System.out.println("  Token0 received: " + removeResult.getAmount0());
        System.out.println("  Token1 received: " + removeResult.getAmount1());

        System.out.println();
    }

    static void orderbookExample(SynorDex dex) throws Exception {
        System.out.println("=== Order Book ===");

        // Get order book snapshot
        Orderbook orderbook = dex.orderbook().getSnapshot("SYN-USDC", 10).get();

        System.out.println("Order book for SYN-USDC:");
        System.out.println("\nAsks (sells):");
        for (OrderbookEntry ask : orderbook.getAsks().subList(0, Math.min(5, orderbook.getAsks().size()))) {
            System.out.println("  " + ask.getQuantity() + " @ " + ask.getPrice());
        }

        System.out.println("\nBids (buys):");
        for (OrderbookEntry bid : orderbook.getBids().subList(0, Math.min(5, orderbook.getBids().size()))) {
            System.out.println("  " + bid.getQuantity() + " @ " + bid.getPrice());
        }

        System.out.println("\nSpread: " + orderbook.getSpread() + " (" + orderbook.getSpreadPercent() + "%)");
        System.out.println("Mid price: " + orderbook.getMidPrice());

        // Get recent trades
        List<Trade> recentTrades = dex.orderbook().getRecentTrades("SYN-USDC", 10).get();
        System.out.println("\nRecent trades:");
        for (Trade trade : recentTrades.subList(0, Math.min(5, recentTrades.size()))) {
            System.out.println("  " + trade.getSide() + " " + trade.getQuantity() + " @ " + trade.getPrice());
        }

        System.out.println();
    }

    static void portfolioExample(SynorDex dex) throws Exception {
        System.out.println("=== Portfolio ===");

        // Get portfolio overview
        Portfolio portfolio = dex.portfolio().getOverview().get();

        System.out.println("Portfolio overview:");
        System.out.println("  Total value: $" + portfolio.getTotalValue());
        System.out.println("  Available balance: $" + portfolio.getAvailableBalance());
        System.out.println("  In orders: $" + portfolio.getInOrders());
        System.out.println("  In positions: $" + portfolio.getInPositions());
        System.out.println("  Unrealized PnL: $" + portfolio.getUnrealizedPnL());

        // Get balances
        List<Balance> balances = dex.portfolio().getBalances().get();
        System.out.println("\nBalances:");
        for (Balance balance : balances) {
            System.out.println("  " + balance.getAsset() + ": " + balance.getTotal() +
                             " (available: " + balance.getAvailable() + ", in orders: " + balance.getInOrders() + ")");
        }

        // Get PnL history
        List<PnLEntry> pnlHistory = dex.portfolio().getPnLHistory(30).get(); // Last 30 days
        System.out.println("\nPnL history (last " + pnlHistory.size() + " days):");
        if (!pnlHistory.isEmpty()) {
            System.out.println("  Total PnL: $" + pnlHistory.get(pnlHistory.size() - 1).getCumulativePnL());
        }

        // Get trade statistics
        TradeStats stats = dex.portfolio().getStats().get();
        System.out.println("\nTrade statistics:");
        System.out.println("  Total trades: " + stats.getTotalTrades());
        System.out.println("  Win rate: " + stats.getWinRate() + "%");
        System.out.println("  Avg profit: $" + stats.getAvgProfit());
        System.out.println("  Avg loss: $" + stats.getAvgLoss());
        System.out.println("  Best trade: $" + stats.getBestTrade());
        System.out.println("  Worst trade: $" + stats.getWorstTrade());

        System.out.println();
    }
}
