// 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 {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
import {CheckpointHubConfig} from "../../contracts/mainnet-checkpoint/libraries/CheckpointHubConfig.sol";

contract MockCcipRouter {
    function deliver(address receiver, IRouterClient.Any2EVMMessage calldata message) external {
        Chain138MainnetCheckpoint(receiver).ccipReceive(message);
    }
}

contract CcipReceiveTest is Test {
    Chain138MainnetCheckpoint hub;
    MockCcipRouter router;
    address emitter = address(0xE01);
    address admin = address(0xA11CE);

    function setUp() public {
        router = new MockCcipRouter();
        Chain138MainnetCheckpoint impl = new Chain138MainnetCheckpoint();
        bytes memory initData = abi.encodeCall(
            Chain138MainnetCheckpoint.initialize,
            (admin, address(router), uint64(138_0001), emitter)
        );
        hub = Chain138MainnetCheckpoint(address(new ERC1967Proxy(address(impl), initData)));
        vm.startPrank(admin);
        CheckpointHubConfig.HubConfig memory cfg = CheckpointHubConfig.mainnetDefaults();
        cfg.enforcePreviousBatchId = false;
        cfg.requireValidatorSigs = false;
        cfg.allowCCIPIngress = true;
        cfg.ccipRouter = address(router);
        cfg.batchEmitterOnSource = emitter;
        hub.applyConfig(cfg);
        vm.stopPrank();
    }

    function testCcipReceiveSubmitsBatch() public {
        CheckpointLeaf.PaymentLeafV1 memory leaf = CheckpointLeaf.PaymentLeafV1({
            txHash: keccak256("ccip-tx"),
            from: address(1),
            to: address(2),
            value: 1 ether,
            blockNumber: 50,
            blockTimestamp: 1,
            gasUsed: 21000,
            success: true
        });
        bytes32 root = CheckpointLeaf.paymentLeafV1(138, leaf);
        CheckpointStorage.CheckpointHeader memory header = CheckpointStorage.CheckpointHeader({
            batchId: 1,
            previousBatchId: 0,
            chainId: 138,
            checkpointBlock: 50,
            startBlock: 50,
            endBlock: 50,
            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] = leaf.txHash;
        bytes memory payload = abi.encode(header, hex"01", hashes, bytes32(0), abi.encode(leaf));

        IRouterClient.Any2EVMMessage memory message = IRouterClient.Any2EVMMessage({
            messageId: keccak256("mid"),
            sourceChainSelector: 138_0001,
            sender: abi.encode(emitter),
            data: payload,
            tokenAmounts: new IRouterClient.TokenAmount[](0)
        });

        router.deliver(address(hub), message);
        assertEq(hub.getLatestBatchId(), 1);
    }
}
