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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "../M00BridgeStorage.sol";
import "../interfaces/IM00MainnetBridgeFacet.sol";
import "../interfaces/IChain138BatchEmitterBridge.sol";
import "../interfaces/ILegacyMainnetMirror.sol";

/**
 * @title M00MainnetBridgeFacet
 * @notice ERC-2535 facet: two-way Chain 138 ↔ Mainnet attestation path.
 * @dev Outbound: Chain138BatchEmitter (CCIP) + optional direct v1 mirror/tether relay on mainnet.
 *      Inbound: ackInboundCheckpoint after CCIP delivery (relayer/emitter calls on 138).
 *      Full tx mirror on mainnet is executed by operator/aggregator with MAINNET mirror admin — hub schedules + correlates.
 */
contract M00MainnetBridgeFacet is AccessControl, IM00MainnetBridgeFacet {
    bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");
    bytes32 public constant MIRROR_RELAYER_ROLE = keccak256("MIRROR_RELAYER_ROLE");

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(BRIDGE_OPERATOR_ROLE, admin);
        _grantRole(MIRROR_RELAYER_ROLE, admin);
    }

    function wireMainnetBridge(BridgeConfig calldata cfg) external onlyRole(DEFAULT_ADMIN_ROLE) {
        M00BridgeStorage.Layout storage l = M00BridgeStorage.layout();
        l.chain138BatchEmitter = cfg.chain138BatchEmitter;
        l.chain138Mirror = cfg.chain138Mirror;
        l.mainnetCheckpoint = cfg.mainnetCheckpoint;
        l.mainnetMirror = cfg.mainnetMirror;
        l.mainnetTether = cfg.mainnetTether;
        l.rwaTokenFactory = cfg.rwaTokenFactory;
        l.rwaTokenRegistry = cfg.rwaTokenRegistry;
        emit MainnetBridgeWired(cfg);
    }

    function getMainnetBridgeConfig() external view returns (BridgeConfig memory) {
        M00BridgeStorage.Layout storage l = M00BridgeStorage.layout();
        return BridgeConfig({
            chain138BatchEmitter: l.chain138BatchEmitter,
            chain138Mirror: l.chain138Mirror,
            mainnetCheckpoint: l.mainnetCheckpoint,
            mainnetMirror: l.mainnetMirror,
            mainnetTether: l.mainnetTether,
            rwaTokenFactory: l.rwaTokenFactory,
            rwaTokenRegistry: l.rwaTokenRegistry
        });
    }

    function commitBatchOn138(
        uint64 batchId,
        bytes32 paymentsRoot,
        uint256 checkpointBlock,
        uint256 startBlock,
        uint16 txCount,
        bytes32[] calldata txHashes
    ) external onlyRole(BRIDGE_OPERATOR_ROLE) {
        address emitter = M00BridgeStorage.layout().chain138BatchEmitter;
        require(emitter != address(0), "M00Bridge: emitter");
        IChain138BatchEmitterBridge(emitter).commitBatch(
            batchId, paymentsRoot, checkpointBlock, startBlock, txCount, txHashes
        );
        M00BridgeStorage.layout().lastCommittedBatchId = batchId;
        M00BridgeStorage.layout().lastAnchoredBlock138 = checkpointBlock;
        emit BatchCommittedOnHub(batchId, paymentsRoot, checkpointBlock);
    }

    function sendBatchToMainnet(uint64 batchId, bytes calldata encodedCheckpointPayload, uint256 linkFee)
        external
        onlyRole(BRIDGE_OPERATOR_ROLE)
        returns (bytes32 ccipMessageId)
    {
        address emitter = M00BridgeStorage.layout().chain138BatchEmitter;
        require(emitter != address(0), "M00Bridge: emitter");
        return IChain138BatchEmitterBridge(emitter).sendBatchToMainnet(batchId, encodedCheckpointPayload, linkFee);
    }

    /// @notice Record mirror intent; relayer with MIRROR_RELAYER_ROLE calls mirrorOnMainnet on mainnet.
    function scheduleMirrorToMainnet(
        bytes32 txHash,
        address from,
        address to,
        uint256 value,
        uint256 blockNumber,
        uint256 blockTimestamp,
        uint256 gasUsed,
        bool success,
        bytes calldata data
    ) external onlyRole(BRIDGE_OPERATOR_ROLE) {
        txHash;
        from;
        to;
        value;
        blockTimestamp;
        gasUsed;
        success;
        data;
        emit OutboundMirrorScheduled(txHash, blockNumber);
    }

    /// @notice Relayer executes v1 TransactionMirror.mirrorTransaction on Ethereum mainnet (separate chain).
    function mirrorOnMainnet(
        bytes32 txHash,
        address from,
        address to,
        uint256 value,
        uint256 blockNumber,
        uint256 blockTimestamp,
        uint256 gasUsed,
        bool success,
        bytes calldata data
    ) external onlyRole(MIRROR_RELAYER_ROLE) {
        address mirror = M00BridgeStorage.layout().mainnetMirror;
        require(mirror != address(0), "M00Bridge: mirror");
        ILegacyMainnetMirror(mirror).mirrorTransaction(
            txHash, from, to, value, blockNumber, blockTimestamp, gasUsed, success, data
        );
    }

    function anchorStateProofOnMainnet(
        uint256 blockNumber,
        bytes32 blockHash,
        bytes32 stateRoot,
        bytes32 previousBlockHash,
        uint256 timestamp,
        bytes calldata signatures,
        uint256 validatorCount
    ) external onlyRole(MIRROR_RELAYER_ROLE) {
        address tether = M00BridgeStorage.layout().mainnetTether;
        require(tether != address(0), "M00Bridge: tether");
        ILegacyMainnetTether(tether).anchorStateProof(
            blockNumber, blockHash, stateRoot, previousBlockHash, timestamp, signatures, validatorCount
        );
        M00BridgeStorage.layout().lastAnchoredBlock138 = blockNumber;
    }

    function ackInboundCheckpoint(uint64 batchId, bytes32 paymentsRoot)
        external
        onlyRole(BRIDGE_OPERATOR_ROLE)
    {
        emit InboundCheckpointAck(batchId, paymentsRoot);
    }
}
