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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/Nonces.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "../compliance/LegallyCompliantBaseV2.sol";
import "./interfaces/ICompliantFiatTokenV2.sol";

/**
 * @title CompliantFiatTokenV2
 * @notice Canonical GRU c* V2 money token with permit, authorization transfers, explicit audit attribution,
 *         role-gated mint/burn, and version-aware asset identity.
 */
contract CompliantFiatTokenV2 is ERC20, Pausable, Nonces, EIP712, LegallyCompliantBaseV2, ICompliantFiatTokenV2 {
    using ECDSA for bytes32;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant BRIDGE_ROLE = keccak256("BRIDGE_ROLE");
    bytes32 public constant SUPPLY_ADMIN_ROLE = keccak256("SUPPLY_ADMIN_ROLE");
    bytes32 public constant METADATA_ADMIN_ROLE = keccak256("METADATA_ADMIN_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");

    bytes32 public constant OPERATION_TRANSFER = keccak256("TRANSFER");
    bytes32 public constant OPERATION_MINT = keccak256("MINT");
    bytes32 public constant OPERATION_BURN = keccak256("BURN");
    bytes32 public constant OPERATION_TRANSFER_WITH_AUTHORIZATION = keccak256("TRANSFER_WITH_AUTHORIZATION");
    bytes32 public constant OPERATION_RECEIVE_WITH_AUTHORIZATION = keccak256("RECEIVE_WITH_AUTHORIZATION");

    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
        keccak256(
            "TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
        );
    bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH =
        keccak256(
            "ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
        );
    bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH =
        keccak256("CancelAuthorization(address authorizer,bytes32 nonce)");

    uint8 private immutable _decimalsStorage;
    string private _currencyCode;
    string private _tokenURI;
    string public versionTag;
    string public symbolDisplay;
    bytes32 public immutable assetId;
    bytes32 public immutable assetVersionId;
    bool public forwardCanonical;
    address private _owner;
    address public governanceController;
    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;

    uint256 public supplyCap;
    uint256 public mintingPeriodCap;
    uint256 public mintingPeriodDuration;
    uint256 public currentMintingPeriodStart;
    uint256 public mintedInCurrentPeriod;

    string[] private _legacyAliases;
    mapping(bytes32 => bool) private _legacyAliasExists;
    mapping(address => mapping(bytes32 => bool)) private _authorizationStates;

    struct OperationContext {
        bool active;
        bytes32 operationType;
        address initiator;
        address authorizer;
        address executor;
        bytes32 reasonHash;
        bytes32 accountingRef;
        bytes32 messageCorrelationId;
        bytes32 additionalDataHash;
    }

    OperationContext private _operationContext;

    error ERC2612ExpiredSignature(uint256 deadline);
    error ERC2612InvalidSigner(address signer, address owner);
    error AuthorizationExpired(uint256 validBefore);
    error AuthorizationNotYetValid(uint256 validAfter);
    error AuthorizationAlreadyUsed(address authorizer, bytes32 nonce);
    error AuthorizationInvalidSigner(address signer, address authorizer);
    error AuthorizationMustBeUsedByPayee(address payee, address caller);
    error SupplyCapExceeded(uint256 cap, uint256 attemptedTotalSupply);
    error MintCapExceeded(uint256 cap, uint256 attemptedPeriodMint);
    error EmptyAlias();
    error DuplicateAlias(string aliasValue);
    error OwnerUnauthorized(address caller);
    error ZeroOwner();
    error GovernanceControllerOnly(address caller);
    error GovernanceControllerNotConfigured();
    error ZeroGovernanceController();

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

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

    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        string memory currencyCode_,
        string memory versionTag_,
        address initialOperator,
        address admin,
        uint256 initialSupply,
        bool forwardCanonical_
    ) ERC20(name_, symbol_) EIP712(name_, versionTag_) LegallyCompliantBaseV2(admin) {
        _decimalsStorage = decimals_;
        _currencyCode = currencyCode_;
        versionTag = versionTag_;
        symbolDisplay = symbol_;
        forwardCanonical = forwardCanonical_;
        _owner = admin;
        assetId = keccak256(bytes(string.concat("GRU:", symbol_)));
        assetVersionId = keccak256(bytes(string.concat("GRU:", symbol_, ":", versionTag_)));

        _grantRole(MINTER_ROLE, initialOperator);
        _grantRole(BURNER_ROLE, initialOperator);
        _grantRole(PAUSER_ROLE, initialOperator);
        _grantRole(BRIDGE_ROLE, initialOperator);
        _grantRole(SUPPLY_ADMIN_ROLE, admin);
        _grantRole(METADATA_ADMIN_ROLE, admin);
        _grantRole(GOVERNANCE_ROLE, admin);
        _grantRole(JURISDICTION_ADMIN_ROLE, admin);
        _grantRole(REGULATOR_ROLE, admin);
        _grantRole(SUPERVISOR_ROLE, admin);
        _grantRole(EMERGENCY_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
        _grantRole(BURNER_ROLE, admin);
        _grantRole(PAUSER_ROLE, admin);
        _grantRole(BRIDGE_ROLE, admin);

        governanceProfileId = keccak256(bytes(string.concat("GRU:GOV:", symbol_, ":", versionTag_)));
        supervisionProfileId = keccak256(bytes(string.concat("GRU:SUP:", currencyCode_)));
        storageNamespace = keccak256(bytes(string.concat("gru.storage.asset.", symbol_, ".", versionTag_)));
        primaryJurisdiction = LEGAL_JURISDICTION;
        supervisionRequired = true;
        minimumUpgradeNoticePeriod = 7 days;

        emit OwnershipTransferred(address(0), admin);

        if (initialSupply > 0) {
            _setOperationContext(
                OPERATION_MINT,
                initialOperator,
                initialOperator,
                initialOperator,
                bytes32(0),
                bytes32(0),
                bytes32(0),
                bytes32(0)
            );
            _mint(initialOperator, initialSupply);
        }
    }

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

    function currencyCode() external view returns (string memory) {
        return _currencyCode;
    }

    function owner() public view returns (address) {
        return _owner;
    }

    function tokenURI() external view returns (string memory) {
        return _tokenURI;
    }

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

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

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

    function legacyAliases() external view returns (string[] memory) {
        return _legacyAliases;
    }

    function authorizationState(address authorizer, bytes32 nonce) public view returns (bool) {
        return _authorizationStates[authorizer][nonce];
    }

    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return _domainSeparatorV4();
    }

    function nonces(address tokenOwner) public view override(Nonces, IERC20Permit) returns (uint256) {
        return super.nonces(tokenOwner);
    }

    function pause() external {
        _checkPauseAuthority();
        _pause();
    }

    function unpause() external {
        _checkPauseAuthority();
        _unpause();
    }

    function transferOwnership(address newOwner) external {
        if (_msgSender() != _owner && !hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {
            revert OwnerUnauthorized(_msgSender());
        }
        if (newOwner == address(0)) revert ZeroOwner();
        address previousOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(previousOwner, newOwner);
    }

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

    function setForwardCanonical(bool value) external onlyGovernanceExecution {
        _setForwardCanonicalValue(value);
    }

    function emergencySetForwardCanonical(bool value) external onlyRole(EMERGENCY_ADMIN_ROLE) {
        _setForwardCanonicalValue(value);
    }

    function setTokenURI(string calldata tokenURI_) external onlyGovernanceExecution {
        _setTokenURIValue(tokenURI_);
    }

    function emergencySetPresentationMetadata(
        bool forwardCanonical_,
        string calldata tokenURI_,
        string calldata symbolDisplay_
    ) external onlyRole(EMERGENCY_ADMIN_ROLE) {
        _setForwardCanonicalValue(forwardCanonical_);
        _setTokenURIValue(tokenURI_);
        _setSymbolDisplayValue(symbolDisplay_);
    }

    function setSymbolDisplay(string calldata symbolDisplay_) external onlyGovernanceExecution {
        _setSymbolDisplayValue(symbolDisplay_);
    }

    function addLegacyAlias(string calldata aliasValue) external onlyGovernanceExecution {
        _addLegacyAliasValue(aliasValue);
    }

    function emergencyAddLegacyAlias(string calldata aliasValue) external onlyRole(EMERGENCY_ADMIN_ROLE) {
        _addLegacyAliasValue(aliasValue);
    }

    function setSupplyControls(
        uint256 supplyCap_,
        uint256 mintingPeriodCap_,
        uint256 mintingPeriodDuration_
    ) external onlyRole(SUPPLY_ADMIN_ROLE) {
        supplyCap = supplyCap_;
        mintingPeriodCap = mintingPeriodCap_;
        mintingPeriodDuration = mintingPeriodDuration_;
        if (mintingPeriodDuration_ > 0 && currentMintingPeriodStart == 0) {
            currentMintingPeriodStart = block.timestamp;
        }
        emit SupplyControlsUpdated(supplyCap_, mintingPeriodCap_, mintingPeriodDuration_);
    }

    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);
    }

    function permit(
        address tokenOwner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public {
        if (block.timestamp > deadline) revert ERC2612ExpiredSignature(deadline);

        bytes32 structHash = keccak256(
            abi.encode(PERMIT_TYPEHASH, tokenOwner, spender, value, _useNonce(tokenOwner), deadline)
        );
        bytes32 digest = _hashTypedDataV4(structHash);
        address signer = ECDSA.recover(digest, v, r, s);
        if (signer != tokenOwner) revert ERC2612InvalidSigner(signer, tokenOwner);

        _approve(tokenOwner, spender, value);
    }

    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        mint(to, amount, bytes32(0));
    }

    function mint(address to, uint256 amount, bytes32 reasonHash) public onlyRole(MINTER_ROLE) {
        _consumeMintCapacity(amount);
        _setOperationContext(
            OPERATION_MINT,
            _msgSender(),
            _msgSender(),
            _msgSender(),
            reasonHash,
            bytes32(0),
            bytes32(0),
            bytes32(0)
        );
        _mint(to, amount);
    }

    function burn(uint256 amount) external {
        _setOperationContext(
            OPERATION_BURN,
            _msgSender(),
            _msgSender(),
            _msgSender(),
            bytes32(0),
            bytes32(0),
            bytes32(0),
            bytes32(0)
        );
        _burn(_msgSender(), amount);
    }

    function burn(address from, uint256 amount, bytes32 reasonHash) public onlyRole(BURNER_ROLE) {
        _setOperationContext(
            OPERATION_BURN,
            from,
            from,
            _msgSender(),
            reasonHash,
            bytes32(0),
            bytes32(0),
            bytes32(0)
        );
        _burn(from, amount);
    }

    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        _useAuthorization(
            from,
            to,
            value,
            validAfter,
            validBefore,
            nonce,
            v,
            r,
            s,
            TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
            OPERATION_TRANSFER_WITH_AUTHORIZATION
        );
    }

    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        if (_msgSender() != to) revert AuthorizationMustBeUsedByPayee(to, _msgSender());
        _useAuthorization(
            from,
            to,
            value,
            validAfter,
            validBefore,
            nonce,
            v,
            r,
            s,
            RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
            OPERATION_RECEIVE_WITH_AUTHORIZATION
        );
    }

    function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external {
        if (_authorizationStates[authorizer][nonce]) revert AuthorizationAlreadyUsed(authorizer, nonce);
        bytes32 structHash = keccak256(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce));
        bytes32 digest = _hashTypedDataV4(structHash);
        address signer = ECDSA.recover(digest, v, r, s);
        if (signer != authorizer) revert AuthorizationInvalidSigner(signer, authorizer);

        _authorizationStates[authorizer][nonce] = true;
        emit AuthorizationCanceled(authorizer, nonce);
    }

    function _update(address from, address to, uint256 amount) internal override whenNotPaused {
        OperationContext memory ctx = _operationContext;
        if (!ctx.active) {
            ctx = OperationContext({
                active: false,
                operationType: OPERATION_TRANSFER,
                initiator: from == address(0) ? _msgSender() : from,
                authorizer: from == address(0) ? _msgSender() : from,
                executor: _msgSender(),
                reasonHash: bytes32(0),
                accountingRef: bytes32(0),
                messageCorrelationId: bytes32(0),
                additionalDataHash: bytes32(0)
            });
        }

        _enforceCompliantOperation(
            ctx.operationType,
            ctx.initiator,
            ctx.authorizer,
            ctx.executor,
            from,
            to,
            amount,
            ctx.reasonHash,
            ctx.accountingRef,
            ctx.messageCorrelationId,
            ctx.additionalDataHash
        );

        super._update(from, to, amount);

        _recordCompliantOperation(
            ctx.operationType,
            ctx.initiator,
            ctx.authorizer,
            ctx.executor,
            from,
            to,
            amount,
            ctx.reasonHash,
            ctx.accountingRef,
            ctx.messageCorrelationId,
            ctx.additionalDataHash
        );

        if (_operationContext.active) {
            delete _operationContext;
        }
    }

    function _useAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s,
        bytes32 typeHash,
        bytes32 operationType
    ) internal {
        if (block.timestamp <= validAfter) revert AuthorizationNotYetValid(validAfter);
        if (block.timestamp >= validBefore) revert AuthorizationExpired(validBefore);
        if (_authorizationStates[from][nonce]) revert AuthorizationAlreadyUsed(from, nonce);

        bytes32 structHash = keccak256(
            abi.encode(typeHash, from, to, value, validAfter, validBefore, nonce)
        );
        bytes32 digest = _hashTypedDataV4(structHash);
        address signer = ECDSA.recover(digest, v, r, s);
        if (signer != from) revert AuthorizationInvalidSigner(signer, from);

        _authorizationStates[from][nonce] = true;
        _setOperationContext(
            operationType,
            from,
            from,
            _msgSender(),
            bytes32(0),
            bytes32(0),
            bytes32(0),
            nonce
        );
        _transfer(from, to, value);
        emit AuthorizationUsed(from, to, nonce, value);
    }

    function _consumeMintCapacity(uint256 amount) internal {
        if (supplyCap > 0 && totalSupply() + amount > supplyCap) {
            revert SupplyCapExceeded(supplyCap, totalSupply() + amount);
        }

        if (mintingPeriodCap == 0 || mintingPeriodDuration == 0) {
            return;
        }

        if (currentMintingPeriodStart == 0 || block.timestamp >= currentMintingPeriodStart + mintingPeriodDuration) {
            currentMintingPeriodStart = block.timestamp;
            mintedInCurrentPeriod = 0;
        }

        uint256 attemptedPeriodMint = mintedInCurrentPeriod + amount;
        if (attemptedPeriodMint > mintingPeriodCap) {
            revert MintCapExceeded(mintingPeriodCap, attemptedPeriodMint);
        }

        mintedInCurrentPeriod = attemptedPeriodMint;
    }

    function _setOperationContext(
        bytes32 operationType,
        address initiator,
        address authorizer,
        address executor,
        bytes32 reasonHash,
        bytes32 accountingRef,
        bytes32 messageCorrelationId,
        bytes32 additionalDataHash
    ) internal {
        _operationContext = OperationContext({
            active: true,
            operationType: operationType,
            initiator: initiator,
            authorizer: authorizer,
            executor: executor,
            reasonHash: reasonHash,
            accountingRef: accountingRef,
            messageCorrelationId: messageCorrelationId,
            additionalDataHash: additionalDataHash
        });
    }

    function _enforceCompliantOperation(
        bytes32,
        address,
        address,
        address,
        address,
        address,
        uint256,
        bytes32,
        bytes32,
        bytes32,
        bytes32
    ) internal view virtual {
        // Stub hook for future PolicyRouter / ComplianceGate / ReserveGate integration.
    }

    function _checkPauseAuthority() internal view {
        if (_msgSender() == _owner) {
            return;
        }
        if (!hasRole(PAUSER_ROLE, _msgSender())) {
            revert OwnerUnauthorized(_msgSender());
        }
    }

    function _setForwardCanonicalValue(bool value) internal {
        forwardCanonical = value;
        emit ForwardCanonicalUpdated(value);
    }

    function _setTokenURIValue(string memory tokenURI_) internal {
        _tokenURI = tokenURI_;
        emit TokenURIUpdated(tokenURI_);
    }

    function _setSymbolDisplayValue(string memory symbolDisplay_) internal {
        symbolDisplay = symbolDisplay_;
        emit SymbolDisplayUpdated(symbolDisplay_);
    }

    function _addLegacyAliasValue(string memory aliasValue) internal {
        if (bytes(aliasValue).length == 0) revert EmptyAlias();
        bytes32 aliasHash = keccak256(bytes(aliasValue));
        if (_legacyAliasExists[aliasHash]) revert DuplicateAlias(aliasValue);
        _legacyAliasExists[aliasHash] = true;
        _legacyAliases.push(aliasValue);
        emit LegacyAliasAdded(aliasValue);
    }

    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_
        );
    }
}
