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

import {Test} from "forge-std/Test.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {Chain138MainnetCheckpoint} from "../../contracts/mainnet-checkpoint/Chain138MainnetCheckpoint.sol";
import {CheckpointStorage} from "../../contracts/mainnet-checkpoint/storage/CheckpointStorage.sol";
import {CheckpointLeaf} from "../../contracts/mainnet-checkpoint/libraries/CheckpointLeaf.sol";
import {CheckpointFlags} from "../../contracts/mainnet-checkpoint/libraries/CheckpointFlags.sol";
import {CheckpointErrors} from "../../contracts/mainnet-checkpoint/libraries/CheckpointErrors.sol";

contract Chain138MainnetCheckpointTest is Test {
    Chain138MainnetCheckpoint checkpoint;
    address admin = address(0xA11CE);
    address submitter = address(0xB0B);

    function setUp() public {
        Chain138MainnetCheckpoint impl = new Chain138MainnetCheckpoint();
        bytes memory initData = abi.encodeCall(
            Chain138MainnetCheckpoint.initialize,
            (admin, address(0xC0C0C0C0), uint64(1380001), address(0))
        );
        checkpoint = Chain138MainnetCheckpoint(address(new ERC1967Proxy(address(impl), initData)));
        vm.startPrank(admin);
        checkpoint.grantRole(checkpoint.SUBMITTER_ROLE(), submitter);
        vm.stopPrank();
    }

    function _leaf(bytes32 txHash) internal pure returns (CheckpointLeaf.PaymentLeafV1 memory) {
        return CheckpointLeaf.PaymentLeafV1({
            txHash: txHash,
            from: address(1),
            to: address(2),
            value: 1 ether,
            blockNumber: 100,
            blockTimestamp: 1,
            gasUsed: 21000,
            success: true
        });
    }

    function _buildRoot(CheckpointLeaf.PaymentLeafV1[] memory leaves) internal pure returns (bytes32) {
        require(leaves.length == 1, "single leaf test");
        return CheckpointLeaf.paymentLeafV1(138, leaves[0]);
    }

    function testSubmitCheckpointUpdatesLatest() public {
        CheckpointLeaf.PaymentLeafV1[] memory leaves = new CheckpointLeaf.PaymentLeafV1[](1);
        leaves[0] = _leaf(keccak256("tx1"));
        bytes32 root = _buildRoot(leaves);

        CheckpointStorage.CheckpointHeader memory header = CheckpointStorage.CheckpointHeader({
            batchId: 1,
            previousBatchId: 0,
            chainId: 138,
            checkpointBlock: 100,
            startBlock: 99,
            endBlock: 100,
            blockHash: keccak256("bh"),
            stateRoot: keccak256("sr"),
            paymentsRoot: root,
            receiptsRoot: bytes32(0),
            txCount: 1,
            flags: CheckpointFlags.PARTIAL_BATCH,
            submittedAt: 0,
            submitter: address(0),
            contentURI: bytes32(0)
        });

        bytes32[] memory hashes = new bytes32[](1);
        hashes[0] = leaves[0].txHash;

        vm.prank(submitter);
        checkpoint.submitCheckpoint(header, hex"01", hashes, abi.encode(leaves));

        assertEq(checkpoint.getLatestBatchId(), 1);
        assertEq(checkpoint.getLatestCheckpoint().paymentsRoot, root);
        (bool inc,) = checkpoint.isTxIncluded(leaves[0].txHash);
        assertTrue(inc);
    }

    function testPauseBlocksSubmit() public {
        vm.prank(admin);
        checkpoint.pause();
        CheckpointStorage.CheckpointHeader memory header;
        vm.prank(submitter);
        vm.expectRevert(CheckpointErrors.Paused.selector);
        checkpoint.submitCheckpoint(header, hex"01", new bytes32[](0), "");
    }
}
