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

import { Test } from "forge-std/Test.sol";
import { BlockOracle } from "src/dispute/BlockOracle.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";

contract BlockOracle_Test is Test {
    BlockOracle oracle;

    function setUp() public {
        oracle = new BlockOracle();
        // Roll the chain forward 255 blocks.
        vm.roll(block.number + 255);
        // Set the time to a realistic date.
        vm.warp(1690906994);
    }

    /// @notice Tests that loading a block hash for a block number within the range of the
    ///         `BLOCKHASH` opcode succeeds.
    function testFuzz_store_succeeds(uint256 _blockNumber) public {
        _blockNumber = bound(_blockNumber, 0, 255);
        oracle.store(_blockNumber);

        BlockOracle.BlockInfo memory res = oracle.load(_blockNumber);
        assertEq(Hash.unwrap(res.hash), blockhash(_blockNumber));
        emit log_uint(block.timestamp - ((block.number - _blockNumber) * 13));
        assertEq(Timestamp.unwrap(res.timestamp), block.timestamp - ((block.number - _blockNumber) * 13));
    }

    /// @notice Tests that loading a block hash for a block number outside the range of the
    ///         `BLOCKHASH` opcode fails.
    function testFuzz_store_oob_reverts(uint256 _blockNumber) public {
        // Fast forward another 256 blocks.
        vm.roll(block.number + 256);
        // Bound the block number to the set { 0, ..., 255 } ∪ { 512, ..., type(uint256).max }
        _blockNumber = _blockNumber % 2 == 0
            ? bound(_blockNumber, 0, 255)
            : bound(_blockNumber, 512, type(uint256).max);

        // Attempt to load the block hash, which should fail.
        vm.expectRevert(BlockNumberOOB.selector);
        oracle.store(_blockNumber);
    }

    /// @notice Tests that the `load` function reverts if the block hash for the given block
    ///         number has not been stored.
    function test_load_noBlockHash_reverts() public {
        vm.expectRevert(BlockHashNotPresent.selector);
        oracle.load(0);
    }
}
