/**
 * @file dex.hpp
 * @brief Synor DEX SDK for C++
 *
 * Complete decentralized exchange client with support for:
 * - AMM swaps (constant product, stable, concentrated)
 * - Liquidity provision
 * - Perpetual futures (up to 100x leverage)
 * - Order books (limit orders)
 * - Farming & staking
 */

#ifndef SYNOR_DEX_HPP
#define SYNOR_DEX_HPP

#include <string>
#include <vector>
#include <optional>
#include <memory>
#include <future>
#include <functional>
#include <cstdint>

namespace synor {
namespace dex {

// Forward declarations
class SynorDexImpl;
class PerpsClient;
class OrderBookClient;
class FarmsClient;

// Enums
enum class PoolType {
    ConstantProduct,
    Stable,
    Concentrated
};

enum class PositionSide {
    Long,
    Short
};

enum class OrderType {
    Market,
    Limit,
    StopMarket,
    StopLimit,
    TakeProfit,
    TakeProfitLimit
};

enum class MarginType {
    Cross,
    Isolated
};

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

enum class OrderStatus {
    Pending,
    Open,
    PartiallyFilled,
    Filled,
    Cancelled,
    Expired
};

// Configuration
struct Config {
    std::string api_key;
    std::string endpoint = "https://dex.synor.io/v1";
    std::string ws_endpoint = "wss://dex.synor.io/v1/ws";
    uint32_t timeout_ms = 30000;
    uint32_t retries = 3;
    bool debug = false;
};

// Types
struct Token {
    std::string address;
    std::string symbol;
    std::string name;
    int decimals;
    std::string total_supply;
    std::optional<double> price_usd;
    std::optional<std::string> logo_url;
    bool verified = false;
};

struct Pool {
    std::string id;
    Token token_a;
    Token token_b;
    PoolType pool_type;
    std::string reserve_a;
    std::string reserve_b;
    double fee;
    double tvl_usd;
    double volume_24h;
    double apr;
    std::string lp_token_address;
};

struct PoolFilter {
    std::optional<std::vector<std::string>> tokens;
    std::optional<double> min_tvl;
    std::optional<double> min_volume_24h;
    std::optional<bool> verified;
    std::optional<int> limit;
    std::optional<int> offset;
};

struct Quote {
    std::string token_in;
    std::string token_out;
    std::string amount_in;
    std::string amount_out;
    double price_impact;
    std::vector<std::string> route;
    std::string fee;
    std::string minimum_received;
    int64_t expires_at;
};

struct QuoteParams {
    std::string token_in;
    std::string token_out;
    std::string amount_in;
    std::optional<double> slippage;
};

struct SwapParams {
    std::string token_in;
    std::string token_out;
    std::string amount_in;
    std::string min_amount_out;
    std::optional<int64_t> deadline;
    std::optional<std::string> recipient;
};

struct SwapResult {
    std::string transaction_hash;
    std::string amount_in;
    std::string amount_out;
    double effective_price;
    std::string fee_paid;
    std::vector<std::string> route;
};

struct AddLiquidityParams {
    std::string token_a;
    std::string token_b;
    std::string amount_a;
    std::string amount_b;
    std::optional<std::string> min_amount_a;
    std::optional<std::string> min_amount_b;
    std::optional<int64_t> deadline;
};

struct RemoveLiquidityParams {
    std::string pool;
    std::string lp_amount;
    std::optional<std::string> min_amount_a;
    std::optional<std::string> min_amount_b;
    std::optional<int64_t> deadline;
};

struct LiquidityResult {
    std::string transaction_hash;
    std::string amount_a;
    std::string amount_b;
    std::string lp_tokens;
    double pool_share;
};

struct LPPosition {
    std::string pool_id;
    std::string lp_tokens;
    std::string token_a_amount;
    std::string token_b_amount;
    double value_usd;
    std::string unclaimed_fees_a;
    std::string unclaimed_fees_b;
    double impermanent_loss;
};

struct PerpMarket {
    std::string symbol;
    std::string base_asset;
    std::string quote_asset;
    double index_price;
    double mark_price;
    double funding_rate;
    int64_t next_funding_time;
    std::string open_interest;
    double volume_24h;
    int max_leverage;
};

struct OpenPositionParams {
    std::string market;
    PositionSide side;
    std::string size;
    int leverage;
    OrderType order_type;
    std::optional<double> limit_price;
    std::optional<double> stop_loss;
    std::optional<double> take_profit;
    MarginType margin_type = MarginType::Cross;
    bool reduce_only = false;
};

struct ClosePositionParams {
    std::string market;
    std::optional<std::string> size;
    OrderType order_type = OrderType::Market;
    std::optional<double> limit_price;
};

struct PerpPosition {
    std::string id;
    std::string market;
    PositionSide side;
    std::string size;
    double entry_price;
    double mark_price;
    double liquidation_price;
    std::string margin;
    int leverage;
    std::string unrealized_pnl;
};

struct PerpOrder {
    std::string id;
    std::string market;
    PositionSide side;
    OrderType order_type;
    std::string size;
    std::optional<double> price;
    OrderStatus status;
};

struct FundingPayment {
    std::string market;
    std::string amount;
    double rate;
    std::string position_size;
    int64_t timestamp;
};

struct OrderBookEntry {
    double price;
    std::string size;
    int orders;
};

struct OrderBook {
    std::string market;
    std::vector<OrderBookEntry> bids;
    std::vector<OrderBookEntry> asks;
    int64_t timestamp;
};

struct LimitOrderParams {
    std::string market;
    std::string side;
    double price;
    std::string size;
    TimeInForce time_in_force = TimeInForce::GTC;
    bool post_only = false;
};

struct Order {
    std::string id;
    std::string market;
    std::string side;
    double price;
    std::string size;
    std::string filled_size;
    OrderStatus status;
    TimeInForce time_in_force;
    bool post_only;
};

struct Farm {
    std::string id;
    std::string name;
    Token stake_token;
    std::vector<Token> reward_tokens;
    double tvl_usd;
    double apr;
};

struct StakeParams {
    std::string farm;
    std::string amount;
};

struct FarmPosition {
    std::string farm_id;
    std::string staked_amount;
    std::vector<std::string> pending_rewards;
    int64_t staked_at;
};

struct OHLCV {
    int64_t timestamp;
    double open;
    double high;
    double low;
    double close;
    double volume;
};

struct TradeHistory {
    std::string id;
    std::string market;
    std::string side;
    double price;
    std::string size;
    int64_t timestamp;
    std::string maker;
    std::string taker;
};

struct VolumeStats {
    double volume_24h;
    double volume_7d;
    double volume_30d;
    int trades_24h;
};

struct TVLStats {
    double total_tvl;
    double pools_tvl;
    double farms_tvl;
    double perps_tvl;
};

struct ClaimRewardsResult {
    std::string amount;
    std::string transaction_hash;
};

struct FundingRateInfo {
    double rate;
    int64_t next_time;
};

// Exception
class DexException : public std::runtime_error {
public:
    DexException(const std::string& message,
                 const std::string& code = "",
                 int status = 0)
        : std::runtime_error(message), code_(code), status_(status) {}

