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

import {Greeter} from "../../../tests/Greeter.sol";

import {MultiSend} from "../../../vendor/MultiSend.sol";
import {Test} from "forge-std/Test.sol";

contract L2EPTest is Test {
  /// Helper variable(s)
  address internal s_strangerAddr = vm.addr(0x1);
  address internal s_l1OwnerAddr = vm.addr(0x2);
  address internal s_eoaValidator = vm.addr(0x3);
  address internal s_deployerAddr = vm.addr(0x4);

  /// @param expectedGasUsage - the expected gas usage
  /// @param startGasUsage - the gas usage before the code of interest is run
  /// @param finalGasUsage - the gas usage after the code of interest is run
  /// @param deviation - the amount of gas that the actual usage is allowed to deviate by (e.g. (expectedGas - deviation) <= actualGasUsage <= (expectedGas + deviation))
  function assertGasUsageIsCloseTo(
    uint256 expectedGasUsage,
    uint256 startGasUsage,
    uint256 finalGasUsage,
    uint256 deviation
  ) public {
    uint256 gasUsed = (startGasUsage - finalGasUsage) * tx.gasprice;
    assertLe(gasUsed, expectedGasUsage + deviation);
    assertGe(gasUsed, expectedGasUsage - deviation);
  }

  /// @param selector - the function selector
  /// @param greeterAddr - the address of the Greeter contract
  /// @param message - the new greeting message, which will be passed as an argument to Greeter#setGreeting
  /// @return a 2-layer encoding such that decoding the first layer provides the CrossDomainForwarder#forward
  ///         function selector and the corresponding arguments to the forward function, and decoding the
  ///         second layer provides the Greeter#setGreeting function selector and the corresponding
  ///         arguments to the set greeting function (which in this case is the input message)
  function encodeCrossDomainSetGreetingMsg(
    bytes4 selector,
    address greeterAddr,
    string memory message
  ) public pure returns (bytes memory) {
    return abi.encodeWithSelector(selector, greeterAddr, abi.encodeWithSelector(Greeter.setGreeting.selector, message));
  }

  /// @param selector - the function selector
  /// @param multiSendAddr - the address of the MultiSend contract
  /// @param encodedTxs - an encoded list of transactions (e.g. abi.encodePacked(encodeMultiSendTx("some data"), ...))
  /// @return a 2-layer encoding such that decoding the first layer provides the CrossDomainGoverner#forwardDelegate
  ///         function selector and the corresponding arguments to the forwardDelegate function, and decoding the
  ///         second layer provides the MultiSend#multiSend function selector and the corresponding
  ///         arguments to the multiSend function (which in this case is the input encodedTxs)
  function encodeCrossDomainMultiSendMsg(
    bytes4 selector,
    address multiSendAddr,
    bytes memory encodedTxs
  ) public pure returns (bytes memory) {
    return
      abi.encodeWithSelector(selector, multiSendAddr, abi.encodeWithSelector(MultiSend.multiSend.selector, encodedTxs));
  }

  /// @param greeterAddr - the address of the greeter contract
  /// @param data - the transaction data string
  /// @return an encoded transaction structured as specified in the MultiSend#multiSend comments
  function encodeMultiSendTx(address greeterAddr, bytes memory data) public pure returns (bytes memory) {
    bytes memory txData = abi.encodeWithSelector(Greeter.setGreeting.selector, data);
    return
      abi.encodePacked(
        uint8(0), // operation
        greeterAddr, // to
        uint256(0), // value
        uint256(txData.length), // data length
        txData // data as bytes
      );
  }

  /// @param l1Address - Address on L1
  /// @return an Arbitrum L2 address
  function toArbitrumL2AliasAddress(address l1Address) public pure returns (address) {
    return address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111));
  }
}
