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

import "forge-std/Test.sol";
import "../../../contracts/bridge/trustless/Lockbox138.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
    constructor() ERC20("Mock Token", "MOCK") {
        // Mint to deployer, but we'll mint to test users as needed
    }
    
    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }
}

contract Lockbox138Test is Test {
    Lockbox138 public lockbox;
    MockERC20 public mockToken;
    address public user = address(0x1);
    address public recipient = address(0x2);

    event Deposit(
        uint256 indexed depositId,
        address indexed asset,
        uint256 amount,
        address indexed recipient,
        bytes32 nonce,
        address depositor,
        uint256 timestamp
    );

    function setUp() public {
        lockbox = new Lockbox138();
        mockToken = new MockERC20();
        vm.deal(user, 100 ether);
    }

    function testDepositNative() public {
        uint256 amount = 1 ether;
        bytes32 nonce = keccak256("test-nonce");

        vm.prank(user);
        uint256 depositId = lockbox.depositNative{value: amount}(recipient, nonce);

        assertGt(depositId, 0);
        assertEq(address(lockbox).balance, amount);
        assertTrue(lockbox.isDepositProcessed(depositId));
        assertEq(lockbox.getNonce(user), 1);
    }

    function testDepositERC20() public {
        uint256 amount = 100 ether;
        bytes32 nonce = keccak256("test-nonce-erc20");

        // Mint tokens to user first
        mockToken.mint(user, amount);

        vm.startPrank(user);
        mockToken.approve(address(lockbox), amount);
        uint256 depositId = lockbox.depositERC20(address(mockToken), amount, recipient, nonce);
        vm.stopPrank();

        assertGt(depositId, 0);
        assertEq(mockToken.balanceOf(address(lockbox)), amount);
        assertTrue(lockbox.isDepositProcessed(depositId));
        assertEq(lockbox.getNonce(user), 1);
    }

    function testDepositNativeEmitsEvent() public {
        uint256 amount = 1 ether;
        bytes32 nonce = keccak256("test-nonce");

        vm.prank(user);
        // Check only non-indexed data fields (amount, nonce, depositor, timestamp)
        // Skip indexed fields (depositId, asset, recipient) since depositId is unpredictable
        vm.expectEmit(false, false, false, true); // Only check data, skip all topics
        emit Deposit(
            uint256(0), // Ignored (topic)
            address(0), // Ignored (topic)
            amount,     // Checked (data)
            recipient,  // Ignored (topic)
            nonce,      // Checked (data)
            user,       // Checked (data)
            block.timestamp // Checked (data)
        );
        lockbox.depositNative{value: amount}(recipient, nonce);
    }

    function testDepositNativeZeroAmount() public {
        bytes32 nonce = keccak256("test-nonce");

        vm.prank(user);
        vm.expectRevert(Lockbox138.ZeroAmount.selector);
        lockbox.depositNative{value: 0}(recipient, nonce);
    }

    function testDepositNativeZeroRecipient() public {
        uint256 amount = 1 ether;
        bytes32 nonce = keccak256("test-nonce");

        vm.prank(user);
        vm.expectRevert(Lockbox138.ZeroRecipient.selector);
        lockbox.depositNative{value: amount}(address(0), nonce);
    }

    function testDepositERC20ZeroAmount() public {
        bytes32 nonce = keccak256("test-nonce");

        vm.startPrank(user);
        vm.expectRevert(Lockbox138.ZeroAmount.selector);
        lockbox.depositERC20(address(mockToken), 0, recipient, nonce);
        vm.stopPrank();
    }

    function testDepositERC20ZeroAsset() public {
        uint256 amount = 100 ether;
        bytes32 nonce = keccak256("test-nonce");

        vm.prank(user);
        vm.expectRevert(Lockbox138.ZeroAsset.selector);
        lockbox.depositERC20(address(0), amount, recipient, nonce);
    }

    function testDepositNativeReplayProtection() public {
        uint256 amount = 1 ether;
        bytes32 nonce = keccak256("test-nonce");

        vm.startPrank(user);
        uint256 depositId = lockbox.depositNative{value: amount}(recipient, nonce);
        
        // Try to deposit again with same parameters (should fail due to nonce increment)
        // Actually, the depositId is different each time due to timestamp/block.number
        // But the nonce increments, so this is more of a user-side protection
        vm.stopPrank();
        
        assertTrue(lockbox.isDepositProcessed(depositId));
    }

    function testMultipleDepositsIncrementNonce() public {
        bytes32 nonce1 = keccak256("nonce-1");
        bytes32 nonce2 = keccak256("nonce-2");

        vm.startPrank(user);
        lockbox.depositNative{value: 1 ether}(recipient, nonce1);
        assertEq(lockbox.getNonce(user), 1);

        lockbox.depositNative{value: 1 ether}(recipient, nonce2);
        assertEq(lockbox.getNonce(user), 2);
        vm.stopPrank();
    }
}
