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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "../interop/BridgeEscrowVault.sol";
import "../../iso4217w/interfaces/IISO4217WToken.sol";
import "../../iso4217w/oracle/ReserveOracle.sol";

/**
 * @title WTokenReserveVerifier
 * @notice Verifies W token reserves before allowing bridge operations
 * @dev Ensures 1:1 backing maintained across bridge operations
 */
contract WTokenReserveVerifier is AccessControl {
    bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    BridgeEscrowVault public bridgeEscrowVault;
    ReserveOracle public reserveOracle;
    mapping(address => bool) public verifiedTokens; // W token => verified

    // Reserve verification threshold (10000 = 100%)
    uint256 public reserveThreshold = 10000; // 100% (must be fully backed)

    event TokenVerified(address indexed token, bool verified);
    event ReserveVerified(
        address indexed token,
        uint256 reserve,
        uint256 supply,
        uint256 bridgeAmount,
        bool sufficient
    );

    error InsufficientReserve();
    error TokenNotVerified();
    error InvalidToken();

    constructor(
        address admin,
        address bridgeEscrowVault_,
        address reserveOracle_
    ) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(VERIFIER_ROLE, admin);
        _grantRole(OPERATOR_ROLE, admin);

        require(bridgeEscrowVault_ != address(0), "WTokenReserveVerifier: zero bridge");
        require(reserveOracle_ != address(0), "WTokenReserveVerifier: zero oracle");

        bridgeEscrowVault = BridgeEscrowVault(bridgeEscrowVault_);
        reserveOracle = ReserveOracle(reserveOracle_);
    }

    /**
     * @notice Verify reserve before bridge operation
     * @param token W token address
     * @param bridgeAmount Amount to bridge
     * @return verified True if reserve is sufficient
     */
    function verifyReserveBeforeBridge(
        address token,
        uint256 bridgeAmount
    ) external returns (bool verified) {
        if (!verifiedTokens[token]) revert TokenNotVerified();

        IISO4217WToken wToken = IISO4217WToken(token);
        
        uint256 verifiedReserve = wToken.verifiedReserve();
        uint256 currentSupply = wToken.totalSupply();

        // After bridge, supply on this chain decreases, but reserve must still cover remaining supply
        // Reserve must be >= (currentSupply - bridgeAmount) * reserveThreshold / 10000
        uint256 requiredReserve = ((currentSupply - bridgeAmount) * reserveThreshold) / 10000;

        verified = verifiedReserve >= requiredReserve;

        if (!verified) revert InsufficientReserve();

        emit ReserveVerified(token, verifiedReserve, currentSupply, bridgeAmount, verified);
    }

    /**
     * @notice Verify reserve on destination chain (after bridge)
     * @param token W token address on destination chain
     * @param bridgeAmount Amount bridged
     * @param destinationReserve Reserve on destination chain
     * @param destinationSupply Supply on destination chain (before minting bridged amount)
     * @return verified True if destination reserve is sufficient
     */
    function verifyDestinationReserve(
        address token,
        uint256 bridgeAmount,
        uint256 destinationReserve,
        uint256 destinationSupply
    ) external returns (bool verified) {
        // After minting bridged amount on destination: newSupply = destinationSupply + bridgeAmount
        // Required reserve: (newSupply * reserveThreshold) / 10000
        uint256 newSupply = destinationSupply + bridgeAmount;
        uint256 requiredReserve = (newSupply * reserveThreshold) / 10000;

        verified = destinationReserve >= requiredReserve;

        if (!verified) revert InsufficientReserve();

        emit ReserveVerified(token, destinationReserve, newSupply, bridgeAmount, verified);
    }

    /**
     * @notice Verify reserve sufficiency using oracle
     * @param token W token address
     * @param bridgeAmount Amount to bridge
     * @return verified True if reserve is sufficient according to oracle
     */
    function verifyReserveWithOracle(
        address token,
        uint256 bridgeAmount
    ) external returns (bool verified) {
        if (!verifiedTokens[token]) revert TokenNotVerified();

        IISO4217WToken wToken = IISO4217WToken(token);
        
        // Get currency code from token
        string memory currencyCode = wToken.currencyCode();
        
        // Get verified reserve from oracle (consensus)
        (uint256 verifiedReserve, ) = reserveOracle.getVerifiedReserve(currencyCode);
        
        uint256 currentSupply = wToken.totalSupply();

        // Required reserve after bridge
        uint256 requiredReserve = ((currentSupply - bridgeAmount) * reserveThreshold) / 10000;

        verified = verifiedReserve >= requiredReserve;

        if (!verified) revert InsufficientReserve();

        emit ReserveVerified(token, verifiedReserve, currentSupply, bridgeAmount, verified);
    }

    /**
     * @notice Register a W token for reserve verification
     * @param token W token address
     */
    function registerToken(address token) external onlyRole(OPERATOR_ROLE) {
        require(token != address(0), "WTokenReserveVerifier: zero token");

        // Verify it's a valid W token (implements IISO4217WToken)
        try IISO4217WToken(token).currencyCode() returns (string memory) {
            verifiedTokens[token] = true;
            emit TokenVerified(token, true);
        } catch {
            revert InvalidToken();
        }
    }

    /**
     * @notice Unregister a W token
     * @param token W token address
     */
    function unregisterToken(address token) external onlyRole(OPERATOR_ROLE) {
        verifiedTokens[token] = false;
        emit TokenVerified(token, false);
    }

    /**
     * @notice Set reserve verification threshold
     * @param threshold Threshold in basis points (10000 = 100%)
     */
    function setReserveThreshold(uint256 threshold) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(threshold <= 10000, "WTokenReserveVerifier: threshold > 100%");
        require(threshold >= 10000, "WTokenReserveVerifier: threshold must be 100%"); // Hard requirement
        reserveThreshold = threshold;
    }

    /**
     * @notice Check if token is verified
     */
    function isTokenVerified(address token) external view returns (bool) {
        return verifiedTokens[token];
    }
}
