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

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

/**
 * @title ConfigurationRegistry
 * @notice Centralized configuration without hardcoding
 * @dev Eliminates hardcoded addresses, enables runtime configuration
 */
contract ConfigurationRegistry is 
    Initializable,
    AccessControlUpgradeable,
    UUPSUpgradeable
{
    bytes32 public constant CONFIG_ADMIN_ROLE = keccak256("CONFIG_ADMIN_ROLE");
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    mapping(address => mapping(bytes32 => bytes)) private configs;
    mapping(address => bytes32[]) private configKeys;

    event ConfigSet(address indexed contractAddr, bytes32 indexed key, bytes value);
    event ConfigDeleted(address indexed contractAddr, bytes32 indexed key);

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

    function initialize(address admin) external initializer {
        __AccessControl_init();
        __UUPSUpgradeable_init();
        
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(CONFIG_ADMIN_ROLE, admin);
        _grantRole(UPGRADER_ROLE, admin);
    }

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

    function set(address contractAddr, bytes32 key, bytes calldata value) external onlyRole(CONFIG_ADMIN_ROLE) {
        require(contractAddr != address(0), "Zero address");
        require(key != bytes32(0), "Zero key");
        
        if (configs[contractAddr][key].length == 0) {
            configKeys[contractAddr].push(key);
        }
        
        configs[contractAddr][key] = value;
        emit ConfigSet(contractAddr, key, value);
    }

    function get(address contractAddr, bytes32 key) external view returns (bytes memory) {
        return configs[contractAddr][key];
    }

    function getAddress(address contractAddr, bytes32 key) external view returns (address) {
        bytes memory data = configs[contractAddr][key];
        require(data.length == 32, "Invalid data");
        return abi.decode(data, (address));
    }

    function getUint256(address contractAddr, bytes32 key) external view returns (uint256) {
        bytes memory data = configs[contractAddr][key];
        require(data.length == 32, "Invalid data");
        return abi.decode(data, (uint256));
    }
}
