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

import "forge-std/Script.sol";
import {GrcDiamond} from "@gru/GrcDiamond.sol";
import {IDiamondCut} from "@gru/interfaces/IDiamondCut.sol";
import {PauseFacet} from "@gru/facets/PauseFacet.sol";
import {AccessFacet} from "@gru/facets/AccessFacet.sol";
import {M00MainnetBridgeFacet} from "../../contracts/m00-diamond/facets/M00MainnetBridgeFacet.sol";
import {M00DiamondInit} from "../../contracts/m00-diamond/M00DiamondInit.sol";
import {RWAInstrumentFacet} from "../../contracts/rwa/diamond/facets/RWAInstrumentFacet.sol";
import {RWADocumentFacet} from "../../contracts/rwa/diamond/facets/RWADocumentFacet.sol";
import {RWAStandardsRegistryFacet} from "../../contracts/rwa/diamond/facets/RWAStandardsRegistryFacet.sol";
import {IM00MainnetBridgeFacet} from "../../contracts/m00-diamond/interfaces/IM00MainnetBridgeFacet.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";

/**
 * @title DeployM00DiamondHub138
 * @notice Deploy GRC-2535 M00 Diamond hub on Chain 138 with RWA + mainnet mirror/tether/checkpoint bridge facet.
 * @dev Paris profile mandatory. Wire env from project .env (load-project-env before forge).
 *
 * Env: PRIVATE_KEY, GOVERNANCE_CONTROLLER or OWNER
 *   CHAIN138_BATCH_EMITTER, TRANSACTION_MIRROR_ADDRESS (138 local), CHAIN138_MAINNET_CHECKPOINT_PROXY (mainnet)
 *   TRANSACTION_MIRROR_MAINNET, MAINNET_TETHER_ADDRESS
 *   RWA_TOKEN_FACTORY, RWA_TOKEN_REGISTRY
 */
