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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**
 * @title Lockbox138
 * @notice Asset lock contract on ChainID 138 (Besu) for trustless bridge deposits
 * @dev Supports both native ETH and ERC-20 tokens. Immutable after deployment (no admin functions).
 */
contract Lockbox138 is ReentrancyGuard {
    using SafeERC20 for IERC20;

    // Replay protection: track nonces per user
    mapping(address => uint256) public nonces;
    
    // Track processed deposit IDs to prevent double deposits
    mapping(uint256 => bool) public processedDeposits;

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

    error ZeroAmount();
    error ZeroRecipient();
    error ZeroAsset();
    error DepositAlreadyProcessed();
    error TransferFailed();

    /**
     * @notice Lock native ETH for cross-chain transfer
     * @param recipient Address on destination chain (Ethereum) to receive funds
     * @param nonce Unique nonce for this deposit (prevents replay attacks)
     * @return depositId Unique identifier for this deposit
     */
    function depositNative(
        address recipient,
        bytes32 nonce
    ) external payable nonReentrant returns (uint256 depositId) {
        if (msg.value == 0) revert ZeroAmount();
        if (recipient == address(0)) revert ZeroRecipient();

        // Increment user nonce
        nonces[msg.sender]++;

        // Generate unique deposit ID
        depositId = _generateDepositId(
            address(0), // Native ETH is represented as address(0)
            msg.value,
            recipient,
            nonce
        );

        // Replay protection: ensure deposit ID hasn't been used
        if (processedDeposits[depositId]) revert DepositAlreadyProcessed();
        processedDeposits[depositId] = true;

        emit Deposit(
            depositId,
            address(0), // address(0) represents native ETH
            msg.value,
            recipient,
            nonce,
            msg.sender,
            block.timestamp
        );

        return depositId;
    }

    /**
     * @notice Lock ERC-20 tokens (e.g., WETH) for cross-chain transfer
     * @param token ERC-20 token address to lock
     * @param amount Amount of tokens to lock
     * @param recipient Address on destination chain (Ethereum) to receive funds
     * @param nonce Unique nonce for this deposit (prevents replay attacks)
     * @return depositId Unique identifier for this deposit
     */
    function depositERC20(
        address token,
        uint256 amount,
        address recipient,
        bytes32 nonce
    ) external nonReentrant returns (uint256 depositId) {
        if (token == address(0)) revert ZeroAsset();
        if (amount == 0) revert ZeroAmount();
        if (recipient == address(0)) revert ZeroRecipient();

        // Increment user nonce
        nonces[msg.sender]++;

        // Generate unique deposit ID
        depositId = _generateDepositId(token, amount, recipient, nonce);

        // Replay protection: ensure deposit ID hasn't been used
        if (processedDeposits[depositId]) revert DepositAlreadyProcessed();
        processedDeposits[depositId] = true;

        // Transfer tokens from user to this contract
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);

        emit Deposit(
            depositId,
            token,
            amount,
            recipient,
            nonce,
            msg.sender,
            block.timestamp
        );

        return depositId;
    }

    /**
     * @notice Get current nonce for a user
     * @param user Address to check nonce for
     * @return Current nonce value
     */
    function getNonce(address user) external view returns (uint256) {
        return nonces[user];
    }

    /**
     * @notice Check if a deposit ID has been processed
     * @param depositId Deposit ID to check
     * @return True if deposit has been processed
     */
    function isDepositProcessed(uint256 depositId) external view returns (bool) {
        return processedDeposits[depositId];
    }

    /**
     * @notice Generate a unique deposit ID from deposit parameters
     * @dev Uses keccak256 hash of all deposit parameters + sender + timestamp to ensure uniqueness
     * @param asset Asset address (address(0) for native ETH)
     * @param amount Deposit amount
     * @param recipient Recipient address on destination chain
     * @param nonce User-provided nonce
     * @return depositId Unique deposit identifier
     */
    function _generateDepositId(
        address asset,
        uint256 amount,
        address recipient,
        bytes32 nonce
    ) internal view returns (uint256) {
        return uint256(
            keccak256(
                abi.encodePacked(
                    asset,
                    amount,
                    recipient,
                    nonce,
                    msg.sender,
                    block.timestamp,
                    block.number
                )
            )
        );
    }
}
