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

import "../stablecoins/IHypMailbox.sol";

/**
 * @title MockWarpMailboxV3 — faithful stand-in for AereMailboxV3's external
 *        surface used by AereWarpRouteV3 tests. Not for mainnet.
 *
 * @dev  Mirrors the real AereMailboxV3 behaviour the router depends on:
 *         - quoteDispatch(uint32,bytes32,bytes) returns a configurable fee.
 *         - dispatch(...) requires msg.value >= fee, keeps the fee, refunds any
 *           excess to msg.sender (exactly like AereMailboxV3._dispatch), records
 *           the call, returns a v3-style messageId.
 *         - mockDeliver(...) lets a test drive an inbound handle() so the
 *           recipient sees msg.sender == this mailbox (like process()).
 */
contract MockWarpMailboxV3 is IHypMailbox {
    uint32 public override localDomain;
    uint256 public feeAmount = 0.001 ether;

    // Recorded from the last dispatch — asserted by tests.
    uint32 public lastDestination;
    bytes32 public lastRecipient;
    bytes public lastBody;
    uint256 public lastFeePaid;
    bytes32 public lastMessageId;
    uint32 public dispatchNonce;

    event Dispatched(uint32 indexed destination, bytes32 indexed recipient, bytes body, uint256 feePaid);

    constructor(uint32 _localDomain) {
        localDomain = _localDomain;
    }

    function setFee(uint256 f) external {
        feeAmount = f;
    }

    function quoteDispatch(uint32, bytes32, bytes calldata) external view override returns (uint256) {
        return feeAmount;
    }

    function dispatch(
        uint32 destination,
        bytes32 recipient,
        bytes calldata body
    ) external payable override returns (bytes32) {
        require(msg.value >= feeAmount, "MockWarpMailboxV3: insufficient fee");
        lastDestination = destination;
        lastRecipient = recipient;
        lastBody = body;
        lastFeePaid = feeAmount;

        bytes32 id = keccak256(abi.encodePacked(localDomain, dispatchNonce, destination, recipient, body));
        dispatchNonce += 1;
        lastMessageId = id;
        emit Dispatched(destination, recipient, body, feeAmount);

        uint256 excess = msg.value - feeAmount;
        if (excess > 0) {
            (bool ok, ) = msg.sender.call{value: excess}("");
            require(ok, "MockWarpMailboxV3: refund failed");
        }
        return id;
    }

    /// @notice Test-only inbound delivery: call recipient.handle so it sees
    ///         msg.sender == this mailbox.
    function mockDeliver(
        address recipient,
        uint32 origin,
        bytes32 sender,
        bytes calldata body
    ) external {
        IHypMessageRecipient(recipient).handle(origin, sender, body);
    }

    receive() external payable {}
}

/**
 * @title MockWarpIGPV3 — minimal AereIGPV3 quoting surface for the router's
 *        quoteGasPayment() transparency view test.
 */
contract MockWarpIGPV3 {
    function quoteGasPayment(uint32, uint256 gasLimit) external pure returns (uint256) {
        // Deterministic: 1 gwei of AERE per unit of destination gas.
        return gasLimit * 1e9;
    }
}
