//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import { OptimistInviter } from "../../universal/op-nft/OptimistInviter.sol";

/**
 * Simple helper contract that helps with testing flow and signature for OptimistInviter contract.
 * Made this a separate contract instead of including in OptimistInviter.t.sol for reusability.
 */
contract OptimistInviterHelper {
    /**
     * @notice EIP712 typehash for the ClaimableInvite type.
     */
    bytes32 public constant CLAIMABLE_INVITE_TYPEHASH =
        keccak256("ClaimableInvite(address issuer,bytes32 nonce)");

    /**
     * @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
     */
    bytes32 public constant EIP712_DOMAIN_TYPEHASH =
        keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );

    /**
     * @notice Address of OptimistInviter contract we are testing.
     */
    OptimistInviter public optimistInviter;

    /**
     * @notice OptimistInviter contract name. Used to construct the EIP-712 domain.
     */
    string public name;

    /**
     * @notice Keeps track of current nonce to generate new nonces for each invite.
     */
    uint256 public currentNonce;

    constructor(OptimistInviter _optimistInviter, string memory _name) {
        optimistInviter = _optimistInviter;
        name = _name;
    }

    /**
     * @notice Returns the hash of the struct ClaimableInvite.
     *
     * @param _claimableInvite ClaimableInvite struct to hash.
     *
     * @return EIP-712 typed struct hash.
     */
    function getClaimableInviteStructHash(OptimistInviter.ClaimableInvite memory _claimableInvite)
        public
        pure
        returns (bytes32)
    {
        return
            keccak256(
                abi.encode(
                    CLAIMABLE_INVITE_TYPEHASH,
                    _claimableInvite.issuer,
                    _claimableInvite.nonce
                )
            );
    }

    /**
     * @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
     *         pseudorandom nonces.
     *
     * @return Nonce that should be used as part of ClaimableInvite.
     */
    function consumeNonce() public returns (bytes32) {
        return bytes32(keccak256(abi.encode(currentNonce++)));
    }

    /**
     * @notice Returns a ClaimableInvite with the issuer and current nonce.
     *
     * @param _issuer Issuer to include in the ClaimableInvite.
     *
     * @return ClaimableInvite that can be hashed & signed.
     */
    function getClaimableInviteWithNewNonce(address _issuer)
        public
        returns (OptimistInviter.ClaimableInvite memory)
    {
        return OptimistInviter.ClaimableInvite(_issuer, consumeNonce());
    }

    /**
     * @notice Computes the EIP712 digest with default correct parameters.
     *
     * @param _claimableInvite ClaimableInvite struct to hash.
     *
     * @return EIP-712 compatible digest.
     */
    function getDigest(OptimistInviter.ClaimableInvite calldata _claimableInvite)
        public
        view
        returns (bytes32)
    {
        return
            getDigestWithEIP712Domain(
                _claimableInvite,
                bytes(name),
                bytes(optimistInviter.EIP712_VERSION()),
                block.chainid,
                address(optimistInviter)
            );
    }

    /**
     * @notice Computes the EIP712 digest with the given domain parameters.
     *         Used for testing that different domain parameters fail.
     *
     * @param _claimableInvite   ClaimableInvite struct to hash.
     * @param _name              Contract name to use in the EIP712 domain.
     * @param _version           Contract version to use in the EIP712 domain.
     * @param _chainid           Chain ID to use in the EIP712 domain.
     * @param _verifyingContract Address to use in the EIP712 domain.
     *
     * @return EIP-712 compatible digest.
     */
    function getDigestWithEIP712Domain(
        OptimistInviter.ClaimableInvite calldata _claimableInvite,
        bytes memory _name,
        bytes memory _version,
        uint256 _chainid,
        address _verifyingContract
    ) public pure returns (bytes32) {
        bytes32 domainSeparator = keccak256(
            abi.encode(
                EIP712_DOMAIN_TYPEHASH,
                keccak256(_name),
                keccak256(_version),
                _chainid,
                _verifyingContract
            )
        );
        return
            ECDSA.toTypedDataHash(domainSeparator, getClaimableInviteStructHash(_claimableInvite));
    }
}
