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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "../interfaces/IRegulatedAssetMetadata.sol";

/**
 * @title CompliantWrappedToken
 * @notice ERC-20 wrapper for bridged compliant tokens (cWUSDT, cWUSDC, etc.) on public chains.
 * @dev Only MINTER_ROLE can mint (e.g. CCIP receiver/bridge); BURNER_ROLE can burn (bridge-back).
 *      Admin can grant/revoke roles. Used for cW* representation of Chain 138 compliant tokens.
 */
contract CompliantWrappedToken is ERC20, AccessControl, IRegulatedAssetMetadata {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
    bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
    bytes32 public constant JURISDICTION_ADMIN_ROLE = keccak256("JURISDICTION_ADMIN_ROLE");
    bytes32 public constant REGULATOR_ROLE = keccak256("REGULATOR_ROLE");
    bytes32 public constant SUPERVISOR_ROLE = keccak256("SUPERVISOR_ROLE");
    bytes32 public constant EMERGENCY_ADMIN_ROLE = keccak256("EMERGENCY_ADMIN_ROLE");

    uint8 private immutable _decimalsStorage;
    bool public operationalRolesFrozen;
    address public governanceController;
    bytes32 public immutable assetId;
    bytes32 public immutable assetVersionId;
    bytes32 public governanceProfileId;
    bytes32 public supervisionProfileId;
    bytes32 public storageNamespace;
    string public primaryJurisdiction;
    string private _regulatoryDisclosureURI;
    string private _reportingURI;
    address public canonicalUnderlyingAsset;
    bool public supervisionRequired;
    bool public governmentApprovalRequired;
    uint256 public minimumUpgradeNoticePeriod;

    event OperationalRolesFrozen(address indexed caller);
    event GovernanceProfileUpdated(bytes32 governanceProfileId);
    event SupervisionProfileUpdated(bytes32 supervisionProfileId);
    event StorageNamespaceUpdated(bytes32 storageNamespace);
    event PrimaryJurisdictionUpdated(string jurisdiction);
    event RegulatoryDisclosureURIUpdated(string disclosureURI);
    event ReportingURIUpdated(string reportingURI);
    event CanonicalUnderlyingAssetUpdated(address canonicalUnderlyingAsset);
    event SupervisionConfigurationUpdated(
        bool supervisionRequired,
        bool governmentApprovalRequired,
        uint256 minimumUpgradeNoticePeriod
    );
    event RegulatoryApprovalRecorded(bytes32 indexed approvalId, string actionType, bytes32 referenceHash);
    event SupervisoryNoticeRecorded(bytes32 indexed noticeId, string category, string uri);

    error OperationalRolesAreFrozen();
    error GovernanceControllerOnly(address caller);
    error GovernanceControllerNotConfigured();
    error ZeroGovernanceController();

    modifier onlyGovernanceExecution() {
        if (governanceController == address(0)) revert GovernanceControllerNotConfigured();
        if (msg.sender != governanceController) revert GovernanceControllerOnly(msg.sender);
        _;
    }

    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        address admin_
    ) ERC20(name_, symbol_) {
        _decimalsStorage = decimals_;
        assetId = keccak256(bytes(string.concat("GRU:", symbol_)));
        assetVersionId = keccak256(bytes(string.concat("GRU:", symbol_, ":transport")));
        governanceProfileId = keccak256(bytes(string.concat("GRU:GOV:", symbol_, ":transport")));
        supervisionProfileId = keccak256(bytes(string.concat("GRU:SUP:", symbol_, ":transport")));
        storageNamespace = keccak256(bytes(string.concat("gru.storage.transport.", symbol_)));
        primaryJurisdiction = "Destination Public Network";
        supervisionRequired = true;
        minimumUpgradeNoticePeriod = 7 days;
        _grantRole(DEFAULT_ADMIN_ROLE, admin_);
        _grantRole(MINTER_ROLE, admin_);
        _grantRole(BURNER_ROLE, admin_);
        _grantRole(GOVERNANCE_ROLE, admin_);
        _grantRole(JURISDICTION_ADMIN_ROLE, admin_);
        _grantRole(REGULATOR_ROLE, admin_);
        _grantRole(SUPERVISOR_ROLE, admin_);
        _grantRole(EMERGENCY_ADMIN_ROLE, admin_);
    }

    function decimals() public view override returns (uint8) {
        return _decimalsStorage;
    }

    function wrappedTransport() external pure returns (bool) {
        return true;
    }

    function regulatoryDisclosureURI() external view returns (string memory) {
        return _regulatoryDisclosureURI;
    }

    function reportingURI() external view returns (string memory) {
        return _reportingURI;
    }

    /**
     * @notice Permanently freeze future MINTER_ROLE / BURNER_ROLE changes.
     * @dev Existing bridge roles keep working; only admin churn is disabled.
     */
    function freezeOperationalRoles() external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (operationalRolesFrozen) {
            return;
        }
        operationalRolesFrozen = true;
        emit OperationalRolesFrozen(msg.sender);
    }

    function grantRole(bytes32 role, address account) public override onlyRole(getRoleAdmin(role)) {
        _revertIfFrozenOperationalRole(role);
        super.grantRole(role, account);
    }

    function revokeRole(bytes32 role, address account) public override onlyRole(getRoleAdmin(role)) {
        _revertIfFrozenOperationalRole(role);
        super.revokeRole(role, account);
    }

    function setGovernanceController(address governanceController_) external onlyRole(EMERGENCY_ADMIN_ROLE) {
        if (governanceController_ == address(0)) revert ZeroGovernanceController();
        governanceController = governanceController_;
    }

    function setGovernanceProfileId(bytes32 governanceProfileId_) external onlyGovernanceExecution {
        _setGovernanceProfileIdValue(governanceProfileId_);
    }

    function setSupervisionProfileId(bytes32 supervisionProfileId_) external onlyGovernanceExecution {
        _setSupervisionProfileIdValue(supervisionProfileId_);
    }

    function setStorageNamespace(bytes32 storageNamespace_) external onlyGovernanceExecution {
        _setStorageNamespaceValue(storageNamespace_);
    }

    function setPrimaryJurisdiction(string calldata jurisdiction_) external onlyGovernanceExecution {
        _setPrimaryJurisdictionValue(jurisdiction_);
    }

    function setRegulatoryDisclosureURI(string calldata disclosureURI_) external onlyGovernanceExecution {
        _setRegulatoryDisclosureURIValue(disclosureURI_);
    }

    function setReportingURI(string calldata reportingURI_) external onlyGovernanceExecution {
        _setReportingURIValue(reportingURI_);
    }

    function setCanonicalUnderlyingAsset(address canonicalUnderlyingAsset_) external onlyGovernanceExecution {
        _setCanonicalUnderlyingAssetValue(canonicalUnderlyingAsset_);
    }

    function setSupervisionConfiguration(
        bool supervisionRequired_,
        bool governmentApprovalRequired_,
        uint256 minimumUpgradeNoticePeriod_
    ) external onlyGovernanceExecution {
        _setSupervisionConfigurationValue(
            supervisionRequired_,
            governmentApprovalRequired_,
            minimumUpgradeNoticePeriod_
        );
    }

    function emergencySetGovernanceMetadata(
        bytes32 governanceProfileId_,
        bytes32 supervisionProfileId_,
        bytes32 storageNamespace_,
        string calldata jurisdiction_,
        address canonicalUnderlyingAsset_,
        bool supervisionRequired_,
        bool governmentApprovalRequired_,
        uint256 minimumUpgradeNoticePeriod_
    ) external onlyRole(EMERGENCY_ADMIN_ROLE) {
        _setGovernanceProfileIdValue(governanceProfileId_);
        _setSupervisionProfileIdValue(supervisionProfileId_);
        _setStorageNamespaceValue(storageNamespace_);
        _setPrimaryJurisdictionValue(jurisdiction_);
        _setCanonicalUnderlyingAssetValue(canonicalUnderlyingAsset_);
        _setSupervisionConfigurationValue(
            supervisionRequired_,
            governmentApprovalRequired_,
            minimumUpgradeNoticePeriod_
        );
    }

    function emergencySetDisclosureMetadata(
        string calldata disclosureURI_,
        string calldata reportingURI_
    ) external onlyRole(EMERGENCY_ADMIN_ROLE) {
        _setRegulatoryDisclosureURIValue(disclosureURI_);
        _setReportingURIValue(reportingURI_);
    }

    function recordRegulatoryApproval(
        bytes32 approvalId,
        string calldata actionType,
        bytes32 referenceHash
    ) external onlyRole(REGULATOR_ROLE) {
        emit RegulatoryApprovalRecorded(approvalId, actionType, referenceHash);
    }

    function recordSupervisoryNotice(
        bytes32 noticeId,
        string calldata category,
        string calldata uri
    ) external onlyRole(SUPERVISOR_ROLE) {
        emit SupervisoryNoticeRecorded(noticeId, category, uri);
    }

    /// @notice Mint to account (only MINTER_ROLE, e.g. bridge receiver).
    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    /// @notice Burn from account (only BURNER_ROLE, e.g. bridge for burn-on-exit).
    function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) {
        _burn(from, amount);
    }

    /// @notice Burn from account (only BURNER_ROLE). Enables TwoWayTokenBridgeL2 and other bridges that call burnFrom(user, amount) for outbound.
    function burnFrom(address from, uint256 amount) external onlyRole(BURNER_ROLE) {
        _burn(from, amount);
    }

    function _revertIfFrozenOperationalRole(bytes32 role) internal view {
        if (operationalRolesFrozen && (role == MINTER_ROLE || role == BURNER_ROLE)) {
            revert OperationalRolesAreFrozen();
        }
    }

    function _setGovernanceProfileIdValue(bytes32 governanceProfileId_) internal {
        governanceProfileId = governanceProfileId_;
        emit GovernanceProfileUpdated(governanceProfileId_);
    }

    function _setSupervisionProfileIdValue(bytes32 supervisionProfileId_) internal {
        supervisionProfileId = supervisionProfileId_;
        emit SupervisionProfileUpdated(supervisionProfileId_);
    }

    function _setStorageNamespaceValue(bytes32 storageNamespace_) internal {
        storageNamespace = storageNamespace_;
        emit StorageNamespaceUpdated(storageNamespace_);
    }

    function _setPrimaryJurisdictionValue(string memory jurisdiction_) internal {
        primaryJurisdiction = jurisdiction_;
        emit PrimaryJurisdictionUpdated(jurisdiction_);
    }

    function _setRegulatoryDisclosureURIValue(string memory disclosureURI_) internal {
        _regulatoryDisclosureURI = disclosureURI_;
        emit RegulatoryDisclosureURIUpdated(disclosureURI_);
    }

    function _setReportingURIValue(string memory reportingURI_) internal {
        _reportingURI = reportingURI_;
        emit ReportingURIUpdated(reportingURI_);
    }

    function _setCanonicalUnderlyingAssetValue(address canonicalUnderlyingAsset_) internal {
        canonicalUnderlyingAsset = canonicalUnderlyingAsset_;
        emit CanonicalUnderlyingAssetUpdated(canonicalUnderlyingAsset_);
    }

    function _setSupervisionConfigurationValue(
        bool supervisionRequired_,
        bool governmentApprovalRequired_,
        uint256 minimumUpgradeNoticePeriod_
    ) internal {
        supervisionRequired = supervisionRequired_;
        governmentApprovalRequired = governmentApprovalRequired_;
        minimumUpgradeNoticePeriod = minimumUpgradeNoticePeriod_;
        emit SupervisionConfigurationUpdated(
            supervisionRequired_,
            governmentApprovalRequired_,
            minimumUpgradeNoticePeriod_
        );
    }
}
