// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface IPayVault {
    function pay(bytes32 findingId, address reporter, uint256 amount) external;
    function fund(uint256 amount) external;
}

/// TEST-ONLY mock (local hardhat, NEVER deploy to chain 2800): an ERC20 whose
/// transfer() re-enters AereBugBountyVault.pay() (or fund()) exactly once, using
/// try/catch so the outer legitimate call can still complete. Lets the fuzz suite
/// prove the ReentrancyGuard on pay()/fund() blocks a token-callback re-entry
/// drain. A plain ERC20 can never exercise this path.
contract MockReentrantBountyToken is ERC20 {
    address public vault;         // AereBugBountyVault under test
    bool public armed;            // when true, transfer() re-enters once
    bool public reenterFund;      // re-enter fund() instead of pay() when true
    bool public reentryAttempted;
    bool public reentryReverted;
    string public lastRevert;

    constructor() ERC20("Reentrant WAERE", "rWAERE") {}

    function mint(address to, uint256 amt) external { _mint(to, amt); }

    function setVault(address v) external { vault = v; }
    function arm(bool on, bool useFund) external { armed = on; reenterFund = useFund; }

    /// Outer entrypoints so this token can be set AS the vault's triager, letting
    /// the nested re-entry pass onlyTriager and actually reach the ReentrancyGuard.
    function triggerPay(bytes32 findingId, address reporter, uint256 amount) external {
        IPayVault(vault).pay(findingId, reporter, amount);
    }
    function triggerFund(uint256 amount) external {
        // vault.fund does transferFrom(msg.sender=this, ...); ensure allowance is set by test.
        IPayVault(vault).fund(amount);
    }

    function _maybeReenter() internal {
        if (!armed || vault == address(0)) return;
        armed = false; // one-shot so we do not recurse forever
        reentryAttempted = true;
        if (reenterFund) {
            try IPayVault(vault).fund(1) {
                reentryReverted = false;
            } catch Error(string memory reason) {
                reentryReverted = true;
                lastRevert = reason;
            } catch {
                reentryReverted = true;
                lastRevert = "unknown";
            }
        } else {
            try IPayVault(vault).pay(bytes32("REENTRY"), address(0xBEEF), 1) {
                reentryReverted = false;
            } catch Error(string memory reason) {
                reentryReverted = true;
                lastRevert = reason;
            } catch {
                reentryReverted = true;
                lastRevert = "unknown";
            }
        }
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        _maybeReenter();
        return super.transfer(to, amount);
    }

    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        _maybeReenter();
        return super.transferFrom(from, to, amount);
    }
}
