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

/**
 * @title MirrorRegistry
 * @notice Stores Merkle commitment roots for DBIS/Alltra transaction mirroring to public mainnets.
 * @dev submitCommit(chainId, startBlock, endBlock, root, uri, ts) + event CommitSubmitted.
 *      Publisher allowlist and optional rate limiting.
 */
contract MirrorRegistry {
    address public admin;
    bool public paused;

    mapping(address => bool) public publishers;
    uint256 public commitsCount;
    uint256 public constant MAX_COMMITS_PER_BLOCK = 5;
    uint256 public commitsThisBlock;
    uint256 public lastBlock;

    event CommitSubmitted(
        uint256 indexed sourceChainId,
        uint64 startBlock,
        uint64 endBlock,
        bytes32 root,
        string uri,
        uint64 ts
    );
    event AdminChanged(address indexed newAdmin);
    event Paused();
    event Unpaused();
    event PublisherSet(address indexed publisher, bool allowed);

    modifier onlyAdmin() {
        require(msg.sender == admin, "only admin");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "paused");
        _;
    }

    modifier onlyPublisher() {
        require(publishers[msg.sender], "not publisher");
        _;
    }

    constructor(address _admin) {
        require(_admin != address(0), "zero admin");
        admin = _admin;
        publishers[_admin] = true;
    }

    function setAdmin(address newAdmin) external onlyAdmin {
        require(newAdmin != address(0), "zero admin");
        admin = newAdmin;
        emit AdminChanged(newAdmin);
    }

    function pause() external onlyAdmin {
        paused = true;
        emit Paused();
    }

    function unpause() external onlyAdmin {
        paused = false;
        emit Unpaused();
    }

    function setPublisher(address publisher, bool allowed) external onlyAdmin {
        publishers[publisher] = allowed;
        emit PublisherSet(publisher, allowed);
    }

    /**
     * @notice Submit a Merkle commitment for a block range on source chain (138 or 651940).
     * @param sourceChainId Chain ID (e.g. 138, 651940).
     * @param startBlock Start block (inclusive).
     * @param endBlock End block (inclusive).
     * @param root Merkle root of leaves (tx_hash, block_number, receipt_root, payload_hash, sal_hash).
     * @param uri URI to leaf set (e.g. S3 or API).
     * @param ts Timestamp of commit (epoch seconds).
     */
    function submitCommit(
        uint256 sourceChainId,
        uint64 startBlock,
        uint64 endBlock,
        bytes32 root,
        string calldata uri,
        uint64 ts
    ) external onlyPublisher whenNotPaused {
        require(startBlock <= endBlock, "invalid range");
        require(root != bytes32(0), "zero root");

        if (block.number != lastBlock) {
            lastBlock = block.number;
            commitsThisBlock = 0;
        }
        require(commitsThisBlock < MAX_COMMITS_PER_BLOCK, "rate limit");
        commitsThisBlock++;
        commitsCount++;

        emit CommitSubmitted(sourceChainId, startBlock, endBlock, root, uri, ts);
    }
}
