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

import "./IHypMailbox.sol";

/**
 * @title MockHypMailbox — in-memory Mailbox used by the warp-route tests
 * @notice Simulates Hyperlane's interchain dispatch/handle flow inside a
 *         single Hardhat test runner. Two mock-mailbox instances can be
 *         paired (setSister) so that a dispatch on instance A triggers a
 *         handle() call on instance B's enrolled recipient.
 *
 * @dev    Only used in tests, never deployed to mainnet. Exposes:
 *           - quoteDispatch  → returns a configurable flat fee
 *           - dispatch       → forwards body to the sister Mailbox's
 *                              recipient.handle() with origin = this.localDomain
 *           - localDomain    → configurable per instance
 */
contract MockHypMailbox is IHypMailbox {
    uint32 public override localDomain;
    uint256 public fee = 0.001 ether;

    MockHypMailbox public sister;
    address public localRecipient;   // The application contract on THIS Mailbox's chain that receives inbound messages.

    event Dispatched(uint32 indexed destination, bytes32 indexed recipient, bytes body);
    event Handled(uint32 indexed origin, bytes32 indexed sender, bytes body);

    constructor(uint32 _localDomain) {
        localDomain = _localDomain;
    }

    /// @notice Pair the two MockHypMailbox instances.
    /// @param _sister          The OTHER Mailbox (on the other "chain").
    /// @param _localRecipient  The application contract on OUR side that
    ///                         should receive inbound .handle() calls.
    function setSister(MockHypMailbox _sister, address _localRecipient) external {
        sister = _sister;
        localRecipient = _localRecipient;
    }

    function setFee(uint256 _fee) external { fee = _fee; }

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

    function dispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata messageBody
    ) external payable override returns (bytes32 messageId) {
        require(msg.value >= fee, "MockMailbox: insufficient fee");
        emit Dispatched(destinationDomain, recipientAddress, messageBody);

        messageId = keccak256(abi.encode(localDomain, destinationDomain, recipientAddress, messageBody, block.timestamp));

        // Delivery: msg.sender of dispatch is the application on OUR chain;
        // we pass its bytes32-padded address as the `sender` field that the
        // sister's recipient will check against its enrolled routers. The
        // sister Mailbox actually performs the call so that recipient.handle()
        // sees msg.sender == sister Mailbox (matching production behaviour).
        if (address(sister) != address(0)) {
            bytes32 ourSenderB32 = bytes32(uint256(uint160(msg.sender)));
            sister.deliverInbound(localDomain, ourSenderB32, messageBody);
        }
    }

    /// @dev Internal-cross-mailbox hook. Called by the SOURCE Mailbox; we
    ///      forward to our locally-configured recipient so it sees
    ///      msg.sender == us (the destination Mailbox), per production
    ///      Hyperlane semantics.
    function deliverInbound(uint32 origin, bytes32 sender, bytes calldata body) external {
        require(msg.sender == address(sister), "MockMailbox: not from sister");
        require(localRecipient != address(0), "MockMailbox: no local recipient");
        IHypMessageRecipient(localRecipient).handle(origin, sender, body);
        emit Handled(origin, sender, body);
    }
}
