// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./LiquidityPoolETH.sol";
import "./interfaces/ISwapRouter.sol";
import "./interfaces/ICurvePool.sol";
import "./interfaces/IAggregationRouter.sol";
import "./interfaces/IDodoexRouter.sol";
import "./interfaces/IBalancerVault.sol";
import "./interfaces/IWETH.sol";
import "../../liquidity/interfaces/ILiquidityProvider.sol";

/**
 * @title EnhancedSwapRouter
 * @notice Multi-protocol swap router with intelligent routing and decision logic
 * @dev Supports Uniswap V3, Curve, Dodoex PMM, Balancer, and 1inch aggregation
 */
contract EnhancedSwapRouter is AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;
    
    bytes32 public constant COORDINATOR_ROLE = keccak256("COORDINATOR_ROLE");
    bytes32 public constant ROUTING_MANAGER_ROLE = keccak256("ROUTING_MANAGER_ROLE");

    enum SwapProvider {
        UniswapV3,
        Curve,
        Dodoex,
        Balancer,
        OneInch
    }

    // Protocol addresses
    address public immutable uniswapV3Router;
    address public immutable curve3Pool;
    address public immutable dodoexRouter;
    address public immutable balancerVault;
    address public immutable oneInchRouter;
    
    // Token addresses
    address public immutable weth;
    address public immutable usdt;
    address public immutable usdc;
    address public immutable dai;

    // Routing configuration
    struct RoutingConfig {
        SwapProvider[] providers; // Ordered list of providers to try
        uint256[] sizeThresholds; // Size thresholds in wei
        bool enabled;
    }

    mapping(SwapProvider => bool) public providerEnabled;
    mapping(uint256 => RoutingConfig) public sizeBasedRouting; // size category => config
    uint256 public constant SMALL_SWAP_THRESHOLD = 10_000 * 1e18; // $10k
    uint256 public constant MEDIUM_SWAP_THRESHOLD = 100_000 * 1e18; // $100k

    // Uniswap V3 fee tiers
    uint24 public constant FEE_TIER_LOW = 500;
    uint24 public constant FEE_TIER_MEDIUM = 3000;
    uint24 public constant FEE_TIER_HIGH = 10000;

    // Balancer pool IDs (example - would be set via admin)
    mapping(address => mapping(address => bytes32)) public balancerPoolIds; // tokenIn => tokenOut => poolId

    // Dodoex PMM pool addresses (tokenIn => tokenOut => PMM pool address)
    mapping(address => mapping(address => address)) public dodoPoolAddresses;
    address public dodoLiquidityProvider;

    /// @dev Uniswap V3 Quoter for on-chain quotes; set via setUniswapQuoter when deployed on 138/651940
    address public uniswapQuoter;

    event SwapExecuted(
        SwapProvider indexed provider,
        LiquidityPoolETH.AssetType indexed inputAsset,
        address indexed tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        uint256 gasUsed
    );

    event RoutingConfigUpdated(uint256 sizeCategory, SwapProvider[] providers);
    event ProviderToggled(SwapProvider provider, bool enabled);

    error ZeroAddress();
    error ZeroAmount();
    error SwapFailed();
    error InvalidProvider();
    error ProviderDisabled();
    error InsufficientOutput();
    error InvalidRoutingConfig();
    error DodoRouteNotConfigured();

    /**
     * @notice Constructor
     * @param _uniswapV3Router Uniswap V3 SwapRouter address
     * @param _curve3Pool Curve 3pool address
     * @param _dodoexRouter Dodoex Router address
     * @param _balancerVault Balancer Vault address
     * @param _oneInchRouter 1inch Router address (can be address(0))
     * @param _weth WETH address
     * @param _usdt USDT address
     * @param _usdc USDC address
     * @param _dai DAI address
     */
    constructor(
        address _uniswapV3Router,
        address _curve3Pool,
        address _dodoexRouter,
        address _balancerVault,
        address _oneInchRouter,
        address _weth,
        address _usdt,
        address _usdc,
        address _dai
    ) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        
        if (_uniswapV3Router == address(0) || _curve3Pool == address(0) || 
            _dodoexRouter == address(0) || _balancerVault == address(0) ||
            _weth == address(0) || _usdt == address(0) || _usdc == address(0) || _dai == address(0)) {
            revert ZeroAddress();
        }

        uniswapV3Router = _uniswapV3Router;
        curve3Pool = _curve3Pool;
        dodoexRouter = _dodoexRouter;
        balancerVault = _balancerVault;
        oneInchRouter = _oneInchRouter;
        weth = _weth;
        usdt = _usdt;
        usdc = _usdc;
        dai = _dai;

        // Enable all providers by default
        providerEnabled[SwapProvider.UniswapV3] = true;
        providerEnabled[SwapProvider.Curve] = true;
        providerEnabled[SwapProvider.Dodoex] = true;
        providerEnabled[SwapProvider.Balancer] = true;
        if (_oneInchRouter != address(0)) {
            providerEnabled[SwapProvider.OneInch] = true;
        }

        // Initialize default routing configs
        _initializeDefaultRouting();
    }

    /**
     * @notice Swap to stablecoin using intelligent routing
     * @param inputAsset Input asset type (ETH or WETH)
     * @param stablecoinToken Target stablecoin
     * @param amountIn Input amount
     * @param amountOutMin Minimum output (slippage protection)
     * @param preferredProvider Optional preferred provider (0 = auto-select)
     * @return amountOut Output amount
     * @return providerUsed Provider that executed the swap
     */
    function swapToStablecoin(
        LiquidityPoolETH.AssetType inputAsset,
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin,
        SwapProvider preferredProvider
    ) external payable nonReentrant returns (uint256 amountOut, SwapProvider providerUsed) {
        if (amountIn == 0) revert ZeroAmount();
        if (stablecoinToken == address(0)) revert ZeroAddress();
        if (!_isValidStablecoin(stablecoinToken)) revert("EnhancedSwapRouter: invalid stablecoin");

        // Convert ETH to WETH if needed
        if (inputAsset == LiquidityPoolETH.AssetType.ETH) {
            require(msg.value == amountIn, "EnhancedSwapRouter: ETH amount mismatch");
            IWETH(weth).deposit{value: amountIn}();
        }

        // Get routing providers based on swap size
        SwapProvider[] memory providers = _getRoutingProviders(amountIn, preferredProvider);

        // Try each provider in order
        for (uint256 i = 0; i < providers.length; i++) {
            if (!providerEnabled[providers[i]]) continue;

            try this._executeSwap(
                providers[i],
                stablecoinToken,
                amountIn,
                amountOutMin
            ) returns (uint256 output) {
                if (output >= amountOutMin) {
                    // Transfer output to caller
                    IERC20(stablecoinToken).safeTransfer(msg.sender, output);
                    
                    emit SwapExecuted(
                        providers[i],
                        inputAsset,
                        weth,
                        stablecoinToken,
                        amountIn,
                        output,
                        gasleft()
                    );
                    
                    return (output, providers[i]);
                }
            } catch {
                // Try next provider
                continue;
            }
        }

        revert SwapFailed();
    }

    /**
     * @notice Get quote from all enabled providers
     * @param stablecoinToken Target stablecoin
     * @param amountIn Input amount
     * @return providers Array of providers that returned quotes
     * @return amounts Array of output amounts for each provider
     */
    function getQuotes(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (SwapProvider[] memory providers, uint256[] memory amounts) {
        SwapProvider[] memory enabledProviders = new SwapProvider[](5);
        uint256[] memory quotes = new uint256[](5);
        uint256 count = 0;

        // Query each enabled provider
        if (providerEnabled[SwapProvider.UniswapV3]) {
            try this._getUniswapV3Quote(stablecoinToken, amountIn) returns (uint256 quote) {
                enabledProviders[count] = SwapProvider.UniswapV3;
                quotes[count] = quote;
                count++;
            } catch {}
        }

        if (providerEnabled[SwapProvider.Dodoex]) {
            try this._getDodoexQuote(stablecoinToken, amountIn) returns (uint256 quote) {
                enabledProviders[count] = SwapProvider.Dodoex;
                quotes[count] = quote;
                count++;
            } catch {}
        }

        if (providerEnabled[SwapProvider.Balancer]) {
            try this._getBalancerQuote(stablecoinToken, amountIn) returns (uint256 quote) {
                enabledProviders[count] = SwapProvider.Balancer;
                quotes[count] = quote;
                count++;
            } catch {}
        }

        // Resize arrays
        SwapProvider[] memory resultProviders = new SwapProvider[](count);
        uint256[] memory resultQuotes = new uint256[](count);
        for (uint256 i = 0; i < count; i++) {
            resultProviders[i] = enabledProviders[i];
            resultQuotes[i] = quotes[i];
        }

        return (resultProviders, resultQuotes);
    }

    /**
     * @notice Set routing configuration for a size category
     * @param sizeCategory 0 = small, 1 = medium, 2 = large
     * @param providers Ordered list of providers to try
     */
    function setRoutingConfig(
        uint256 sizeCategory,
        SwapProvider[] calldata providers
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        require(sizeCategory < 3, "EnhancedSwapRouter: invalid size category");
        require(providers.length > 0, "EnhancedSwapRouter: empty providers");

        sizeBasedRouting[sizeCategory] = RoutingConfig({
            providers: providers,
            sizeThresholds: new uint256[](0),
            enabled: true
        });

        emit RoutingConfigUpdated(sizeCategory, providers);
    }

    /**
     * @notice Toggle provider on/off
     * @param provider Provider to toggle
     * @param enabled Whether to enable
     */
    function setProviderEnabled(
        SwapProvider provider,
        bool enabled
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        providerEnabled[provider] = enabled;
        emit ProviderToggled(provider, enabled);
    }

    /**
     * @notice Set Balancer pool ID for a token pair
     * @param tokenIn Input token
     * @param tokenOut Output token
     * @param poolId Balancer pool ID
     */
    function setBalancerPoolId(
        address tokenIn,
        address tokenOut,
        bytes32 poolId
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        balancerPoolIds[tokenIn][tokenOut] = poolId;
    }

    /**
     * @notice Set Dodoex PMM pool address for a token pair
     * @param tokenIn Input token
     * @param tokenOut Output token
     * @param poolAddress Dodo PMM pool address
     */
    function setDodoPoolAddress(
        address tokenIn,
        address tokenOut,
        address poolAddress
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        dodoPoolAddresses[tokenIn][tokenOut] = poolAddress;
    }

    /**
     * @notice Set DODO liquidity provider implementation for environments that execute
     * swaps via a local provider/integration instead of an external Dodoex router.
     * @param provider Address of ILiquidityProvider-compatible contract
     */
    function setDodoLiquidityProvider(address provider) external onlyRole(ROUTING_MANAGER_ROLE) {
        dodoLiquidityProvider = provider;
    }

    /**
     * @notice Set Uniswap V3 Quoter address for on-chain quotes
     * @param _quoter Quoter contract address (address(0) to use 0.5% slippage estimate)
     */
    function setUniswapQuoter(address _quoter) external onlyRole(ROUTING_MANAGER_ROLE) {
        uniswapQuoter = _quoter;
    }

    /**
     * @notice Swap arbitrary token pair via Dodoex when pool is configured
     * @param tokenIn Input token
     * @param tokenOut Output token
     * @param amountIn Input amount
     * @param amountOutMin Minimum output (slippage protection)
     * @return amountOut Output amount
     */
    function swapTokenToToken(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin
    ) external nonReentrant returns (uint256 amountOut) {
        if (amountIn == 0) revert ZeroAmount();
        if (tokenIn == address(0) || tokenOut == address(0)) revert ZeroAddress();
        address pool = dodoPoolAddresses[tokenIn][tokenOut];
        if (!_hasDodoRoute(tokenIn, tokenOut, pool)) revert DodoRouteNotConfigured();

        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);

        if (dodoLiquidityProvider != address(0)) {
            IERC20(tokenIn).approve(dodoLiquidityProvider, amountIn);
            amountOut = ILiquidityProvider(dodoLiquidityProvider).executeSwap(
                tokenIn,
                tokenOut,
                amountIn,
                amountOutMin
            );
            require(amountOut >= amountOutMin, "EnhancedSwapRouter: insufficient output");
            IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
            return amountOut;
        }

        IERC20(tokenIn).approve(dodoexRouter, amountIn);

        address[] memory dodoPairs = new address[](1);
        dodoPairs[0] = pool;

        IDodoexRouter.DodoSwapParams memory params = IDodoexRouter.DodoSwapParams({
            fromToken: tokenIn,
            toToken: tokenOut,
            fromTokenAmount: amountIn,
            minReturnAmount: amountOutMin,
            dodoPairs: dodoPairs,
            directions: 0,
            isIncentive: false,
            deadLine: block.timestamp + 300
        });

        amountOut = IDodoexRouter(dodoexRouter).dodoSwapV2TokenToToken(params);
        require(amountOut >= amountOutMin, "EnhancedSwapRouter: insufficient output");
        IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
        return amountOut;
    }

    // ============ Internal Functions ============

    /**
     * @notice Execute swap via specified provider
     */
    function _executeSwap(
        SwapProvider provider,
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) external returns (uint256) {
        require(msg.sender == address(this), "EnhancedSwapRouter: internal only");

        if (provider == SwapProvider.UniswapV3) {
            return _executeUniswapV3Swap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.Dodoex) {
            return _executeDodoexSwap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.Balancer) {
            return _executeBalancerSwap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.Curve) {
            return _executeCurveSwap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.OneInch && oneInchRouter != address(0)) {
            return _execute1inchSwap(stablecoinToken, amountIn, amountOutMin);
        }

        revert InvalidProvider();
    }

    /**
     * @notice Get routing providers based on swap size
     */
    function _getRoutingProviders(
        uint256 amountIn,
        SwapProvider preferredProvider
    ) internal view returns (SwapProvider[] memory) {
        // If preferred provider is specified and enabled, use it first
        if (preferredProvider != SwapProvider.UniswapV3 && providerEnabled[preferredProvider]) {
            SwapProvider[] memory providers = new SwapProvider[](1);
            providers[0] = preferredProvider;
            return providers;
        }

        // Determine size category
        uint256 category;
        if (amountIn < SMALL_SWAP_THRESHOLD) {
            category = 0; // Small
        } else if (amountIn < MEDIUM_SWAP_THRESHOLD) {
            category = 1; // Medium
        } else {
            category = 2; // Large
        }

        RoutingConfig memory config = sizeBasedRouting[category];
        if (config.enabled && config.providers.length > 0) {
            return config.providers;
        }

        // Default fallback routing
        SwapProvider[] memory defaultProviders = new SwapProvider[](5);
        defaultProviders[0] = SwapProvider.Dodoex;
        defaultProviders[1] = SwapProvider.UniswapV3;
        defaultProviders[2] = SwapProvider.Balancer;
        defaultProviders[3] = SwapProvider.Curve;
        defaultProviders[4] = SwapProvider.OneInch;
        return defaultProviders;
    }

    /**
     * @notice Execute Uniswap V3 swap
     */
    function _executeUniswapV3Swap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        // Approve for swap
        IERC20 wethToken = IERC20(weth);
        wethToken.approve(uniswapV3Router, amountIn);

        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: weth,
            tokenOut: stablecoinToken,
            fee: FEE_TIER_MEDIUM,
            recipient: address(this),
            deadline: block.timestamp + 300,
            amountIn: amountIn,
            amountOutMinimum: amountOutMin,
            sqrtPriceLimitX96: 0
        });

        return ISwapRouter(uniswapV3Router).exactInputSingle(params);
    }

    /**
     * @notice Execute Dodoex PMM swap
     */
    function _executeDodoexSwap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        address pool = dodoPoolAddresses[weth][stablecoinToken];
        if (!_hasDodoRoute(weth, stablecoinToken, pool)) revert DodoRouteNotConfigured();

        if (dodoLiquidityProvider != address(0)) {
            IERC20 wethTokenViaProvider = IERC20(weth);
            wethTokenViaProvider.approve(dodoLiquidityProvider, amountIn);
            return ILiquidityProvider(dodoLiquidityProvider).executeSwap(
                weth,
                stablecoinToken,
                amountIn,
                amountOutMin
            );
        }

        IERC20 wethToken = IERC20(weth);
        wethToken.approve(dodoexRouter, amountIn);

        address[] memory dodoPairs = new address[](1);
        dodoPairs[0] = pool;

        IDodoexRouter.DodoSwapParams memory params = IDodoexRouter.DodoSwapParams({
            fromToken: weth,
            toToken: stablecoinToken,
            fromTokenAmount: amountIn,
            minReturnAmount: amountOutMin,
            dodoPairs: dodoPairs,
            directions: 0,
            isIncentive: false,
            deadLine: block.timestamp + 300
        });

        return IDodoexRouter(dodoexRouter).dodoSwapV2TokenToToken(params);
    }

    /**
     * @notice Execute Balancer swap
     */
    function _executeBalancerSwap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        bytes32 poolId = balancerPoolIds[weth][stablecoinToken];
        require(poolId != bytes32(0), "EnhancedSwapRouter: pool not configured");

        IERC20 wethToken = IERC20(weth);
        wethToken.approve(balancerVault, amountIn);

        IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap({
            poolId: poolId,
            kind: IBalancerVault.SwapKind.GIVEN_IN,
            assetIn: weth,
            assetOut: stablecoinToken,
            amount: amountIn,
            userData: ""
        });

        IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({
            sender: address(this),
            fromInternalBalance: false,
            recipient: payable(address(this)),
            toInternalBalance: false
        });

        return IBalancerVault(balancerVault).swap(
            singleSwap,
            funds,
            amountOutMin,
            block.timestamp + 300
        );
    }

    /**
     * @notice Execute Curve swap
     */
    function _executeCurveSwap(
        address,
        uint256,
        uint256
    ) internal pure returns (uint256) {
        // Curve 3pool doesn't support WETH directly
        // Would need intermediate swap or different pool
        revert("EnhancedSwapRouter: Curve direct swap not supported");
    }

    /**
     * @notice Execute 1inch swap
     */
    function _execute1inchSwap(
        address,
        uint256 amountIn,
        uint256
    ) internal returns (uint256) {
        if (oneInchRouter == address(0)) revert ProviderDisabled();
        
        IERC20 wethToken = IERC20(weth);
        wethToken.approve(oneInchRouter, amountIn);
        
        // 1inch: requires route data from 1inch API (e.g. /swap/v5.2/138/swap). Use 1inch SDK or API to get calldata and execute separately.
        revert("EnhancedSwapRouter: 1inch requires route calldata from API; use 1inch aggregator SDK");
    }

    /**
     * @notice Get Uniswap V3 quote (view)
     * When UNISWAP_QUOTER_ADDRESS is configured, queries quoter. Otherwise returns
     * estimate via amountIn * 9950/10000 (0.5% slippage) for stablecoin pairs.
     */
    function _getUniswapV3Quote(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (uint256) {
        if (uniswapQuoter != address(0) && _isValidStablecoin(stablecoinToken)) {
            // IQuoter.quoteExactInputSingle(tokenIn, tokenOut, fee, amountIn, sqrtPriceLimitX96)
            (bool ok, bytes memory data) = uniswapQuoter.staticcall(
                abi.encodeWithSignature(
                    "quoteExactInputSingle(address,address,uint24,uint256,uint160)",
                    weth,
                    stablecoinToken,
                    FEE_TIER_MEDIUM,
                    amountIn,
                    uint160(0)
                )
            );
            if (ok && data.length >= 32) {
                uint256 quoted = abi.decode(data, (uint256));
                if (quoted > 0) return quoted;
            }
        }
        if (_isValidStablecoin(stablecoinToken)) {
            return (amountIn * 9950) / 10000; // 0.5% slippage estimate for WETH->stable
        }
        return 0;
    }

    /**
     * @notice Get Dodoex quote (view)
     */
    function _getDodoexQuote(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (uint256) {
        if (dodoLiquidityProvider != address(0)) {
            if (!ILiquidityProvider(dodoLiquidityProvider).supportsTokenPair(weth, stablecoinToken)) {
                return 0;
            }
            (uint256 amountOut,) = ILiquidityProvider(dodoLiquidityProvider).getQuote(weth, stablecoinToken, amountIn);
            return amountOut;
        }
        if (dodoPoolAddresses[weth][stablecoinToken] == address(0)) {
            return 0;
        }
        return IDodoexRouter(dodoexRouter).getDodoSwapQuote(weth, stablecoinToken, amountIn);
    }

    function _hasDodoRoute(
        address tokenIn,
        address tokenOut,
        address configuredPool
    ) internal view returns (bool) {
        if (dodoLiquidityProvider != address(0)) {
            return ILiquidityProvider(dodoLiquidityProvider).supportsTokenPair(tokenIn, tokenOut);
        }
        return configuredPool != address(0);
    }

    /**
     * @notice Get Balancer quote (view)
     * When poolId configured, would query Balancer. Otherwise estimate for stablecoins.
     */
    function _getBalancerQuote(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (uint256) {
        bytes32 poolId = balancerPoolIds[weth][stablecoinToken];
        if (poolId != bytes32(0)) {
            (address[] memory tokens, uint256[] memory balances,) =
                IBalancerVault(balancerVault).getPoolTokens(poolId);
            if (tokens.length >= 2 && balances.length >= 2) {
                uint256 wethIdx = type(uint256).max;
                uint256 stableIdx = type(uint256).max;
                for (uint256 i = 0; i < tokens.length; i++) {
                    if (tokens[i] == weth) wethIdx = i;
                    if (tokens[i] == stablecoinToken) stableIdx = i;
                }
                if (wethIdx != type(uint256).max && stableIdx != type(uint256).max && balances[wethIdx] > 0) {
                    uint256 amountOut = (amountIn * balances[stableIdx]) / balances[wethIdx];
                    return (amountOut * 9950) / 10000; // 0.5% slippage
                }
            }
        }
        if (_isValidStablecoin(stablecoinToken)) {
            return (amountIn * 9950) / 10000; // 0.5% slippage estimate when pool not configured
        }
        return 0;
    }

    /**
     * @notice Initialize default routing configurations
     */
    function _initializeDefaultRouting() internal {
        // Small swaps (< $10k): Uniswap V3, Dodoex
        SwapProvider[] memory smallProviders = new SwapProvider[](2);
        smallProviders[0] = SwapProvider.UniswapV3;
        smallProviders[1] = SwapProvider.Dodoex;
        sizeBasedRouting[0] = RoutingConfig({
            providers: smallProviders,
            sizeThresholds: new uint256[](0),
            enabled: true
        });

        // Medium swaps ($10k-$100k): Dodoex, Balancer, Uniswap V3
        SwapProvider[] memory mediumProviders = new SwapProvider[](3);
        mediumProviders[0] = SwapProvider.Dodoex;
        mediumProviders[1] = SwapProvider.Balancer;
        mediumProviders[2] = SwapProvider.UniswapV3;
        sizeBasedRouting[1] = RoutingConfig({
            providers: mediumProviders,
            sizeThresholds: new uint256[](0),
            enabled: true
        });

        // Large swaps (> $100k): Dodoex, Curve, Balancer
        SwapProvider[] memory largeProviders = new SwapProvider[](3);
        largeProviders[0] = SwapProvider.Dodoex;
        largeProviders[1] = SwapProvider.Curve;
        largeProviders[2] = SwapProvider.Balancer;
        sizeBasedRouting[2] = RoutingConfig({
            providers: largeProviders,
            sizeThresholds: new uint256[](0),
            enabled: true
        });
    }

    /**
     * @notice Check if token is valid stablecoin
     */
    function _isValidStablecoin(address token) internal view returns (bool) {
        return token == usdt || token == usdc || token == dai;
    }

    // Allow contract to receive ETH
    receive() external payable {}
}
