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

import {Test} from "forge-std/Test.sol";
import "../../../contracts/bridge/trustless/EnhancedSwapRouter.sol";
import "../../../contracts/bridge/trustless/LiquidityPoolETH.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _mint(msg.sender, 1000000 ether);
    }
}

contract MockLiquidityProvider {
    mapping(address => mapping(address => bool)) public supported;
    mapping(address => mapping(address => uint256)) public quotes;

    function setSupport(address tokenIn, address tokenOut, bool isSupported) external {
        supported[tokenIn][tokenOut] = isSupported;
    }

    function setQuote(address tokenIn, address tokenOut, uint256 amountOut) external {
        quotes[tokenIn][tokenOut] = amountOut;
    }

    function getQuote(
        address tokenIn,
        address tokenOut,
        uint256
    ) external view returns (uint256 amountOut, uint256 slippageBps) {
        if (!supported[tokenIn][tokenOut]) {
            return (0, 10000);
        }
        return (quotes[tokenIn][tokenOut], 30);
    }

    function executeSwap(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256
    ) external returns (uint256 amountOut) {
        require(supported[tokenIn][tokenOut], "unsupported");
        amountOut = quotes[tokenIn][tokenOut];
        ERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
        ERC20(tokenOut).transfer(msg.sender, amountOut);
    }

    function supportsTokenPair(address tokenIn, address tokenOut) external view returns (bool) {
        return supported[tokenIn][tokenOut];
    }

    function providerName() external pure returns (string memory) {
        return "Mock";
    }

    function estimateGas(address, address, uint256) external pure returns (uint256) {
        return 150000;
    }
}