contract DeployM00DiamondHub138 is Script {
    function run() external {
        uint256 pk = vm.envUint("PRIVATE_KEY");
        address deployer = vm.addr(pk);
        address governance = vm.envOr("GOVERNANCE_CONTROLLER", vm.envOr("OWNER", deployer));

        vm.startBroadcast(pk);

        // Diamond owner must be broadcaster for initial diamondCut (ErrUpgradeRole otherwise).
        GrcDiamond diamond = new GrcDiamond(deployer, "M00.1.0");
        PauseFacet pause = new PauseFacet();
        AccessFacet access = new AccessFacet();
        M00MainnetBridgeFacet bridge = new M00MainnetBridgeFacet(governance);
        RWAInstrumentFacet rwaInst = new RWAInstrumentFacet(governance);
        RWADocumentFacet rwaDoc = new RWADocumentFacet(governance);
        RWAStandardsRegistryFacet rwaStd = new RWAStandardsRegistryFacet(governance);
        M00DiamondInit init = new M00DiamondInit();

        IDiamondCut.FacetCut[] memory cut = _buildCuts(
            address(pause), address(access), address(bridge), address(rwaInst), address(rwaDoc), address(rwaStd)
        );

        IM00MainnetBridgeFacet.BridgeConfig memory cfg = IM00MainnetBridgeFacet.BridgeConfig({
            chain138BatchEmitter: vm.envOr("CHAIN138_BATCH_EMITTER", address(0)),
            chain138Mirror: vm.envOr("TRANSACTION_MIRROR_ADDRESS", address(0)),
            mainnetCheckpoint: vm.envOr("CHAIN138_MAINNET_CHECKPOINT_PROXY", address(0)),
            mainnetMirror: vm.envOr("TRANSACTION_MIRROR_MAINNET", address(0)),
            mainnetTether: vm.envOr("MAINNET_TETHER_ADDRESS", address(0)),
            rwaTokenFactory: vm.envOr("RWA_TOKEN_FACTORY", address(0)),
            rwaTokenRegistry: vm.envOr("RWA_TOKEN_REGISTRY", address(0))
        });

        diamond.diamondCut(cut, address(init), abi.encodeCall(M00DiamondInit.init, (cfg, governance)));

        console.log("M00Diamond", address(diamond));
        console.log("M00MainnetBridgeFacet", address(bridge));
        console.log("RWAInstrumentFacet", address(rwaInst));

        vm.stopBroadcast();
    }

    function _buildCuts(
        address pause,
        address access,
        address bridge,
        address rwaInst,
        address rwaDoc,
        address rwaStd
    ) internal pure returns (IDiamondCut.FacetCut[] memory cut) {
        cut = new IDiamondCut.FacetCut[](6);
        cut[0] = _add(pause, _pauseSelectors());
        cut[1] = _add(access, _accessSelectors());
        cut[2] = _add(bridge, _bridgeSelectors());
        cut[3] = _add(rwaInst, _rwaInstSelectors());
        cut[4] = _add(rwaDoc, _rwaDocSelectors());
        cut[5] = _add(rwaStd, _rwaStdSelectors());
    }

    function _add(address facet, bytes4[] memory sels)
        internal
        pure
        returns (IDiamondCut.FacetCut memory)
    {
        return IDiamondCut.FacetCut({
            facetAddress: facet,
            action: IDiamondCut.FacetCutAction.Add,
            functionSelectors: sels
        });
    }

    function _pauseSelectors() internal pure returns (bytes4[] memory s) {
        s = new bytes4[](4);
        s[0] = PauseFacet.setGlobalPause.selector;
        s[1] = PauseFacet.setFunctionPause.selector;
        s[2] = PauseFacet.isPaused.selector;
        s[3] = PauseFacet.isGlobalPaused.selector;
    }

    function _accessSelectors() internal pure returns (bytes4[] memory s) {
        s = new bytes4[](7);
        s[0] = AccessFacet.grantRoles.selector;
        s[1] = AccessFacet.revokeRoles.selector;
        s[2] = AccessFacet.hasRoles.selector;
        s[3] = AccessFacet.ROLE_UPGRADE_BIT.selector;
        s[4] = AccessFacet.ROLE_GOVERNANCE_BIT.selector;
        s[5] = AccessFacet.ROLE_INDEX_BIT.selector;
        s[6] = AccessFacet.ROLE_MONETARY_BIT.selector;
    }

    function _bridgeSelectors() internal pure returns (bytes4[] memory s) {
        s = new bytes4[](11);
        s[0] = M00MainnetBridgeFacet.wireMainnetBridge.selector;
        s[1] = M00MainnetBridgeFacet.getMainnetBridgeConfig.selector;
        s[2] = M00MainnetBridgeFacet.commitBatchOn138.selector;
        s[3] = M00MainnetBridgeFacet.sendBatchToMainnet.selector;
        s[4] = M00MainnetBridgeFacet.scheduleMirrorToMainnet.selector;
        s[5] = M00MainnetBridgeFacet.mirrorOnMainnet.selector;
        s[6] = M00MainnetBridgeFacet.anchorStateProofOnMainnet.selector;
        s[7] = M00MainnetBridgeFacet.ackInboundCheckpoint.selector;
        s[8] = IAccessControl.grantRole.selector;
        s[9] = IAccessControl.revokeRole.selector;
        s[10] = IAccessControl.hasRole.selector;
    }

    function _rwaInstSelectors() internal pure returns (bytes4[] memory s) {
        s = new bytes4[](5);
        s[0] = RWAInstrumentFacet.setIssuanceMode.selector;
        s[1] = RWAInstrumentFacet.setInstrumentIdentity.selector;
        s[2] = RWAInstrumentFacet.setTokenPointer.selector;
        s[3] = RWAInstrumentFacet.getIssuanceMode.selector;
        s[4] = RWAInstrumentFacet.getTokenPointer.selector;
    }

    function _rwaDocSelectors() internal pure returns (bytes4[] memory s) {
        s = new bytes4[](4);
        s[0] = RWADocumentFacet.anchorDocument.selector;
        s[1] = RWADocumentFacet.setPrimaryContentHash.selector;
        s[2] = RWADocumentFacet.documentCount.selector;
        s[3] = RWADocumentFacet.getDocument.selector;
    }

    function _rwaStdSelectors() internal pure returns (bytes4[] memory s) {
        s = new bytes4[](5);
        s[0] = RWAStandardsRegistryFacet.enableStandard.selector;
        s[1] = RWAStandardsRegistryFacet.disableStandard.selector;
        s[2] = RWAStandardsRegistryFacet.bindAssetStandardFacet.selector;
        s[3] = RWAStandardsRegistryFacet.isStandardEnabled.selector;
        s[4] = RWAStandardsRegistryFacet.assetStandardFacet.selector;
    }
}
