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

/**
 * @title FraudProofTypes
 * @notice Library for encoding/decoding fraud proof data
 * @dev Defines structures and encoding for different fraud proof types
 */
library FraudProofTypes {
    /**
     * @notice Fraud proof for non-existent deposit
     * @dev Contains Merkle proof showing deposit doesn't exist in source chain state
     */
    struct NonExistentDepositProof {
        bytes32 stateRoot;           // State root from source chain block
        bytes32 depositHash;         // Hash of deposit data that should exist
        bytes32[] merkleProof;       // Merkle proof path
        bytes32 leftSibling;         // Left sibling for non-existence proof
        bytes32 rightSibling;        // Right sibling for non-existence proof
        bytes blockHeader;           // Block header from source chain
        uint256 blockNumber;         // Block number
    }

    /**
     * @notice Fraud proof for incorrect amount
     * @dev Contains proof showing actual deposit amount differs from claimed amount
     */
    struct IncorrectAmountProof {
        bytes32 stateRoot;           // State root from source chain block
        bytes32 depositHash;         // Hash of actual deposit data
        bytes32[] merkleProof;      // Merkle proof for actual deposit
        uint256 actualAmount;        // Actual deposit amount from source chain
        bytes blockHeader;           // Block header from source chain
        uint256 blockNumber;         // Block number
    }

    /**
     * @notice Fraud proof for incorrect recipient
     * @dev Contains proof showing actual recipient differs from claimed recipient
     */
    struct IncorrectRecipientProof {
        bytes32 stateRoot;           // State root from source chain block
        bytes32 depositHash;         // Hash of actual deposit data
        bytes32[] merkleProof;      // Merkle proof for actual deposit
        address actualRecipient;     // Actual recipient from source chain
        bytes blockHeader;           // Block header from source chain
        uint256 blockNumber;         // Block number
    }

    /**
     * @notice Fraud proof for double spend
     * @dev Contains proof showing deposit was already claimed in another claim
     */
    struct DoubleSpendProof {
        uint256 previousClaimId;    // Deposit ID of previous claim
        bytes32 previousClaimHash;   // Hash of previous claim
        bytes32[] merkleProof;      // Merkle proof for previous claim
        bytes blockHeader;           // Block header from source chain
        uint256 blockNumber;         // Block number
    }

    /**
     * @notice Encode NonExistentDepositProof to bytes
     */
    function encodeNonExistentDeposit(NonExistentDepositProof memory proof)
        internal
        pure
        returns (bytes memory)
    {
        return abi.encode(
            proof.stateRoot,
            proof.depositHash,
            proof.merkleProof,
            proof.leftSibling,
            proof.rightSibling,
            proof.blockHeader,
            proof.blockNumber
        );
    }

    /**
     * @notice Decode bytes to NonExistentDepositProof
     */
    function decodeNonExistentDeposit(bytes memory data)
        internal
        pure
        returns (NonExistentDepositProof memory)
    {
        (
            bytes32 stateRoot,
            bytes32 depositHash,
            bytes32[] memory merkleProof,
            bytes32 leftSibling,
            bytes32 rightSibling,
            bytes memory blockHeader,
            uint256 blockNumber
        ) = abi.decode(data, (bytes32, bytes32, bytes32[], bytes32, bytes32, bytes, uint256));

        return NonExistentDepositProof({
            stateRoot: stateRoot,
            depositHash: depositHash,
            merkleProof: merkleProof,
            leftSibling: leftSibling,
            rightSibling: rightSibling,
            blockHeader: blockHeader,
            blockNumber: blockNumber
        });
    }

    /**
     * @notice Encode IncorrectAmountProof to bytes
     */
    function encodeIncorrectAmount(IncorrectAmountProof memory proof)
        internal
        pure
        returns (bytes memory)
    {
        return abi.encode(
            proof.stateRoot,
            proof.depositHash,
            proof.merkleProof,
            proof.actualAmount,
            proof.blockHeader,
            proof.blockNumber
        );
    }

    /**
     * @notice Decode bytes to IncorrectAmountProof
     */
    function decodeIncorrectAmount(bytes memory data)
        internal
        pure
        returns (IncorrectAmountProof memory)
    {
        (
            bytes32 stateRoot,
            bytes32 depositHash,
            bytes32[] memory merkleProof,
            uint256 actualAmount,
            bytes memory blockHeader,
            uint256 blockNumber
        ) = abi.decode(data, (bytes32, bytes32, bytes32[], uint256, bytes, uint256));

        return IncorrectAmountProof({
            stateRoot: stateRoot,
            depositHash: depositHash,
            merkleProof: merkleProof,
            actualAmount: actualAmount,
            blockHeader: blockHeader,
            blockNumber: blockNumber
        });
    }

    /**
     * @notice Encode IncorrectRecipientProof to bytes
     */
    function encodeIncorrectRecipient(IncorrectRecipientProof memory proof)
        internal
        pure
        returns (bytes memory)
    {
        return abi.encode(
            proof.stateRoot,
            proof.depositHash,
            proof.merkleProof,
            proof.actualRecipient,
            proof.blockHeader,
            proof.blockNumber
        );
    }

    /**
     * @notice Decode bytes to IncorrectRecipientProof
     */
    function decodeIncorrectRecipient(bytes memory data)
        internal
        pure
        returns (IncorrectRecipientProof memory)
    {
        (
            bytes32 stateRoot,
            bytes32 depositHash,
            bytes32[] memory merkleProof,
            address actualRecipient,
            bytes memory blockHeader,
            uint256 blockNumber
        ) = abi.decode(data, (bytes32, bytes32, bytes32[], address, bytes, uint256));

        return IncorrectRecipientProof({
            stateRoot: stateRoot,
            depositHash: depositHash,
            merkleProof: merkleProof,
            actualRecipient: actualRecipient,
            blockHeader: blockHeader,
            blockNumber: blockNumber
        });
    }

    /**
     * @notice Encode DoubleSpendProof to bytes
     */
    function encodeDoubleSpend(DoubleSpendProof memory proof)
        internal
        pure
        returns (bytes memory)
    {
        return abi.encode(
            proof.previousClaimId,
            proof.previousClaimHash,
            proof.merkleProof,
            proof.blockHeader,
            proof.blockNumber
        );
    }

    /**
     * @notice Decode bytes to DoubleSpendProof
     */
    function decodeDoubleSpend(bytes memory data)
        internal
        pure
        returns (DoubleSpendProof memory)
    {
        (
            uint256 previousClaimId,
            bytes32 previousClaimHash,
            bytes32[] memory merkleProof,
            bytes memory blockHeader,
            uint256 blockNumber
        ) = abi.decode(data, (uint256, bytes32, bytes32[], bytes, uint256));

        return DoubleSpendProof({
            previousClaimId: previousClaimId,
            previousClaimHash: previousClaimHash,
            merkleProof: merkleProof,
            blockHeader: blockHeader,
            blockNumber: blockNumber
        });
    }
}

