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

// Import ONLY the V2 history anchor. It transitively imports the V1 history
// anchor (and its single IAereStorageProofVerifier declaration), so this
// compilation closure sees exactly one copy of that interface. It deliberately
// does NOT import AereStateRootAnchor.sol, which declares a same-named interface
// and would collide.
import "../AereHistoryStateRootAnchorV2.sol";

/**
 * @dev Test-only subclass of the ORIGINAL history anchor (V1) that injects a
 *      canonical block hash for a block number, standing in for the EIP-2935
 *      ring buffer without a live fork. Everything else is the production V1
 *      logic, including the redundant double verification in proveAgainstAnchor.
 */
contract MockHistoryAnchorV1 is AereHistoryStateRootAnchor {
    mapping(uint256 => bytes32) public injected;

    constructor(address verifier) AereHistoryStateRootAnchor(verifier) {}

    function setHistoryBlockHash(uint256 blockNumber, bytes32 h) external {
        injected[blockNumber] = h;
    }

    function _historyBlockHash(uint256 blockNumber) internal view override returns (bytes32) {
        bytes32 h = injected[blockNumber];
        if (h == bytes32(0)) revert OutOfHistoryWindow(blockNumber, block.number);
        return h;
    }
}

/**
 * @dev Test-only subclass of the corrected history anchor (V2) with the same
 *      injectable canonical block hash. proveAgainstAnchor runs the single
 *      corrected path.
 */
contract MockHistoryAnchorV2 is AereHistoryStateRootAnchorV2 {
    mapping(uint256 => bytes32) public injected;

    constructor(address verifier) AereHistoryStateRootAnchorV2(verifier) {}

    function setHistoryBlockHash(uint256 blockNumber, bytes32 h) external {
        injected[blockNumber] = h;
    }

    function _historyBlockHash(uint256 blockNumber) internal view override returns (bytes32) {
        bytes32 h = injected[blockNumber];
        if (h == bytes32(0)) revert OutOfHistoryWindow(blockNumber, block.number);
        return h;
    }
}

/**
 * @dev Counting stand-in for the LIVE AereStorageProofVerifier used to prove
 *      how many SP1 verifications a caller triggers.
 *
 *      submitProofWithExpectedRoot is the ONE verification the corrected anchor
 *      must run: it validates, binds the committed root to the expected root,
 *      records, and increments `submitCalls`.
 *
 *      verify() reverts with RedundantVerifyCall. The live verify() is a
 *      stateless view whose whole cost is the SP1 Groth16 pairing; the original
 *      anchor calls it purely to read the block number, which the corrected
 *      anchor derives locally. Making verify() revert here means any code path
 *      that still calls it (the V1 double-verify) fails loudly, while the
 *      corrected path (which never calls verify) is unaffected. `submitCalls`
 *      then witnesses that exactly one verification ran.
 */
contract CountingStorageProofVerifier is IAereStorageProofVerifier {
    bool public shouldValidate = true;
    uint256 public submitCalls;

    /// @notice keccak256(publicValues) => recorded, for assertions.
    mapping(bytes32 => bool) public recorded;

    error RedundantVerifyCall();

    function setValidate(bool v) external {
        shouldValidate = v;
    }

    function verify(bytes calldata, bytes calldata)
        external
        pure
        returns (uint256, bytes32, address, bytes32, bytes32)
    {
        revert RedundantVerifyCall();
    }

    function submitProofWithExpectedRoot(
        bytes calldata publicValues,
        bytes calldata /*proof*/,
        bytes32 expectedStateRoot
    ) external returns (bytes32 value) {
        require(shouldValidate, "InvalidProof");
        require(publicValues.length == 192, "BadPublicValuesLength");
        (, , bytes32 stateRoot, , , bytes32 v) = abi.decode(
            publicValues,
            (uint256, uint256, bytes32, address, bytes32, bytes32)
        );
        require(stateRoot == expectedStateRoot, "StateRootMismatch");
        recorded[keccak256(publicValues)] = true;
        submitCalls += 1;
        return v;
    }
}