    const std::string& code() const { return code_; }
    int status() const { return status_; }

private:
    std::string code_;
    int status_;
};

// Main Client
class SynorDex {
public:
    explicit SynorDex(const Config& config);
    ~SynorDex();

    SynorDex(const SynorDex&) = delete;
    SynorDex& operator=(const SynorDex&) = delete;
    SynorDex(SynorDex&&) noexcept;
    SynorDex& operator=(SynorDex&&) noexcept;

    // Token Operations
    std::future<Token> get_token(const std::string& address);
    std::future<std::vector<Token>> list_tokens();
    std::future<std::vector<Token>> search_tokens(const std::string& query);

    // Pool Operations
    std::future<Pool> get_pool(const std::string& token_a, const std::string& token_b);
    std::future<Pool> get_pool_by_id(const std::string& pool_id);
    std::future<std::vector<Pool>> list_pools(const std::optional<PoolFilter>& filter = std::nullopt);

    // Swap Operations
    std::future<Quote> get_quote(const QuoteParams& params);
    std::future<SwapResult> swap(const SwapParams& params);

    // Liquidity Operations
    std::future<LiquidityResult> add_liquidity(const AddLiquidityParams& params);
    std::future<LiquidityResult> remove_liquidity(const RemoveLiquidityParams& params);
    std::future<std::vector<LPPosition>> get_my_positions();

    // Analytics
    std::future<std::vector<OHLCV>> get_price_history(const std::string& pair,
                                                        const std::string& interval,
                                                        int limit = 100);
    std::future<std::vector<TradeHistory>> get_trade_history(const std::string& pair,
                                                               int limit = 50);
    std::future<VolumeStats> get_volume_stats();
    std::future<TVLStats> get_tvl();

    // Lifecycle
    std::future<bool> health_check();
    void close();
    bool is_closed() const;

    // Sub-clients
    PerpsClient& perps();
    OrderBookClient& orderbook();
    FarmsClient& farms();

private:
    std::unique_ptr<SynorDexImpl> impl_;
};

// Perpetuals Client
class PerpsClient {
public:
    explicit PerpsClient(SynorDex& dex);
    ~PerpsClient();

    std::future<std::vector<PerpMarket>> list_markets();
    std::future<PerpMarket> get_market(const std::string& symbol);
    std::future<PerpPosition> open_position(const OpenPositionParams& params);
    std::future<PerpPosition> close_position(const ClosePositionParams& params);
    std::future<std::vector<PerpPosition>> get_positions();
    std::future<std::vector<PerpOrder>> get_orders();
    std::future<void> cancel_order(const std::string& order_id);
    std::future<int> cancel_all_orders(const std::optional<std::string>& market = std::nullopt);
    std::future<std::vector<FundingPayment>> get_funding_history(const std::string& market,
                                                                   int limit = 100);
    std::future<FundingRateInfo> get_funding_rate(const std::string& market);

private:
    SynorDex& dex_;
};

// OrderBook Client
class OrderBookClient {
public:
    explicit OrderBookClient(SynorDex& dex);
    ~OrderBookClient();

    std::future<OrderBook> get_order_book(const std::string& market, int depth = 20);
    std::future<Order> place_limit_order(const LimitOrderParams& params);
    std::future<void> cancel_order(const std::string& order_id);
    std::future<std::vector<Order>> get_open_orders(const std::optional<std::string>& market = std::nullopt);
    std::future<std::vector<Order>> get_order_history(int limit = 50);

private:
    SynorDex& dex_;
};

// Farms Client
class FarmsClient {
public:
    explicit FarmsClient(SynorDex& dex);
    ~FarmsClient();

    std::future<std::vector<Farm>> list_farms();
    std::future<Farm> get_farm(const std::string& farm_id);
    std::future<FarmPosition> stake(const StakeParams& params);
    std::future<FarmPosition> unstake(const std::string& farm, const std::string& amount);
    std::future<ClaimRewardsResult> claim_rewards(const std::string& farm);
    std::future<std::vector<FarmPosition>> get_my_farm_positions();

private:
    SynorDex& dex_;
};

}  // namespace dex
}  // namespace synor

#endif  // SYNOR_DEX_HPP
