package io.synor.dex;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * Perpetual futures sub-client
 */
public class PerpsClient {
    private final SynorDex dex;

    PerpsClient(SynorDex dex) {
        this.dex = dex;
    }

    public CompletableFuture<List<PerpMarket>> listMarkets() {
        return dex.getList("/perps/markets", PerpMarket.class);
    }

    public CompletableFuture<PerpMarket> getMarket(String symbol) {
        return dex.get("/perps/markets/" + symbol, PerpMarket.class);
    }

    public CompletableFuture<PerpPosition> openPosition(OpenPositionParams params) {
        Map<String, Object> body = new HashMap<>();
        body.put("market", params.getMarket());
        body.put("side", params.getSide().name().toLowerCase());
        body.put("size", params.getSize().toString());
        body.put("leverage", params.getLeverage());
        body.put("order_type", params.getOrderType().name().toLowerCase());
        body.put("margin_type", params.getMarginType().name().toLowerCase());
        body.put("reduce_only", params.isReduceOnly());
        if (params.getLimitPrice() != null) {
            body.put("limit_price", params.getLimitPrice());
        }
        if (params.getStopLoss() != null) {
            body.put("stop_loss", params.getStopLoss());
        }
        if (params.getTakeProfit() != null) {
            body.put("take_profit", params.getTakeProfit());
        }
        return dex.post("/perps/positions", body, PerpPosition.class);
    }

    public CompletableFuture<PerpPosition> closePosition(ClosePositionParams params) {
        Map<String, Object> body = new HashMap<>();
        body.put("market", params.getMarket());
        body.put("order_type", params.getOrderType().name().toLowerCase());
        if (params.getSize() != null) {
            body.put("size", params.getSize().toString());
        }
        if (params.getLimitPrice() != null) {
            body.put("limit_price", params.getLimitPrice());
        }
        return dex.post("/perps/positions/close", body, PerpPosition.class);
    }

    public CompletableFuture<PerpPosition> modifyPosition(ModifyPositionParams params) {
        Map<String, Object> body = new HashMap<>();
        if (params.getNewLeverage() != null) {
            body.put("new_leverage", params.getNewLeverage());
        }
        if (params.getNewMargin() != null) {
            body.put("new_margin", params.getNewMargin().toString());
        }
        if (params.getNewStopLoss() != null) {
            body.put("new_stop_loss", params.getNewStopLoss());
        }
        if (params.getNewTakeProfit() != null) {
            body.put("new_take_profit", params.getNewTakeProfit());
        }
        return dex.post("/perps/positions/" + params.getPositionId() + "/modify", body, PerpPosition.class);
    }

    public CompletableFuture<List<PerpPosition>> getPositions() {
        return dex.getList("/perps/positions", PerpPosition.class);
    }

    public CompletableFuture<PerpPosition> getPosition(String market) {
        return dex.get("/perps/positions/" + market, PerpPosition.class);
    }

    public CompletableFuture<List<PerpOrder>> getOrders() {
        return dex.getList("/perps/orders", PerpOrder.class);
    }

    public CompletableFuture<Void> cancelOrder(String orderId) {
        return dex.delete("/perps/orders/" + orderId, Void.class);
    }

    public CompletableFuture<Integer> cancelAllOrders(String market) {
        String path = market != null ? "/perps/orders?market=" + market : "/perps/orders";
        return dex.delete(path, CancelResult.class).thenApply(r -> r.cancelled);
    }

    public CompletableFuture<List<FundingPayment>> getFundingHistory(String market, int limit) {
        return dex.getList("/perps/funding/" + market + "?limit=" + limit, FundingPayment.class);
    }

    public CompletableFuture<FundingRateInfo> getFundingRate(String market) {
        return dex.get("/perps/funding/" + market + "/current", FundingRateInfo.class);
    }

    private static class CancelResult {
        int cancelled;
    }
}
