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

/**
 * @title GRUConstants
 * @notice Constants and utilities for Global Reserve Unit (GRU)
 * @dev GRU is a NON-ISO 4217 synthetic unit of account, NOT legal tender
 * 
 * MANDATORY COMPLIANCE:
 * - GRU SHALL NOT be treated as fiat currency
 * - GRU SHALL be explicitly identified as synthetic unit of account
 * - All GRU triangulations MUST be conducted through XAU
 * - GRU relationships MUST be enforced exactly: 1 M00 GRU = 5 M0 GRU = 25 M1 GRU
 */
library GRUConstants {
    /**
     * @notice GRU is NOT an ISO 4217 currency
     * @dev This constant explicitly identifies GRU as non-ISO
     */
    string public constant GRU_CURRENCY_CODE = "GRU"; // Non-ISO 4217 synthetic unit of account
    
    /**
     * @notice GRU monetary layers
     */
    string public constant GRU_M00 = "M00"; // Base layer (non-ISO)
    string public constant GRU_M0 = "M0";   // Collateral layer (non-ISO)
    string public constant GRU_M1 = "M1";   // Credit layer (non-ISO)
    
    /**
     * @notice GRU conversion ratios (MANDATORY - must be enforced exactly)
     * @dev 1 M00 GRU = 5 M0 GRU = 25 M1 GRU
     */
    uint256 public constant M00_TO_M0_RATIO = 5;      // 1 M00 = 5 M0
    uint256 public constant M00_TO_M1_RATIO = 25;     // 1 M00 = 25 M1
    uint256 public constant M0_TO_M1_RATIO = 5;       // 1 M0 = 5 M1
    
    /**
     * @notice Decimals for GRU calculations (18 decimals for precision)
     */
    uint256 public constant GRU_DECIMALS = 1e18;
    
    /**
     * @notice Convert M00 GRU to M0 GRU
     * @param m00Amount Amount in M00 GRU (18 decimals)
     * @return m0Amount Amount in M0 GRU (18 decimals)
     */
    function m00ToM0(uint256 m00Amount) internal pure returns (uint256 m0Amount) {
        return (m00Amount * M00_TO_M0_RATIO * GRU_DECIMALS) / GRU_DECIMALS;
    }
    
    /**
     * @notice Convert M00 GRU to M1 GRU
     * @param m00Amount Amount in M00 GRU (18 decimals)
     * @return m1Amount Amount in M1 GRU (18 decimals)
     */
    function m00ToM1(uint256 m00Amount) internal pure returns (uint256 m1Amount) {
        return (m00Amount * M00_TO_M1_RATIO * GRU_DECIMALS) / GRU_DECIMALS;
    }
    
    /**
     * @notice Convert M0 GRU to M1 GRU
     * @param m0Amount Amount in M0 GRU (18 decimals)
     * @return m1Amount Amount in M1 GRU (18 decimals)
     */
    function m0ToM1(uint256 m0Amount) internal pure returns (uint256 m1Amount) {
        return (m0Amount * M0_TO_M1_RATIO * GRU_DECIMALS) / GRU_DECIMALS;
    }
    
    /**
     * @notice Convert M0 GRU to M00 GRU
     * @param m0Amount Amount in M0 GRU (18 decimals)
     * @return m00Amount Amount in M00 GRU (18 decimals)
     */
    function m0ToM00(uint256 m0Amount) internal pure returns (uint256 m00Amount) {
        return (m0Amount * GRU_DECIMALS) / (M00_TO_M0_RATIO * GRU_DECIMALS);
    }
    
    /**
     * @notice Convert M1 GRU to M00 GRU
     * @param m1Amount Amount in M1 GRU (18 decimals)
     * @return m00Amount Amount in M00 GRU (18 decimals)
     */
    function m1ToM00(uint256 m1Amount) internal pure returns (uint256 m00Amount) {
        return (m1Amount * GRU_DECIMALS) / (M00_TO_M1_RATIO * GRU_DECIMALS);
    }
    
    /**
     * @notice Convert M1 GRU to M0 GRU
     * @param m1Amount Amount in M1 GRU (18 decimals)
     * @return m0Amount Amount in M0 GRU (18 decimals)
     */
    function m1ToM0(uint256 m1Amount) internal pure returns (uint256 m0Amount) {
        return (m1Amount * GRU_DECIMALS) / (M0_TO_M1_RATIO * GRU_DECIMALS);
    }
    
    /**
     * @notice Validate GRU layer code
     * @param layerCode Layer code to validate
     * @return isValid True if valid GRU layer
     */
    function isValidGRULayer(string memory layerCode) internal pure returns (bool isValid) {
        bytes32 codeHash = keccak256(bytes(layerCode));
        return codeHash == keccak256(bytes(GRU_M00)) ||
               codeHash == keccak256(bytes(GRU_M0)) ||
               codeHash == keccak256(bytes(GRU_M1)) ||
               codeHash == keccak256(bytes(GRU_CURRENCY_CODE));
    }
}
