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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IMintable {
    function mint(address to, uint256 amount) external;
}

interface ICWMultiTokenBridgeL1 {
    function lockAndSend(
        address canonicalToken,
        uint64 destinationChainSelector,
        address recipient,
        uint256 amount
    ) external payable returns (bytes32);

    function calculateFee(
        address canonicalToken,
        uint64 destinationChainSelector,
        address recipient,
        uint256 amount
    ) external view returns (uint256);
}

/**
 * @notice Batch mint + bridge for c* mirror mesh prep on Chain 138.
 * @dev Called by the operator EOA. Grant IMintable.MINTER_ROLE to this contract on each c* token.
 *      Multicall3 cannot replace this: subcalls would see msg.sender = Multicall3, not the operator.
 */
contract CWMirrorMeshBatch {
    using SafeERC20 for IERC20;

    address public immutable bridge;
    address public immutable link;

    event MintMany(address indexed operator, uint256 callCount);
    event BridgeMany(address indexed operator, address indexed token, uint256 chunkCount, uint256 totalAmount);

    constructor(address bridge_, address link_) {
        require(bridge_ != address(0), "zero bridge");
        require(link_ != address(0), "zero link");
        bridge = bridge_;
        link = link_;
    }

    /// @dev Mint each amount to msg.sender (operator) in one transaction.
    function mintMany(address[] calldata tokens, uint256[] calldata amounts) external {
        uint256 n = tokens.length;
        require(n == amounts.length, "length mismatch");
        for (uint256 i; i < n; ) {
            IMintable(tokens[i]).mint(msg.sender, amounts[i]);
            unchecked {
                ++i;
            }
        }
        emit MintMany(msg.sender, n);
    }

    /// @dev Mint one token repeatedly to msg.sender (same token, many chunks) in one transaction.
    function mintChunks(address token, uint256[] calldata amounts) external {
        uint256 n = amounts.length;
        for (uint256 i; i < n; ) {
            IMintable(token).mint(msg.sender, amounts[i]);
            unchecked {
                ++i;
            }
        }
        emit MintMany(msg.sender, n);
    }

    /**
     * @dev Bridge one canonical token in multiple lockAndSend chunks from msg.sender.
     *      Pulls total token + total LINK fee upfront, then loops lockAndSend.
     */
    function bridgeTokenChunks(
        address canonicalToken,
        uint64 destinationChainSelector,
        address recipient,
        uint256[] calldata amounts
    ) external payable {
        uint256 n = amounts.length;
        require(n > 0, "empty amounts");

        uint256 totalAmount;
        uint256 totalFee;
        for (uint256 i; i < n; ) {
            totalAmount += amounts[i];
            totalFee += ICWMultiTokenBridgeL1(bridge).calculateFee(
                canonicalToken,
                destinationChainSelector,
                recipient,
                amounts[i]
            );
            unchecked {
                ++i;
            }
        }

        IERC20(canonicalToken).safeTransferFrom(msg.sender, address(this), totalAmount);
        IERC20(link).safeTransferFrom(msg.sender, address(this), totalFee);

        for (uint256 i; i < n; ) {
            uint256 amount = amounts[i];
            uint256 fee = ICWMultiTokenBridgeL1(bridge).calculateFee(
                canonicalToken,
                destinationChainSelector,
                recipient,
                amount
            );

            IERC20(canonicalToken).forceApprove(bridge, amount);
            IERC20(link).forceApprove(bridge, fee);

            ICWMultiTokenBridgeL1(bridge).lockAndSend(
                canonicalToken,
                destinationChainSelector,
                recipient,
                amount
            );

            unchecked {
                ++i;
            }
        }

        emit BridgeMany(msg.sender, canonicalToken, n, totalAmount);
    }
}
