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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "../interfaces/ITokenRegistry.sol";
import "../libraries/ISO4217WCompliance.sol";

/**
 * @title TokenRegistry
 * @notice Canonical registry mapping ISO-4217 codes to W token addresses
 * @dev Registry is immutable once published (tokens can be deactivated but not removed)
 */
contract TokenRegistry is ITokenRegistry, AccessControl {
    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");

    // Currency code => TokenInfo
    mapping(string => TokenInfo) private _tokens;
    
    // Track all registered currency codes
    string[] private _registeredCurrencies;
    mapping(string => bool) private _isRegistered;

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(REGISTRAR_ROLE, admin);
    }

    /**
     * @notice Register a new ISO-4217 W token
     * @param currencyCode ISO-4217 currency code (must be valid ISO-4217)
     * @param tokenAddress Token contract address
     * @param tokenSymbol Token symbol (should be <CCC>W format)
     * @param decimals Token decimals (typically 2 for fiat currencies)
     * @param custodian Custodian address
     */
    function registerToken(
        string memory currencyCode,
        address tokenAddress,
        string memory tokenSymbol,
        uint8 decimals,
        address custodian
    ) external override onlyRole(REGISTRAR_ROLE) {
        require(tokenAddress != address(0), "TokenRegistry: zero token address");
        require(custodian != address(0), "TokenRegistry: zero custodian");
        require(!_isRegistered[currencyCode], "TokenRegistry: already registered");
        
        // Validate ISO-4217 format
        require(
            ISO4217WCompliance.isValidISO4217Format(currencyCode),
            "TokenRegistry: invalid ISO-4217 format"
        );
        
        // Validate GRU isolation
        require(
            !ISO4217WCompliance.violatesGRUIsolation(currencyCode),
            "TokenRegistry: GRU isolation violation"
        );
        
        // Validate token symbol matches <CCC>W pattern
        require(
            ISO4217WCompliance.validateTokenSymbol(currencyCode, tokenSymbol),
            "TokenRegistry: token symbol must be <CCC>W"
        );
        
        // Create token info
        TokenInfo memory info = TokenInfo({
            tokenAddress: tokenAddress,
            currencyCode: currencyCode,
            tokenSymbol: tokenSymbol,
            decimals: decimals,
            custodian: custodian,
            mintController: address(0), // Set separately
            burnController: address(0), // Set separately
            isActive: true,
            createdAt: block.timestamp
        });
        
        _tokens[currencyCode] = info;
        _registeredCurrencies.push(currencyCode);
        _isRegistered[currencyCode] = true;
        
        emit TokenRegistered(currencyCode, tokenAddress, tokenSymbol, custodian);
    }

    /**
     * @notice Set mint controller for a currency
     * @param currencyCode ISO-4217 currency code
     * @param mintController Mint controller address
     */
    function setMintController(string memory currencyCode, address mintController) external onlyRole(REGISTRAR_ROLE) {
        require(_isRegistered[currencyCode], "TokenRegistry: not registered");
        require(mintController != address(0), "TokenRegistry: zero address");
        _tokens[currencyCode].mintController = mintController;
    }

    /**
     * @notice Set burn controller for a currency
     * @param currencyCode ISO-4217 currency code
     * @param burnController Burn controller address
     */
    function setBurnController(string memory currencyCode, address burnController) external onlyRole(REGISTRAR_ROLE) {
        require(_isRegistered[currencyCode], "TokenRegistry: not registered");
        require(burnController != address(0), "TokenRegistry: zero address");
        _tokens[currencyCode].burnController = burnController;
    }

    /**
     * @notice Get token address for ISO-4217 code
     * @param currencyCode ISO-4217 currency code
     * @return tokenAddress Token contract address
     */
    function getTokenAddress(string memory currencyCode) external view override returns (address tokenAddress) {
        require(_isRegistered[currencyCode], "TokenRegistry: not registered");
        return _tokens[currencyCode].tokenAddress;
    }

    /**
     * @notice Get token info for ISO-4217 code
     * @param currencyCode ISO-4217 currency code
     * @return info Token information
     */
    function getTokenInfo(string memory currencyCode) external view override returns (TokenInfo memory info) {
        require(_isRegistered[currencyCode], "TokenRegistry: not registered");
        return _tokens[currencyCode];
    }

    /**
     * @notice Check if currency code is registered
     * @param currencyCode ISO-4217 currency code
     * @return registered True if registered
     */
    function isRegistered(string memory currencyCode) external view override returns (bool registered) {
        return _isRegistered[currencyCode];
    }

    /**
     * @notice Deactivate a token (emergency)
     * @param currencyCode ISO-4217 currency code
     */
    function deactivateToken(string memory currencyCode) external override onlyRole(REGISTRAR_ROLE) {
        require(_isRegistered[currencyCode], "TokenRegistry: not registered");
        require(_tokens[currencyCode].isActive, "TokenRegistry: already inactive");
        
        _tokens[currencyCode].isActive = false;
        emit TokenDeactivated(currencyCode, block.timestamp);
    }

    /**
     * @notice Get all registered currency codes
     * @return currencies Array of registered currency codes
     */
    function getRegisteredCurrencies() external view returns (string[] memory currencies) {
        return _registeredCurrencies;
    }
}
