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

import {Test, console} from "forge-std/Test.sol";
import "../../../contracts/bridge/trustless/BondManager.sol";
import "../../../contracts/bridge/trustless/ChallengeManager.sol";
import "../../../contracts/bridge/trustless/InboxETH.sol";
import "../../../contracts/bridge/trustless/LiquidityPoolETH.sol";

/**
 * @title BatchOperationsTest
 * @notice Test suite for batch processing operations
 */
contract BatchOperationsTest is Test {
    BondManager public bondManager;
    ChallengeManager public challengeManager;
    InboxETH public inbox;
    LiquidityPoolETH public liquidityPool;
    
    // Make contract payable to receive ETH from bond releases
    receive() external payable {}

    address public constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    address public relayer = address(0x1111);
    address public recipient = address(0x2222);

    uint256 public constant BOND_MULTIPLIER = 11000;
    uint256 public constant MIN_BOND = 1 ether;
    uint256 public constant CHALLENGE_WINDOW = 30 minutes;

    function setUp() public {
        bondManager = new BondManager(BOND_MULTIPLIER, MIN_BOND);
        challengeManager = new ChallengeManager(address(bondManager), CHALLENGE_WINDOW);
        liquidityPool = new LiquidityPoolETH(WETH, 5, 11000);
        inbox = new InboxETH(address(bondManager), address(challengeManager), address(liquidityPool));

        liquidityPool.authorizeRelease(address(inbox));

        vm.deal(relayer, 100 ether);
        
        // Set initial timestamp to avoid cooldown issues with uninitialized lastClaimTime
        vm.warp(1000);
    }

    function test_BatchClaimSubmission() public {
        uint256[] memory depositIds = new uint256[](3);
        address[] memory assets = new address[](3);
        uint256[] memory amounts = new uint256[](3);
        address[] memory recipients = new address[](3);
        bytes[] memory proofs = new bytes[](3);

        for (uint256 i = 0; i < 3; i++) {
            depositIds[i] = 1000 + i;
            assets[i] = address(0); // ETH
            amounts[i] = 1 ether;
            recipients[i] = recipient;
            proofs[i] = "";
        }

        uint256 totalBond = bondManager.getRequiredBond(1 ether) * 3;
        
        // Advance time to ensure no cooldown issues
        vm.warp(block.timestamp + 1);
        vm.prank(relayer);
        uint256 bondAmount = inbox.submitClaimsBatch{value: totalBond}(
            depositIds,
            assets,
            amounts,
            recipients,
            proofs
        );

        assertEq(bondAmount, totalBond, "Total bond should match");
        
        // Verify all claims registered
        for (uint256 i = 0; i < 3; i++) {
            ChallengeManager.Claim memory claim = challengeManager.getClaim(depositIds[i]);
            assertEq(claim.depositId, depositIds[i], "Claim should be registered");
        }
    }

    function test_BatchFinalization() public {
        // Submit claims first - start from timestamp 1000, first claim at 1001
        uint256 currentTime = 1001;
        for (uint256 i = 0; i < 3; i++) {
            vm.deal(relayer, 100 ether);
            // Advance time to respect cooldown period (61 seconds between claims)
            vm.warp(currentTime);
            vm.prank(relayer);
            inbox.submitClaim{value: bondManager.getRequiredBond(1 ether)}(
                2000 + i,
                address(0),
                1 ether,
                recipient,
                ""
            );
            currentTime += 61 seconds;
        }

        // Wait for challenge window
        vm.warp(block.timestamp + CHALLENGE_WINDOW + 1);

        // Finalize in batch
        uint256[] memory depositIds = new uint256[](3);
        for (uint256 i = 0; i < 3; i++) {
            depositIds[i] = 2000 + i;
        }

        challengeManager.finalizeClaimsBatch(depositIds);

        // Verify all finalized
        for (uint256 i = 0; i < 3; i++) {
            ChallengeManager.Claim memory claim = challengeManager.getClaim(depositIds[i]);
            assertTrue(claim.finalized, "Claim should be finalized");
        }
    }

    function test_BatchBondRelease() public {
        // Submit and finalize claims - start from timestamp 1000, first claim at 1001
        uint256 currentTime = 1001;
        for (uint256 i = 0; i < 3; i++) {
            vm.deal(relayer, 100 ether);
            // Advance time to respect cooldown period (61 seconds between claims)
            vm.warp(currentTime);
            vm.prank(relayer);
            inbox.submitClaim{value: bondManager.getRequiredBond(1 ether)}(
                3000 + i,
                address(0),
                1 ether,
                recipient,
                ""
            );
            currentTime += 61 seconds;
        }

        vm.warp(block.timestamp + CHALLENGE_WINDOW + 1);

        uint256[] memory depositIds = new uint256[](3);
        for (uint256 i = 0; i < 3; i++) {
            depositIds[i] = 3000 + i;
            challengeManager.finalizeClaim(depositIds[i]);
        }

        // Release bonds in batch
        // Note: Due to vm.prank behavior, the relayer stored might be the test contract
        // The test contract needs to be able to receive ETH for this to work
        uint256 totalReleased = bondManager.releaseBondsBatch(depositIds);

        assertGt(totalReleased, 0, "Should release bonds");
        
        // Verify bonds released
        for (uint256 i = 0; i < 3; i++) {
            (address bondRelayer, , bool slashed, bool released) = bondManager.getBond(depositIds[i]);
            assertTrue(released, "Bond should be released");
            assertFalse(slashed, "Bond should not be slashed");
        }
    }

    function test_BatchTooLarge() public {
        uint256[] memory depositIds = new uint256[](51);
        address[] memory assets = new address[](51);
        uint256[] memory amounts = new uint256[](51);
        address[] memory recipients = new address[](51);
        bytes[] memory proofs = new bytes[](51);

        for (uint256 i = 0; i < 51; i++) {
            depositIds[i] = 4000 + i;
            assets[i] = address(0);
            amounts[i] = 1 ether;
            recipients[i] = recipient;
            proofs[i] = "";
        }

        vm.prank(relayer);
        vm.expectRevert("InboxETH: batch too large");
        inbox.submitClaimsBatch{value: 100 ether}(
            depositIds,
            assets,
            amounts,
            recipients,
            proofs
        );
    }
}