contract EnhancedSwapRouterTest is Test {
    EnhancedSwapRouter public router;
    LiquidityPoolETH public liquidityPool;
    MockLiquidityProvider public liquidityProvider;
    
    MockERC20 public weth;
    MockERC20 public usdt;
    MockERC20 public usdc;
    MockERC20 public dai;

    address public deployer = address(0xDE0001);
    address public user = address(0x1111);

    // Mock protocol addresses
    address public uniswapV3Router = address(0x1111111111111111111111111111111111111111);
    address public curve3Pool = address(0x2222222222222222222222222222222222222222);
    address public dodoexRouter = address(0x3333333333333333333333333333333333333333);
    address public balancerVault = address(0x4444444444444444444444444444444444444444);
    address public oneInchRouter = address(0x5555555555555555555555555555555555555555);

    function setUp() public {
        vm.startPrank(deployer);

        // Deploy mock tokens
        weth = new MockERC20("Wrapped Ether", "WETH");
        usdt = new MockERC20("Tether USD", "USDT");
        usdc = new MockERC20("USD Coin", "USDC");
        dai = new MockERC20("Dai Stablecoin", "DAI");
        liquidityProvider = new MockLiquidityProvider();

        // Deploy liquidity pool
        liquidityPool = new LiquidityPoolETH(address(weth), 5, 11000);

        // Deploy enhanced swap router
        router = new EnhancedSwapRouter(
            uniswapV3Router,
            curve3Pool,
            dodoexRouter,
            balancerVault,
            oneInchRouter,
            address(weth),
            address(usdt),
            address(usdc),
            address(dai)
        );

        // Grant ROUTING_MANAGER_ROLE to deployer for tests
        router.grantRole(router.ROUTING_MANAGER_ROLE(), deployer);

        // Fund user
        vm.deal(user, 100 ether);

        vm.stopPrank();
    }

    function testInitialization() public {
        assertEq(address(router.weth()), address(weth));
        assertEq(address(router.usdt()), address(usdt));
        assertTrue(router.providerEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3));
        assertTrue(router.providerEnabled(EnhancedSwapRouter.SwapProvider.Dodoex));
    }

    function testSetProviderEnabled() public {
        // Deployer already has ROUTING_MANAGER_ROLE from setUp
        vm.prank(deployer);
        router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, false);
        
        assertFalse(router.providerEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3));
        
        // Re-enable for other tests
        vm.prank(deployer);
        router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, true);
        
        assertTrue(router.providerEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3));
    }

    function testSetRoutingConfig() public {
        // Deployer already has ROUTING_MANAGER_ROLE from setUp
        EnhancedSwapRouter.SwapProvider[] memory providers = new EnhancedSwapRouter.SwapProvider[](2);
        providers[0] = EnhancedSwapRouter.SwapProvider.Dodoex;
        providers[1] = EnhancedSwapRouter.SwapProvider.Balancer;

        vm.prank(deployer);
        router.setRoutingConfig(0, providers); // Small swaps

        // Verify config was set (would need getter function)
        // For now, just verify no revert
        assertTrue(true);
    }

    function testSetBalancerPoolId() public {
        // Deployer already has ROUTING_MANAGER_ROLE from setUp
        bytes32 poolId = keccak256("test-pool");

        vm.prank(deployer);
        router.setBalancerPoolId(address(weth), address(usdt), poolId);

        assertEq(router.balancerPoolIds(address(weth), address(usdt)), poolId);
    }

    function testGetQuotes() public view {
        // This would require actual protocol integration
        // For now, just verify function exists
        (EnhancedSwapRouter.SwapProvider[] memory providers, uint256[] memory amounts) = 
            router.getQuotes(address(usdt), 1 ether);
        
        // In production, would verify quotes are returned
        assertTrue(providers.length >= 0);
    }

    function testSetDodoPoolAddress() public {
        address mockPool = address(0xDD00);
        vm.prank(deployer);
        router.setDodoPoolAddress(address(weth), address(usdt), mockPool);
        assertEq(router.dodoPoolAddresses(address(weth), address(usdt)), mockPool);
        vm.prank(deployer);
        router.setDodoPoolAddress(address(usdt), address(weth), mockPool);
        assertEq(router.dodoPoolAddresses(address(usdt), address(weth)), mockPool);
    }

    function testSwapTokenToTokenRevertsWhenPoolNotSet() public {
        vm.prank(user);
        weth.approve(address(router), 1 ether);
        vm.expectRevert(EnhancedSwapRouter.DodoRouteNotConfigured.selector);
        router.swapTokenToToken(address(weth), address(usdt), 1 ether, 0);
    }

    function testSetDodoLiquidityProvider() public {
        vm.prank(deployer);
        router.setDodoLiquidityProvider(address(liquidityProvider));
        assertEq(router.dodoLiquidityProvider(), address(liquidityProvider));
    }

    function testGetQuotesUsesDodoLiquidityProviderWhenRouteSupported() public {
        vm.startPrank(deployer);
        router.setDodoLiquidityProvider(address(liquidityProvider));
        router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.UniswapV3, false);
        router.setProviderEnabled(EnhancedSwapRouter.SwapProvider.Balancer, false);
        vm.stopPrank();

        liquidityProvider.setSupport(address(weth), address(usdt), true);
        liquidityProvider.setQuote(address(weth), address(usdt), 1234);

        (EnhancedSwapRouter.SwapProvider[] memory providers, uint256[] memory amounts) =
            router.getQuotes(address(usdt), 1 ether);

        assertEq(providers.length, 1);
        assertEq(uint256(providers[0]), uint256(EnhancedSwapRouter.SwapProvider.Dodoex));
        assertEq(amounts[0], 1234);
    }

    function testSwapTokenToTokenRevertsWhenProviderRouteMissing() public {
        address mockPool = address(0xDD00);
        vm.startPrank(deployer);
        router.setDodoPoolAddress(address(weth), address(usdt), mockPool);
        router.setDodoLiquidityProvider(address(liquidityProvider));
        vm.stopPrank();

        vm.prank(user);
        vm.expectRevert(EnhancedSwapRouter.DodoRouteNotConfigured.selector);
        router.swapTokenToToken(address(weth), address(usdt), 1 ether, 0);
    }
}
