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

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @title UniversalAssetRegistry
 * @notice Central registry for all asset types with governance and compliance
 * @dev Supports 10+ asset types with hybrid governance based on risk levels
 */
contract UniversalAssetRegistry is 
    Initializable,
    AccessControlUpgradeable,
    ReentrancyGuardUpgradeable,
    UUPSUpgradeable
{
    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");
    bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
    bytes32 public constant VALIDATOR_ROLE = keccak256("VALIDATOR_ROLE");
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    // Asset classification types
    enum AssetType {
        ERC20Standard,      // Standard tokens
        ISO4217W,           // eMoney/CBDCs
        GRU,                // Global Reserve Units (M00/M0/M1)
        Commodity,          // Gold, oil, etc.
        Security,           // Tokenized securities
        RealWorldAsset,     // Real estate, art, etc.
        Synthetic,          // Derivatives, futures
        Stablecoin,         // USDT, USDC, etc.
        GovernanceToken,    // DAO tokens
        NFTBacked           // NFT-collateralized tokens
    }

    // Compliance levels
    enum ComplianceLevel {
        Public,             // No restrictions
        KYC,                // KYC required
        Accredited,         // Accredited investors only
        Institutional,      // Institutions only
        Sovereign           // Central banks/governments only
    }

    // Proposal types
    enum ProposalType {
        AddAsset,
        RemoveAsset,
        UpdateRiskParams,
        UpdateCompliance,
        EmergencyPause
    }

    struct UniversalAsset {
        address tokenAddress;
        AssetType assetType;
        ComplianceLevel complianceLevel;
        
        // Metadata
        string name;
        string symbol;
        uint8 decimals;
        string jurisdiction;
        
        // Risk parameters
        uint8 volatilityScore;      // 0-100
        uint256 minBridgeAmount;
        uint256 maxBridgeAmount;
        uint256 dailyVolumeLimit;
        
        // PMM liquidity
        address pmmPool;
        bool hasLiquidity;
        uint256 liquidityReserveUSD;
        
        // Governance
        bool requiresGovernance;
        address[] validators;
        uint256 validationThreshold;
        
        // Status
        bool isActive;
        uint256 registeredAt;
        uint256 lastUpdated;
    }

    struct PendingAssetProposal {
        bytes32 proposalId;
        ProposalType proposalType;
        address proposer;
        uint256 proposedAt;
        uint256 executeAfter;
        uint256 votesFor;
        uint256 votesAgainst;
        bool executed;
        bool cancelled;
        
        // Proposal data
        address tokenAddress;
        AssetType assetType;
        ComplianceLevel complianceLevel;
        string name;
        string symbol;
        uint8 decimals;
        string jurisdiction;
        uint8 volatilityScore;
        uint256 minBridgeAmount;
        uint256 maxBridgeAmount;
    }

    // Storage
    mapping(address => UniversalAsset) public assets;
    mapping(AssetType => address[]) public assetsByType;
    mapping(bytes32 => PendingAssetProposal) public proposals;
    mapping(bytes32 => mapping(address => bool)) public hasVoted;
    
    // Governance parameters
    uint256 public constant TIMELOCK_STANDARD = 1 days;
    uint256 public constant TIMELOCK_MODERATE = 3 days;
    uint256 public constant TIMELOCK_HIGH = 7 days;
    uint256 public quorumPercentage;
    
    // Validator set
    address[] public validators;
    mapping(address => bool) public isValidator;

    // Events
    event AssetProposed(
        bytes32 indexed proposalId,
        address indexed token,
        AssetType assetType,
        address proposer
    );
    
    event AssetApproved(
        address indexed token,
        AssetType assetType,
        ComplianceLevel complianceLevel
    );
    
    event AssetRemoved(address indexed token, AssetType assetType);
    
    event ValidatorAdded(address indexed validator);
    event ValidatorRemoved(address indexed validator);
    
    event ProposalVoted(
        bytes32 indexed proposalId,
        address indexed voter,
        bool support
    );
    
    event ProposalExecuted(bytes32 indexed proposalId);
    event ProposalCancelled(bytes32 indexed proposalId);

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address admin) external initializer {
        __AccessControl_init();
        __ReentrancyGuard_init();
        __UUPSUpgradeable_init();
        
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(REGISTRAR_ROLE, admin);
        _grantRole(PROPOSER_ROLE, admin);
        _grantRole(VALIDATOR_ROLE, admin);
        _grantRole(UPGRADER_ROLE, admin);
        
        quorumPercentage = 51; // 51% quorum
    }

    function _authorizeUpgrade(address newImplementation)
        internal override onlyRole(UPGRADER_ROLE) {}

    /**
     * @notice Propose new asset with timelock governance
     */
    function proposeAsset(
        address tokenAddress,
        AssetType assetType,
        ComplianceLevel complianceLevel,
        string calldata name,
        string calldata symbol,
        uint8 decimals,
        string calldata jurisdiction,
        uint8 volatilityScore,
        uint256 minBridge,
        uint256 maxBridge
    ) external onlyRole(PROPOSER_ROLE) returns (bytes32 proposalId) {
        require(tokenAddress != address(0), "Zero address");
        require(!assets[tokenAddress].isActive, "Already registered");
        require(volatilityScore <= 100, "Invalid volatility");
        require(maxBridge >= minBridge, "Invalid limits");
        
        proposalId = keccak256(abi.encode(
            tokenAddress,
            assetType,
            block.timestamp,
            msg.sender
        ));
        
        uint256 timelockPeriod = _getTimelockPeriod(assetType, complianceLevel);
        
        PendingAssetProposal storage proposal = proposals[proposalId];
        proposal.proposalId = proposalId;
        proposal.proposalType = ProposalType.AddAsset;
        proposal.proposer = msg.sender;
        proposal.proposedAt = block.timestamp;
        proposal.executeAfter = block.timestamp + timelockPeriod;
        proposal.tokenAddress = tokenAddress;
        proposal.assetType = assetType;
        proposal.complianceLevel = complianceLevel;
        proposal.name = name;
        proposal.symbol = symbol;
        proposal.decimals = decimals;
        proposal.jurisdiction = jurisdiction;
        proposal.volatilityScore = volatilityScore;
        proposal.minBridgeAmount = minBridge;
        proposal.maxBridgeAmount = maxBridge;
        
        emit AssetProposed(proposalId, tokenAddress, assetType, msg.sender);
        
        return proposalId;
    }

    /**
     * @notice Vote on asset proposal
     */
    function voteOnProposal(
        bytes32 proposalId,
        bool support
    ) external onlyRole(VALIDATOR_ROLE) {
        PendingAssetProposal storage proposal = proposals[proposalId];
        require(!proposal.executed, "Already executed");
        require(!proposal.cancelled, "Cancelled");
        require(!hasVoted[proposalId][msg.sender], "Already voted");
        
        hasVoted[proposalId][msg.sender] = true;
        
        if (support) {
            proposal.votesFor++;
        } else {
            proposal.votesAgainst++;
        }
        
        emit ProposalVoted(proposalId, msg.sender, support);
    }

    /**
     * @notice Execute proposal after timelock
     */
    function executeProposal(bytes32 proposalId) external nonReentrant {
        PendingAssetProposal storage proposal = proposals[proposalId];
        
        require(!proposal.executed, "Already executed");
        require(!proposal.cancelled, "Cancelled");
        require(block.timestamp >= proposal.executeAfter, "Timelock active");
        
        // Check quorum
        uint256 totalVotes = proposal.votesFor + proposal.votesAgainst;
        uint256 requiredVotes = (validators.length * quorumPercentage) / 100;
        require(totalVotes >= requiredVotes, "Quorum not met");
        require(proposal.votesFor > proposal.votesAgainst, "Rejected");
        
        // Execute based on proposal type
        if (proposal.proposalType == ProposalType.AddAsset) {
            _registerAsset(proposal);
        }
        
        proposal.executed = true;
        emit ProposalExecuted(proposalId);
    }

    /**
     * @notice Register a GRU compliant token (c*) directly — no timelock. For use by deploy scripts to integrate all c* with GRU ERC-2535 / bridge.
     * @dev Only REGISTRAR_ROLE. Registers as AssetType.GRU so GRUCCIPBridge and PoolManager accept the token.
     */
    function registerGRUCompliantAsset(
        address tokenAddress,
        string calldata name,
        string calldata symbol,
        uint8 decimals,
        string calldata jurisdiction
    ) external onlyRole(REGISTRAR_ROLE) {
        require(tokenAddress != address(0), "Zero address");
        require(!assets[tokenAddress].isActive, "Already registered");
        _registerAssetDirect(
            tokenAddress,
            AssetType.GRU,
            ComplianceLevel.Institutional,
            name,
            symbol,
            decimals,
            jurisdiction,
            10,      // volatilityScore (low for stable)
            1e6,     // minBridge (1 unit with 6 decimals)
            100_000_000 * 10**6  // maxBridge (100M with 6 decimals)
        );
    }

    /**
     * @notice Internal asset registration from proposal
     */
    function _registerAsset(PendingAssetProposal storage proposal) internal {
        _registerAssetDirect(
            proposal.tokenAddress,
            proposal.assetType,
            proposal.complianceLevel,
            proposal.name,
            proposal.symbol,
            proposal.decimals,
            proposal.jurisdiction,
            proposal.volatilityScore,
            proposal.minBridgeAmount,
            proposal.maxBridgeAmount
        );
    }

    /**
     * @notice Internal asset registration (shared by proposal execution and registerGRUCompliantAsset)
     */
    function _registerAssetDirect(
        address tokenAddress,
        AssetType assetType,
        ComplianceLevel complianceLevel,
        string memory name,
        string memory symbol,
        uint8 decimals,
        string memory jurisdiction,
        uint8 volatilityScore,
        uint256 minBridgeAmount,
        uint256 maxBridgeAmount
    ) internal {
        UniversalAsset storage asset = assets[tokenAddress];
        asset.tokenAddress = tokenAddress;
        asset.assetType = assetType;
        asset.complianceLevel = complianceLevel;
        asset.name = name;
        asset.symbol = symbol;
        asset.decimals = decimals;
        asset.jurisdiction = jurisdiction;
        asset.volatilityScore = volatilityScore;
        asset.minBridgeAmount = minBridgeAmount;
        asset.maxBridgeAmount = maxBridgeAmount;
        asset.dailyVolumeLimit = maxBridgeAmount * 10;
        asset.requiresGovernance = _requiresGovernance(assetType, complianceLevel);
        asset.isActive = true;
        asset.registeredAt = block.timestamp;
        asset.lastUpdated = block.timestamp;
        assetsByType[assetType].push(tokenAddress);
        emit AssetApproved(tokenAddress, assetType, complianceLevel);
    }

    /**
     * @notice Add validator
     */
    function addValidator(address validator) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(validator != address(0), "Zero address");
        require(!isValidator[validator], "Already validator");
        
        validators.push(validator);
        isValidator[validator] = true;
        _grantRole(VALIDATOR_ROLE, validator);
        
        emit ValidatorAdded(validator);
    }

    /**
     * @notice Remove validator
     */
    function removeValidator(address validator) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(isValidator[validator], "Not validator");
        
        isValidator[validator] = false;
        _revokeRole(VALIDATOR_ROLE, validator);
        
        // Remove from array
        for (uint256 i = 0; i < validators.length; i++) {
            if (validators[i] == validator) {
                validators[i] = validators[validators.length - 1];
                validators.pop();
                break;
            }
        }
        
        emit ValidatorRemoved(validator);
    }

    /**
     * @notice Update PMM pool for asset
     */
    function updatePMMPool(
        address token,
        address pmmPool
    ) external onlyRole(REGISTRAR_ROLE) {
        require(assets[token].isActive, "Not registered");
        
        assets[token].pmmPool = pmmPool;
        assets[token].hasLiquidity = pmmPool != address(0);
        assets[token].lastUpdated = block.timestamp;
    }

    /**
     * @notice Get timelock period based on asset type and compliance
     */
    function _getTimelockPeriod(
        AssetType assetType,
        ComplianceLevel complianceLevel
    ) internal pure returns (uint256) {
        if (assetType == AssetType.Security ||
            assetType == AssetType.ISO4217W ||
            complianceLevel >= ComplianceLevel.Institutional) {
            return TIMELOCK_HIGH;
        } else if (assetType == AssetType.Commodity ||
                   assetType == AssetType.RealWorldAsset ||
                   complianceLevel >= ComplianceLevel.Accredited) {
            return TIMELOCK_MODERATE;
        } else {
            return TIMELOCK_STANDARD;
        }
    }

    /**
     * @notice Check if asset type requires governance
     */
    function _requiresGovernance(
        AssetType assetType,
        ComplianceLevel complianceLevel
    ) internal pure returns (bool) {
        return assetType == AssetType.Security ||
               assetType == AssetType.ISO4217W ||
               assetType == AssetType.Commodity ||
               complianceLevel >= ComplianceLevel.Accredited;
    }

    // View functions
    
    function getAsset(address token) external view returns (UniversalAsset memory) {
        return assets[token];
    }

    function getAssetType(address token) external view returns (AssetType) {
        return assets[token].assetType;
    }

    function getAssetsByType(AssetType assetType) external view returns (address[] memory) {
        return assetsByType[assetType];
    }

    function getValidators() external view returns (address[] memory) {
        return validators;
    }

    function getProposal(bytes32 proposalId) external view returns (PendingAssetProposal memory) {
        return proposals[proposalId];
    }

    function isAssetActive(address token) external view returns (bool) {
        return assets[token].isActive;
    }
}
