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

import {Script, console} from "forge-std/Script.sol";
import "../../../contracts/bridge/trustless/EnhancedSwapRouterV2.sol";
import "../../../contracts/bridge/trustless/IntentBridgeCoordinatorV2.sol";
import "../../../contracts/bridge/trustless/RouteTypesV2.sol";
import "../../../contracts/bridge/trustless/adapters/DodoRouteExecutorAdapter.sol";
import "../../../contracts/bridge/trustless/adapters/DodoV3RouteExecutorAdapter.sol";
import "../../../contracts/bridge/trustless/adapters/UniswapV3RouteExecutorAdapter.sol";
import "../../../contracts/bridge/trustless/adapters/BalancerRouteExecutorAdapter.sol";
import "../../../contracts/bridge/trustless/adapters/CurveRouteExecutorAdapter.sol";
import "../../../contracts/bridge/trustless/adapters/OneInchRouteExecutorAdapter.sol";

contract DeployEnhancedSwapRouterV2 is Script {
    address constant CHAIN138_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address constant CHAIN138_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
    address constant CHAIN138_USDT = 0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1;
    address constant CHAIN138_USDC = 0x71D6687F38b93CCad569Fa6352c876eea967201b;
    address constant CHAIN138_DAI_PLACEHOLDER = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

    address constant CHAIN138_cUSDT = 0x93E66202A11B1772E55407B32B44e5Cd8eda7f22;
    address constant CHAIN138_cUSDC = 0xf22258f57794CC8E06237084b353Ab30fFfa640b;
    address constant CHAIN138_cEURT = 0xdf4b71c61E5912712C1Bdd451416B9aC26949d72;
    address constant CHAIN138_cXAUC = 0x290E52a8819A4fbD0714E517225429aA2B70EC6b;

    address constant CHAIN138_POOL_CUSDTCUSDC = 0x9e89bAe009adf128782E19e8341996c596ac40dC;
    address constant CHAIN138_POOL_CUSDTUSDT = 0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66;
    address constant CHAIN138_POOL_CUSDCUSDC = 0xc39B7D0F40838cbFb54649d327f49a6DAC964062;
    address constant CHAIN138_POOL_CUSDT_XAU_PUBLIC = 0x1AA55E2001E5651349aFf5a63FD7a7ae44f0f1b0;
    address constant CHAIN138_POOL_CUSDC_XAU_PUBLIC = 0xEa9AC6357CaCB42a83b9082B870610363b177CbA;
    address constant CHAIN138_POOL_CEURT_XAU_PUBLIC = 0xba99bc1eAac164569d5aca96c806934dDaf970CF;
    address constant CHAIN138_D3_PROXY = 0xc9a11abB7C63d88546Be24D58a6d95e3762cB843;
    address constant CHAIN138_D3_MM = 0x6550A3a59070061a262a893A1D6F3F490afFDBDA;

    function run() external {
        require(block.chainid == 138, "DeployEnhancedSwapRouterV2: Chain 138 only");
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        address deployer = vm.addr(deployerPrivateKey);

        address dodoProvider = vm.envOr("DODO_PMM_PROVIDER_ADDRESS", address(0));
        if (dodoProvider == address(0)) {
            dodoProvider = vm.envOr("DODO_PMM_PROVIDER", address(0));
        }
        require(dodoProvider != address(0), "DeployEnhancedSwapRouterV2: DODO PMM provider required");

        address uniswapRouter = vm.envOr("UNISWAP_V3_ROUTER", address(0));
        address uniswapQuoter = vm.envOr("UNISWAP_QUOTER_ADDRESS", address(0));
        address balancerVault = vm.envOr("BALANCER_VAULT", address(0));
        address curvePool = vm.envOr("CURVE_3POOL", address(0));
        address oneInchRouter = vm.envOr("ONEINCH_ROUTER", address(0));
        address wethUsdtPool = vm.envOr("CHAIN138_POOL_WETH_USDT", address(0));
        address wethUsdcPool = vm.envOr("CHAIN138_POOL_WETH_USDC", address(0));
        uint24 uniswapWethUsdtFee = uint24(vm.envOr("UNISWAP_V3_WETH_USDT_FEE", uint256(3000)));
        uint24 uniswapWethUsdcFee = uint24(vm.envOr("UNISWAP_V3_WETH_USDC_FEE", uint256(3000)));
        bytes32 balancerWethUsdtPoolId = vm.envOr("BALANCER_WETH_USDT_POOL_ID", bytes32(0));
        bytes32 balancerWethUsdcPoolId = vm.envOr("BALANCER_WETH_USDC_POOL_ID", bytes32(0));
        address d3Proxy = vm.envOr("CHAIN138_D3_PROXY_ADDRESS", CHAIN138_D3_PROXY);
        address d3Pool = vm.envOr("CHAIN138_D3_MM_ADDRESS", CHAIN138_D3_MM);

        console.log("=== EnhancedSwapRouterV2 Deployment ===");
        console.log("Deployer:", deployer);
        console.log("DODO PMM provider:", dodoProvider);
        console.log("UNISWAP_V3_ROUTER:", uniswapRouter);
        console.log("UNISWAP_QUOTER_ADDRESS:", uniswapQuoter);
        console.log("BALANCER_VAULT:", balancerVault);
        console.log("CURVE_3POOL:", curvePool);
        console.log("ONEINCH_ROUTER:", oneInchRouter);
        console.log("CHAIN138_POOL_WETH_USDT:", wethUsdtPool);
        console.log("CHAIN138_POOL_WETH_USDC:", wethUsdcPool);
        console.log("CHAIN138_D3_PROXY_ADDRESS:", d3Proxy);
        console.log("CHAIN138_D3_MM_ADDRESS:", d3Pool);

        vm.startBroadcast(deployerPrivateKey);

        DodoRouteExecutorAdapter dodoAdapter = new DodoRouteExecutorAdapter();
        DodoV3RouteExecutorAdapter dodoV3Adapter = new DodoV3RouteExecutorAdapter();
        UniswapV3RouteExecutorAdapter uniswapAdapter = new UniswapV3RouteExecutorAdapter();
        BalancerRouteExecutorAdapter balancerAdapter = new BalancerRouteExecutorAdapter();
        CurveRouteExecutorAdapter curveAdapter = new CurveRouteExecutorAdapter();
        OneInchRouteExecutorAdapter oneInchAdapter = new OneInchRouteExecutorAdapter();

        EnhancedSwapRouterV2 router =
            new EnhancedSwapRouterV2(CHAIN138_WETH, CHAIN138_USDT, CHAIN138_USDC, CHAIN138_DAI_PLACEHOLDER);
        IntentBridgeCoordinatorV2 coordinator = new IntentBridgeCoordinatorV2(address(router));

        router.setProviderAdapter(RouteTypesV2.Provider.Dodo, address(dodoAdapter));
        router.setProviderAdapter(RouteTypesV2.Provider.DodoV3, address(dodoV3Adapter));
        router.setProviderAdapter(RouteTypesV2.Provider.UniswapV3, address(uniswapAdapter));
        router.setProviderAdapter(RouteTypesV2.Provider.Balancer, address(balancerAdapter));
        router.setProviderAdapter(RouteTypesV2.Provider.Curve, address(curveAdapter));
        router.setProviderAdapter(RouteTypesV2.Provider.OneInch, address(oneInchAdapter));
        _setDodoOnlyRouting(router);

        _setDodoPair(router, CHAIN138_cUSDT, CHAIN138_cUSDC, dodoProvider, CHAIN138_POOL_CUSDTCUSDC);
        _setDodoPair(router, CHAIN138_cUSDT, CHAIN138_USDT, dodoProvider, CHAIN138_POOL_CUSDTUSDT);
        _setDodoPair(router, CHAIN138_cUSDC, CHAIN138_USDC, dodoProvider, CHAIN138_POOL_CUSDCUSDC);
        _setDodoPair(router, CHAIN138_cUSDT, CHAIN138_cXAUC, dodoProvider, CHAIN138_POOL_CUSDT_XAU_PUBLIC);
        _setDodoPair(router, CHAIN138_cUSDC, CHAIN138_cXAUC, dodoProvider, CHAIN138_POOL_CUSDC_XAU_PUBLIC);
        _setDodoPair(router, CHAIN138_cEURT, CHAIN138_cXAUC, dodoProvider, CHAIN138_POOL_CEURT_XAU_PUBLIC);

        if (wethUsdtPool != address(0)) {
            _setDodoPair(router, CHAIN138_WETH, CHAIN138_USDT, dodoProvider, wethUsdtPool);
        }
        if (wethUsdcPool != address(0)) {
            _setDodoPair(router, CHAIN138_WETH, CHAIN138_USDC, dodoProvider, wethUsdcPool);
        }

        if (d3Proxy != address(0) && d3Pool != address(0)) {
            _setDodoV3Pair(router, CHAIN138_WETH10, CHAIN138_USDT, d3Proxy, d3Pool);
        } else {
            router.setProviderEnabled(RouteTypesV2.Provider.DodoV3, false);
        }

        if (uniswapRouter != address(0) && uniswapQuoter != address(0)) {
            _setUniswapPair(router, CHAIN138_WETH, CHAIN138_USDT, uniswapRouter, uniswapQuoter, uniswapWethUsdtFee);
            _setUniswapPair(router, CHAIN138_WETH, CHAIN138_USDC, uniswapRouter, uniswapQuoter, uniswapWethUsdcFee);
        } else {
            router.setProviderEnabled(RouteTypesV2.Provider.UniswapV3, false);
        }

        if (balancerVault != address(0) && balancerWethUsdtPoolId != bytes32(0)) {
            _setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDT, balancerVault, balancerWethUsdtPoolId);
        }
        if (balancerVault != address(0) && balancerWethUsdcPoolId != bytes32(0)) {
            _setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDC, balancerVault, balancerWethUsdcPoolId);
        }
        if (
            balancerVault == address(0)
                || (balancerWethUsdtPoolId == bytes32(0) && balancerWethUsdcPoolId == bytes32(0))
        ) {
            router.setProviderEnabled(RouteTypesV2.Provider.Balancer, false);
        }

        if (curvePool != address(0)) {
            _setCurvePair(router, CHAIN138_USDT, CHAIN138_USDC, curvePool, 0, 1, false);
        } else {
            router.setProviderEnabled(RouteTypesV2.Provider.Curve, false);
        }

        if (oneInchRouter == address(0)) {
            router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
        }
        _disableNonDodoProviders(router);

        vm.stopBroadcast();

        console.log("EnhancedSwapRouterV2:", address(router));
        console.log("IntentBridgeCoordinatorV2:", address(coordinator));
        console.log("DodoRouteExecutorAdapter:", address(dodoAdapter));
        console.log("DodoV3RouteExecutorAdapter:", address(dodoV3Adapter));
        console.log("UniswapV3RouteExecutorAdapter:", address(uniswapAdapter));
        console.log("BalancerRouteExecutorAdapter:", address(balancerAdapter));
        console.log("CurveRouteExecutorAdapter:", address(curveAdapter));
        console.log("OneInchRouteExecutorAdapter:", address(oneInchAdapter));
    }

    function _setDodoOnlyRouting(EnhancedSwapRouterV2 router) internal {
        RouteTypesV2.Provider[] memory providers = new RouteTypesV2.Provider[](1);
        providers[0] = RouteTypesV2.Provider.Dodo;
        router.setRoutingConfig(0, providers);
        router.setRoutingConfig(1, providers);
        router.setRoutingConfig(2, providers);
    }

    function _disableNonDodoProviders(EnhancedSwapRouterV2 router) internal {
        router.setProviderEnabled(RouteTypesV2.Provider.UniswapV3, false);
        router.setProviderEnabled(RouteTypesV2.Provider.Balancer, false);
        router.setProviderEnabled(RouteTypesV2.Provider.Curve, false);
        router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
        router.setProviderEnabled(RouteTypesV2.Provider.Partner, false);
    }

    function _setDodoPair(EnhancedSwapRouterV2 router, address tokenA, address tokenB, address target, address pool)
        internal
    {
        bytes memory providerData = abi.encode(pool);
        router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Dodo, target, providerData, true);
        router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Dodo, target, providerData, true);
    }

    function _setUniswapPair(
        EnhancedSwapRouterV2 router,
        address tokenA,
        address tokenB,
        address target,
        address quoter,
        uint24 fee
    ) internal {
        bytes memory providerData = abi.encode(bytes(""), fee, quoter, false);
        router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.UniswapV3, target, providerData, true);
        router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.UniswapV3, target, providerData, true);
    }

    function _setDodoV3Pair(EnhancedSwapRouterV2 router, address tokenA, address tokenB, address target, address pool)
        internal
    {
        bytes memory providerData = abi.encode(pool);
        router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.DodoV3, target, providerData, true);
        router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.DodoV3, target, providerData, true);
    }

    function _setBalancerPair(
        EnhancedSwapRouterV2 router,
        address tokenA,
        address tokenB,
        address target,
        bytes32 poolId
    ) internal {
        bytes memory providerData = abi.encode(poolId);
        router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Balancer, target, providerData, true);
        router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Balancer, target, providerData, true);
    }

    function _setCurvePair(
        EnhancedSwapRouterV2 router,
        address tokenA,
        address tokenB,
        address target,
        int128 i,
        int128 j,
        bool useUnderlying
    ) internal {
        bytes memory providerData = abi.encode(i, j, useUnderlying);
        router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Curve, target, providerData, true);
        router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Curve, target, providerData, true);
    }
}
