// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

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

contract MaverickV2ExecutorExposed is MaverickV2Executor {
    constructor(address _factory, address _permit2)
        MaverickV2Executor(_factory, _permit2)
    {}

    function decodeParams(bytes calldata data)
        external
        pure
        returns (
            IERC20 tokenIn,
            address target,
            address receiver,
            RestrictTransferFrom.TransferType transferType
        )
    {
        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(MAVERICK_V2_FACTORY, PERMIT2_ADDRESS);
    }

    function testDecodeParams() public view {
        bytes memory params = abi.encodePacked(
            GHO_ADDR,
            GHO_USDC_POOL,
            address(2),
            RestrictTransferFrom.TransferType.Transfer
        );

        (
            IERC20 tokenIn,
            address target,
            address receiver,
            RestrictTransferFrom.TransferType transferType
        ) = maverickV2Exposed.decodeParams(params);

        assertEq(address(tokenIn), GHO_ADDR);
        assertEq(target, GHO_USDC_POOL);
        assertEq(receiver, address(2));
        assertEq(
            uint8(transferType),
            uint8(RestrictTransferFrom.TransferType.Transfer)
        );
    }

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

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

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

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

        uint256 amountOut = maverickV2Exposed.swap(amountIn, protocolData);

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

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

        (
            IERC20 tokenIn,
            address pool,
            address receiver,
            RestrictTransferFrom.TransferType transferType
        ) = maverickV2Exposed.decodeParams(protocolData);

        assertEq(address(tokenIn), GHO_ADDR);
        assertEq(pool, GHO_USDC_POOL);
        assertEq(receiver, BOB);
        assertEq(
            uint8(transferType),
            uint8(RestrictTransferFrom.TransferType.Transfer)
        );
    }

    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);

        uint256 amountOut = maverickV2Exposed.swap(amountIn, protocolData);

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