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

import "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title GRUEntityIbanRegistry
 * @notice Binds normalized IBAN hashes to regulated entity addresses for GRU vault operations.
 * @dev Off-chain normalization: uppercase, strip spaces. Hash = keccak256(bytes(ibanNormalized)).
 *      Integrates with web3-eth-iban Direct IBAN addresses via `directIbanAddress` field.
 */
contract GRUEntityIbanRegistry is AccessControl {
    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");

    struct Record {
        address entity;
        address directIbanAddress;
        bytes32 jurisdictionHash;
        bool active;
    }

    mapping(bytes32 => Record) private _byIbanHash;
    mapping(address => bytes32) public primaryIbanHashByEntity;

    event IbanRegistered(
        bytes32 indexed ibanHash,
        address indexed entity,
        address directIbanAddress,
        bytes32 jurisdictionHash
    );
    event IbanRevoked(bytes32 indexed ibanHash, address indexed entity);
    event DirectIbanAddressUpdated(bytes32 indexed ibanHash, address directIbanAddress);

    constructor(address admin) {
        require(admin != address(0), "GRUEntityIbanRegistry: zero admin");
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(REGISTRAR_ROLE, admin);
    }

    function registerIban(
        bytes32 ibanHash,
        address entity,
        bytes32 jurisdictionHash,
        address directIbanAddress
    ) external onlyRole(REGISTRAR_ROLE) {
        require(ibanHash != bytes32(0), "GRUEntityIbanRegistry: zero hash");
        require(entity != address(0), "GRUEntityIbanRegistry: zero entity");
        require(!_byIbanHash[ibanHash].active, "GRUEntityIbanRegistry: exists");

        _byIbanHash[ibanHash] = Record({
            entity: entity,
            directIbanAddress: directIbanAddress,
            jurisdictionHash: jurisdictionHash,
            active: true
        });
        primaryIbanHashByEntity[entity] = ibanHash;

        emit IbanRegistered(ibanHash, entity, directIbanAddress, jurisdictionHash);
    }

    function setDirectIbanAddress(bytes32 ibanHash, address directIbanAddress) external onlyRole(REGISTRAR_ROLE) {
        require(_byIbanHash[ibanHash].active, "GRUEntityIbanRegistry: inactive");
        _byIbanHash[ibanHash].directIbanAddress = directIbanAddress;
        emit DirectIbanAddressUpdated(ibanHash, directIbanAddress);
    }

    function revokeIban(bytes32 ibanHash) external onlyRole(REGISTRAR_ROLE) {
        Record storage rec = _byIbanHash[ibanHash];
        require(rec.active, "GRUEntityIbanRegistry: inactive");
        address entity = rec.entity;
        rec.active = false;
        if (primaryIbanHashByEntity[entity] == ibanHash) {
            delete primaryIbanHashByEntity[entity];
        }
        emit IbanRevoked(ibanHash, entity);
    }

    function resolveEntity(bytes32 ibanHash) external view returns (address entity, bool active) {
        Record storage rec = _byIbanHash[ibanHash];
        return (rec.entity, rec.active);
    }

    function getRecord(bytes32 ibanHash)
        external
        view
        returns (address entity, address directIbanAddress, bytes32 jurisdictionHash, bool active)
    {
        Record storage rec = _byIbanHash[ibanHash];
        return (rec.entity, rec.directIbanAddress, rec.jurisdictionHash, rec.active);
    }
}
