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

/**
 * @title MockPQCPrecompile, TEST-ONLY faithful mock of the AERE native PQC precompiles
 *
 * @notice Hardhat has no PQC precompile, so tests install this at 0x0AE1..0x0AE4 via
 *         hardhat_setCode. It parses the input EXACTLY as the live Besu precompiles do
 *         (per their Java sources: AereFalconSupport / MLDSA44 / SLHDSA128s) and returns
 *         a RAW 32-byte word 0x..01 iff the embedded signature "commitment" matches
 *         keccak256(pk || message). This models the real semantics: valid iff a genuine
 *         signature by THIS key over THIS message. Any malformed or non-matching input
 *         returns a raw 32-byte 0x..00 (invalid). The raw (non-ABI-encoded) return byte
 *         layout is identical to the live precompiles, so the caller's
 *         `ret.length >= 32 && ret[31] == 0x01` check behaves as it does on mainnet.
 *
 *         The parse offsets here are the precompiles' spec offsets, NOT copied from the
 *         contract under test, so a positive attest passing here proves the contract
 *         built the input at the correct offsets. The real encoding is separately proven
 *         against the LIVE mainnet precompile via eth_call.
 */
contract MockPQCPrecompile {
    uint8 private immutable _scheme;

    constructor(uint8 scheme_) {
        _scheme = scheme_;
    }

    // No public functions other than fallback, so an input whose leading bytes look
    // like a selector can never dispatch anywhere but the verifier.
    fallback() external {
        bool valid = _check(msg.data);
        bytes32 w = valid ? bytes32(uint256(1)) : bytes32(0);
        assembly {
            mstore(0x0, w)
            return(0x0, 32)
        }
    }

    function _check(bytes calldata input) internal view returns (bool) {
        if (_scheme == 1) return _checkFalcon(input, 897, 0x29);
        if (_scheme == 2) return _checkFalcon(input, 1793, 0x2A);
        if (_scheme == 3) return _checkFixed(input, 1312, 2420);
        if (_scheme == 4) return _checkFixed(input, 32, 7856);
        return false;
    }

    // Falcon: input = pk(pkLen) || sm ; sm = sigLen(2 BE) || nonce(40) || message || esig ;
    // esig = esigHeader || compressedSig ; sigLen == esig.length. (mirrors AereFalconSupport.verify)
    function _checkFalcon(bytes calldata input, uint256 pkLen, uint8 esigHeader) internal pure returns (bool) {
        if (input.length <= pkLen) return false;
        bytes calldata pk = input[0:pkLen];
        bytes calldata sm = input[pkLen:];
        if (sm.length < 2 + 40 + 2) return false;
        uint256 sigLen = (uint256(uint8(sm[0])) << 8) | uint256(uint8(sm[1]));
        if (sigLen < 2 || 2 + 40 + sigLen > sm.length) return false;
        uint256 msgLen = sm.length - 2 - 40 - sigLen;
        bytes calldata esig = sm[sm.length - sigLen:];
        if (esig.length < 33) return false;
        if (uint8(esig[0]) != esigHeader) return false;
        bytes calldata message = sm[42:42 + msgLen];
        bytes32 commitment = _load(esig, 1);
        return commitment == keccak256(abi.encodePacked(pk, message));
    }

    // ML-DSA-44 / SLH-DSA-128s: input = pk(pkLen) || sig(sigLen) || message(rest).
    function _checkFixed(bytes calldata input, uint256 pkLen, uint256 sigLen) internal pure returns (bool) {
        if (input.length < pkLen + sigLen + 32) return false;
        bytes calldata pk = input[0:pkLen];
        bytes calldata sig = input[pkLen:pkLen + sigLen];
        bytes calldata message = input[pkLen + sigLen:];
        bytes32 commitment = _load(sig, 0);
        return commitment == keccak256(abi.encodePacked(pk, message));
    }

    function _load(bytes calldata s, uint256 off) internal pure returns (bytes32 r) {
        assembly {
            r := calldataload(add(s.offset, off))
        }
    }
}
