pragma solidity ^0.8.26;

import "../TestUtils.sol";
import "../TychoRouterTestSetup.sol";
import "@src/executors/MaverickV2Executor.sol";
import {Constants} from "../Constants.sol";

contract MaverickV2ExecutorExposed is MaverickV2Executor {
    constructor() MaverickV2Executor() {}

    function decodeParams(bytes calldata data)
        external
        pure
        returns (address target, IERC20 tokenIn)
    {
        return _decodeData(data);
    }
}

contract MaverickV2ExecutorTest is TestUtils, Constants {
    using SafeERC20 for IERC20;

    MaverickV2ExecutorExposed maverickV2Exposed;
    IERC20 GHO = IERC20(GHO_ADDR);
    IERC20 USDC = IERC20(USDC_ADDR);

    function setUp() public {
        uint256 forkBlock = 22096000;
        vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
        maverickV2Exposed = new MaverickV2ExecutorExposed();
    }

    function testDecodeParams() public view {
        bytes memory params =
            abi.encodePacked(GHO_USDC_POOL, GHO_ADDR, USDC_ADDR);

        (address target, IERC20 tokenIn) =
            maverickV2Exposed.decodeParams(params);

        assertEq(address(tokenIn), GHO_ADDR);
        assertEq(target, GHO_USDC_POOL);
    }

    function testDecodeParamsInvalidDataLength() public {
        bytes memory invalidParams = abi.encodePacked(GHO_USDC_POOL, GHO_ADDR);

        vm.expectRevert(MaverickV2Executor__InvalidDataLength.selector);
        maverickV2Exposed.decodeParams(invalidParams);
    }

    function testGetTransferData() public {
        bytes memory params =
            abi.encodePacked(GHO_USDC_POOL, GHO_ADDR, USDC_ADDR);

        (
            TransferManager.TransferType transferType,
            address receiver,
            address tokenIn,
            address tokenOut,
            bool outputToRouter
        ) = maverickV2Exposed.getTransferData(params);

        assertEq(
            uint8(transferType), uint8(TransferManager.TransferType.Transfer)
        );
        assertEq(receiver, GHO_USDC_POOL);
        assertEq(tokenIn, GHO_ADDR);
        assertEq(tokenOut, USDC_ADDR);
        assertEq(outputToRouter, false);
    }

    function testSwap() public {
        uint256 amountIn = 10e18;
        bytes memory protocolData =
            abi.encodePacked(GHO_USDC_POOL, GHO_ADDR, USDC_ADDR);

        deal(GHO_ADDR, address(maverickV2Exposed), amountIn);
        uint256 balanceBefore = USDC.balanceOf(BOB);

        // transfer funds into the pool - this is taken cared of by the Dispatcher now
        vm.prank(address(maverickV2Exposed));
        IERC20(GHO_ADDR).transfer(GHO_USDC_POOL, amountIn);

        maverickV2Exposed.swap(amountIn, protocolData, BOB);

        uint256 balanceAfter = USDC.balanceOf(BOB);
        assertGt(balanceAfter, balanceBefore);
    }

    function testDecodeIntegration() public view {
        // Generated by the SwapEncoder - test_encode_maverick_v2
        bytes memory protocolData =
            loadCallDataFromFile("test_encode_maverick_v2");

        (address target, IERC20 tokenIn) =
            maverickV2Exposed.decodeParams(protocolData);

        assertEq(address(tokenIn), GHO_ADDR);
        assertEq(target, GHO_USDC_POOL);
    }

    function testSwapIntegration() public {
        // Generated by the SwapEncoder - test_encode_maverick_v2
        bytes memory protocolData =
            loadCallDataFromFile("test_encode_maverick_v2");

        uint256 amountIn = 10 ** 18;
        deal(GHO_ADDR, address(maverickV2Exposed), amountIn);
        uint256 balanceBefore = USDC.balanceOf(BOB);

        // transfer funds into the pool - this is taken cared of by the Dispatcher now
        vm.prank(address(maverickV2Exposed));
        IERC20(GHO_ADDR).transfer(GHO_USDC_POOL, amountIn);

        maverickV2Exposed.swap(amountIn, protocolData, BOB);

        uint256 balanceAfter = USDC.balanceOf(BOB);
        assertGt(balanceAfter, balanceBefore);
    }
}

contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
    function testSingleMaverickIntegration() public {
        deal(GHO_ADDR, ALICE, 1 ether);
        uint256 balanceBefore = IERC20(USDC_ADDR).balanceOf(ALICE);

        vm.startPrank(ALICE);
        IERC20(GHO_ADDR).approve(tychoRouterAddr, type(uint256).max);

        bytes memory callData =
            loadCallDataFromFile("test_single_encoding_strategy_maverick");
        (bool success,) = tychoRouterAddr.call(callData);

        uint256 balanceAfter = IERC20(USDC_ADDR).balanceOf(ALICE);

        assertTrue(success, "Call Failed");
        assertGe(balanceAfter - balanceBefore, 999725);
        assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
    }
}
