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

import "../AereStateRootAnchor.sol";

/**
 * @dev Test-only subclass that lets a test inject a canonical block hash for a
 *      given block number. This is used to exercise the FULL anchor logic
 *      (keccak match + RLP state-root decode + record) against a REAL AERE
 *      canonical block (the genesis block) whose hash is outside the live
 *      256-block BLOCKHASH window. Production always uses the real BLOCKHASH
 *      opcode via the un-overridden AereStateRootAnchor._blockHashOf.
 */
contract MockBlockhashAnchor is AereStateRootAnchor {
    mapping(uint256 => bytes32) public injected;

    constructor(address verifier) AereStateRootAnchor(verifier) {}

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

    function _blockHashOf(uint256 blockNumber) internal view override returns (bytes32) {
        return injected[blockNumber];
    }
}

/**
 * @dev Faithful stand-in for the LIVE AereStorageProofVerifier's two entry
 *      points used by the anchor. It decodes the SAME 192-byte public-values
 *      convention and enforces the SAME expected-root check, so the
 *      proveAgainstAnchor end-to-end path is exercised exactly as it would be
 *      against the real verifier (whose SP1 pairing check we do not re-run in a
 *      unit test). `shouldValidate=false` simulates an invalid SP1 proof.
 */
contract MockStorageProofVerifier is IAereStorageProofVerifier {
    bool public shouldValidate = true;

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

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

    function verify(bytes calldata publicValues, bytes calldata /*proof*/)
        external
        view
        returns (
            uint256 blockNumber,
            bytes32 stateRoot,
            address account,
            bytes32 slot,
            bytes32 value
        )
    {
        require(shouldValidate, "InvalidProof");
        require(publicValues.length == 192, "BadPublicValuesLength");
        (, blockNumber, stateRoot, account, slot, value) = abi.decode(
            publicValues,
            (uint256, uint256, bytes32, address, bytes32, bytes32)
        );
    }

    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;
        return v;
    }
}
