// 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/ComplianceGuard.sol";

/**
 * @title WTokenComplianceEnforcer
 * @notice Enforces W token compliance rules on bridge operations
 * @dev Ensures money multiplier = 1.0 and GRU isolation on bridge
 */
contract WTokenComplianceEnforcer is AccessControl {
    bytes32 public constant ENFORCER_ROLE = keccak256("ENFORCER_ROLE");
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    BridgeEscrowVault public bridgeEscrowVault;
    ComplianceGuard public complianceGuard;
    mapping(address => bool) public enabledTokens; // W token => enabled

    event TokenEnabled(address indexed token, bool enabled);
    event ComplianceChecked(
        address indexed token,
        bytes32 reasonCode,
        bool compliant
    );

    error ComplianceViolation(bytes32 reasonCode);
    error TokenNotEnabled();
    error InvalidToken();

    constructor(
        address admin,
        address bridgeEscrowVault_,
        address complianceGuard_
    ) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(ENFORCER_ROLE, admin);
        _grantRole(OPERATOR_ROLE, admin);

        require(bridgeEscrowVault_ != address(0), "WTokenComplianceEnforcer: zero bridge");
        require(complianceGuard_ != address(0), "WTokenComplianceEnforcer: zero guard");

        bridgeEscrowVault = BridgeEscrowVault(bridgeEscrowVault_);
        complianceGuard = ComplianceGuard(complianceGuard_);
    }

    /**
     * @notice Check compliance before bridge operation
     * @param token W token address
     * @param bridgeAmount Amount to bridge
     * @return compliant True if compliant
     */
    function checkComplianceBeforeBridge(
        address token,
        uint256 bridgeAmount
    ) external returns (bool compliant) {
        if (!enabledTokens[token]) revert TokenNotEnabled();

        IISO4217WToken wToken = IISO4217WToken(token);
        string memory currencyCode = wToken.currencyCode();
        uint256 verifiedReserve = wToken.verifiedReserve();
        uint256 currentSupply = wToken.totalSupply();
        uint256 newSupply = currentSupply - bridgeAmount; // Supply after bridge

        // Validate mint operation (simulating future state)
        (bool isValid, bytes32 reasonCode) = complianceGuard.validateMint(
            currencyCode,
            bridgeAmount,
            newSupply,
            verifiedReserve
        );

        if (!isValid) {
            emit ComplianceChecked(token, reasonCode, false);
            revert ComplianceViolation(reasonCode);
        }

        // Validate money multiplier = 1.0
        if (!complianceGuard.validateMoneyMultiplier(verifiedReserve, newSupply)) {
            bytes32 multiplierReason = keccak256("MONEY_MULTIPLIER_VIOLATION");
            emit ComplianceChecked(token, multiplierReason, false);
            revert ComplianceViolation(multiplierReason);
        }

        // Validate GRU isolation
        if (complianceGuard.violatesGRUIsolation(currencyCode)) {
            bytes32 gruReason = keccak256("GRU_ISOLATION_VIOLATION");
            emit ComplianceChecked(token, gruReason, false);
            revert ComplianceViolation(gruReason);
        }

        emit ComplianceChecked(token, bytes32(0), true);
        compliant = true;
    }

    /**
     * @notice Check compliance on destination chain (before minting bridged amount)
     * @param currencyCode ISO-4217 currency code
     * @param bridgeAmount Amount to mint on destination
     * @param destinationReserve Reserve on destination chain
     * @param destinationSupply Supply on destination chain (before mint)
     * @return compliant True if compliant
     */
    function checkDestinationCompliance(
        string memory currencyCode,
        uint256 bridgeAmount,
        uint256 destinationReserve,
        uint256 destinationSupply
    ) external returns (bool compliant) {
        uint256 newSupply = destinationSupply + bridgeAmount;

        // Validate mint operation
        (bool isValid, bytes32 reasonCode) = complianceGuard.validateMint(
            currencyCode,
            bridgeAmount,
            destinationSupply, // Current supply
            destinationReserve
        );

        if (!isValid) {
            emit ComplianceChecked(address(0), reasonCode, false);
            revert ComplianceViolation(reasonCode);
        }

        // Validate money multiplier = 1.0 after mint
        if (!complianceGuard.validateMoneyMultiplier(destinationReserve, newSupply)) {
            bytes32 multiplierReason = keccak256("MONEY_MULTIPLIER_VIOLATION");
            emit ComplianceChecked(address(0), multiplierReason, false);
            revert ComplianceViolation(multiplierReason);
        }

        // Validate GRU isolation
        if (complianceGuard.violatesGRUIsolation(currencyCode)) {
            bytes32 gruReason = keccak256("GRU_ISOLATION_VIOLATION");
            emit ComplianceChecked(address(0), gruReason, false);
            revert ComplianceViolation(gruReason);
        }

        emit ComplianceChecked(address(0), bytes32(0), true);
        compliant = true;
    }

    /**
     * @notice Enable a W token for compliance checking
     * @param token W token address
     */
    function enableToken(address token) external onlyRole(OPERATOR_ROLE) {
        require(token != address(0), "WTokenComplianceEnforcer: zero token");

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

    /**
     * @notice Disable a W token
     * @param token W token address
     */
    function disableToken(address token) external onlyRole(OPERATOR_ROLE) {
        enabledTokens[token] = false;
        emit TokenEnabled(token, false);
    }

    /**
     * @notice Check if token is enabled
     */
    function isTokenEnabled(address token) external view returns (bool) {
        return enabledTokens[token];
    }
}
