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

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

/**
 * @title TokenRegistry
 * @notice Registry for all tokens on ChainID 138
 * @dev Provides a centralized registry for token addresses, metadata, and status
 */
contract TokenRegistry is AccessControl {
    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");
    
    /**
     * @notice Token information structure
     */
    struct TokenInfo {
        address tokenAddress;
        string name;
        string symbol;
        uint8 decimals;
        bool isActive;
        bool isNative;
        address bridgeAddress; // If bridged from another chain
        uint256 registeredAt;
        uint256 lastUpdated;
    }
    
    mapping(address => TokenInfo) private _tokens;
    mapping(string => address) private _tokensBySymbol;
    address[] private _tokenList;
    
    event TokenRegistered(
        address indexed tokenAddress,
        string name,
        string symbol,
        uint8 decimals,
        uint256 timestamp
    );
    
    event TokenUpdated(
        address indexed tokenAddress,
        bool isActive,
        uint256 timestamp
    );
    
    event TokenRemoved(
        address indexed tokenAddress,
        uint256 timestamp
    );
    
    /**
     * @notice Constructor
     * @param admin Address that will receive DEFAULT_ADMIN_ROLE
     */
    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(REGISTRAR_ROLE, admin);
    }
    
    /**
     * @notice Register a new token
     * @param tokenAddress Address of the token contract
     * @param name Token name
     * @param symbol Token symbol
     * @param decimals Number of decimals
     * @param isNative Whether this is a native token (e.g., ETH)
     * @param bridgeAddress Bridge address if this is a bridged token (address(0) if not)
     * @dev Requires REGISTRAR_ROLE
     */
    function registerToken(
        address tokenAddress,
        string calldata name,
        string calldata symbol,
        uint8 decimals,
        bool isNative,
        address bridgeAddress
    ) external onlyRole(REGISTRAR_ROLE) {
        require(tokenAddress != address(0), "TokenRegistry: zero address");
        require(_tokens[tokenAddress].tokenAddress == address(0), "TokenRegistry: token already registered");
        require(_tokensBySymbol[symbol] == address(0), "TokenRegistry: symbol already used");
        
        // Verify token contract exists (if not native)
        if (!isNative) {
            require(tokenAddress.code.length > 0, "TokenRegistry: invalid token contract");
        }
        
        _tokens[tokenAddress] = TokenInfo({
            tokenAddress: tokenAddress,
            name: name,
            symbol: symbol,
            decimals: decimals,
            isActive: true,
            isNative: isNative,
            bridgeAddress: bridgeAddress,
            registeredAt: block.timestamp,
            lastUpdated: block.timestamp
        });
        
        _tokensBySymbol[symbol] = tokenAddress;
        _tokenList.push(tokenAddress);
        
        emit TokenRegistered(tokenAddress, name, symbol, decimals, block.timestamp);
    }
    
    /**
     * @notice Update token status
     * @param tokenAddress Address of the token
     * @param isActive New active status
     * @dev Requires REGISTRAR_ROLE
     */
    function updateTokenStatus(address tokenAddress, bool isActive) external onlyRole(REGISTRAR_ROLE) {
        require(_tokens[tokenAddress].tokenAddress != address(0), "TokenRegistry: token not registered");
        
        _tokens[tokenAddress].isActive = isActive;
        _tokens[tokenAddress].lastUpdated = block.timestamp;
        
        emit TokenUpdated(tokenAddress, isActive, block.timestamp);
    }
    
    /**
     * @notice Remove a token from the registry
     * @param tokenAddress Address of the token
     * @dev Requires REGISTRAR_ROLE
     */
    function removeToken(address tokenAddress) external onlyRole(REGISTRAR_ROLE) {
        require(_tokens[tokenAddress].tokenAddress != address(0), "TokenRegistry: token not registered");
        
        // Remove from symbol mapping
        delete _tokensBySymbol[_tokens[tokenAddress].symbol];
        
        // Remove from list (swap with last element and pop)
        for (uint256 i = 0; i < _tokenList.length; i++) {
            if (_tokenList[i] == tokenAddress) {
                _tokenList[i] = _tokenList[_tokenList.length - 1];
                _tokenList.pop();
                break;
            }
        }
        
        delete _tokens[tokenAddress];
        
        emit TokenRemoved(tokenAddress, block.timestamp);
    }
    
    /**
     * @notice Get token information
     * @param tokenAddress Address of the token
     * @return Token information struct
     */
    function getTokenInfo(address tokenAddress) external view returns (TokenInfo memory) {
        return _tokens[tokenAddress];
    }
    
    /**
     * @notice Get token address by symbol
     * @param symbol Token symbol
     * @return Token address (address(0) if not found)
     */
    function getTokenBySymbol(string calldata symbol) external view returns (address) {
        address tokenAddr = _tokensBySymbol[symbol];
        return tokenAddr;
    }
    
    /**
     * @notice Check if a token is registered
     * @param tokenAddress Address of the token
     * @return True if registered, false otherwise
     */
    function isTokenRegistered(address tokenAddress) external view returns (bool) {
        return _tokens[tokenAddress].tokenAddress != address(0);
    }
    
    /**
     * @notice Check if a token is active
     * @param tokenAddress Address of the token
     * @return True if active, false otherwise
     */
    function isTokenActive(address tokenAddress) external view returns (bool) {
        TokenInfo memory token = _tokens[tokenAddress];
        return token.tokenAddress != address(0) && token.isActive;
    }
    
    /**
     * @notice Get all registered tokens
     * @return Array of token addresses
     */
    function getAllTokens() external view returns (address[] memory) {
        return _tokenList;
    }
    
    /**
     * @notice Get count of registered tokens
     * @return Number of registered tokens
     */
    function getTokenCount() external view returns (uint256) {
        return _tokenList.length;
    }
}

