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

import "./IERC8004.sol";

/// @dev The subset of the LIVE, deployed AerePQCKeyRegistry (chain 2800, 0x1eCa…3691) this adapter
///      uses. verifyWithKey routes to the live native precompile for the key's scheme (0x0AE1
///      Falcon-512, 0x0AE3 ML-DSA-44) and verifies the signature IN FULL on-chain, fail-closed.
interface IAerePQCKeyRegistryValidation {
    function verifyWithKey(uint256 keyId, bytes32 message, bytes calldata signature) external view returns (bool);
    function statusOf(uint256 keyId) external view returns (uint8);
    function schemeOf(uint256 keyId) external view returns (uint8);
}

/// @dev The one AereAgentDID read used to confirm the server agent being validated actually exists.
interface IAereAgentDIDValidation {
    function agentExists(uint256 rootKeyId) external view returns (bool);
}

/**
 * @title AereValidationRegistry8004, the post-quantum ERC-8004 Validation registry
 *
 * @notice The differentiator. Exposes the ERC-8004 Validation surface (request / respond with a
 *         proof) where the validator's RESPONSE proof is a POST-QUANTUM signature, Falcon-512 or
 *         ML-DSA-44, verified in full ON-CHAIN by Aere Network's LIVE native precompiles
 *         (0x0AE1 Falcon-512, 0x0AE3 ML-DSA-44) through the already-deployed AerePQCKeyRegistry.
 *         Every other agent-validation stack in the ecosystem authenticates validator responses
 *         with ECDSA, which a quantum computer forges; this one does not.
 *
 *         FLOW.
 *           1. requestValidation(validatorKeyId, serverAgentId, dataHash): anyone records a request
 *              that the PQC validator identified by `validatorKeyId` validate the work the server
 *              agent committed to via `dataHash`. The validator key must be an ACTIVE Falcon-512 or
 *              ML-DSA-44 key in the registry; the server agent must exist in the live AereAgentDID.
 *           2. respondValidation(requestId, response, pqcProof): the validator answers with a score
 *              (0..100, the ERC-8004 convention) and a PQC signature over this contract's
 *              domain-separated challenge binding (requestId, dataHash, serverAgentId,
 *              validatorKeyId, response). The signature is verified on-chain via the live
 *              precompile. On success the response is recorded once; on any invalid or tampered
 *              proof the call REVERTS and records nothing (FAIL-CLOSED).
 *
 * @dev    NON-REPLAYABLE / NON-FORGEABLE. The challenge binds chainId + this contract + the exact
 *         request tuple + the response, so a proof for one (request, response) can never be reused
 *         for another, another contract, or another chain. A request completes exactly once
 *         (respond reverts once Status != Pending). Authorship is the PQC signature alone: respond
 *         may be relayed by any msg.sender, since only the validator private key can produce a valid
 *         signature over the contract-bound challenge. An empty / zero precompile return (a stale,
 *         un-activated chain) is treated as INVALID.
 *
 * @dev    [VERIFY: confirm against the finalized ERC-8004 interface] ERC-8004 validation in the base
 *         draft identifies the validator by an agent id and carries an opaque response byte. Aere
 *         keeps request/respond SEMANTICS but binds the validator to a REGISTERED POST-QUANTUM KEY
 *         (so the response is cryptographically authenticated on-chain, not merely emitted) and
 *         keeps the response in the ERC-8004 0..100 range. Reconcile exact selectors with the final
 *         ERC before claiming selector-level conformance.
 *
 *         HONEST SCOPE. Application-layer validation only. This does NOT make AERE consensus
 *         post-quantum (blocks are still Besu QBFT with classical secp256k1 ECDSA). It holds no
 *         funds and has no admin. What is post-quantum here is the AUTHENTICITY of a validator's
 *         response, verified by the live precompiles.
 */
contract AereValidationRegistry8004 is IERC8004Validation {
    /// @notice PQC schemes accepted for a validator key (match AerePQCKeyRegistry scheme ids).
    uint8 public constant SCHEME_FALCON512 = 1; // live precompile 0x0AE1
    uint8 public constant SCHEME_MLDSA44 = 3; // live precompile 0x0AE3
    uint8 internal constant REGISTRY_STATUS_ACTIVE = 1;
    uint8 public constant MAX_RESPONSE = 100; // ERC-8004 response score range 0..100

    /// @notice Domain separator for the response challenge the validator PQC-signs.
    bytes32 public constant VALIDATION_DOMAIN = keccak256("AereValidationRegistry8004.v1.response");

    /// @notice The LIVE AerePQCKeyRegistry that verifies validator PQC signatures via the precompiles.
    IAerePQCKeyRegistryValidation public immutable KEY_REGISTRY;

    /// @notice The LIVE AereAgentDID used to confirm the server agent under validation exists.
    IAereAgentDIDValidation public immutable DID;

    struct Request {
        uint256 validatorKeyId;
        uint256 serverAgentId;
        bytes32 dataHash;
        Status status;
        uint8 response; // valid only once status == Completed
        uint8 pqcScheme; // the validator key's scheme at response time
        uint64 requestedBlock;
        uint64 respondedBlock;
    }

    mapping(bytes32 => Request) private _requests; // requestId => request
    mapping(uint256 => uint256) public requestNonce; // validatorKeyId => monotonic request counter
    bytes32[] private _requestIds; // append-only list of every requestId

    error ZeroAddress();
    error ValidatorKeyNotActive(uint256 validatorKeyId);
    error ValidatorSchemeUnsupported(uint256 validatorKeyId, uint8 scheme);
    error ServerAgentUnknown(uint256 serverAgentId);
    error UnknownRequest(bytes32 requestId);
    error RequestNotPending(bytes32 requestId);
    error ResponseOutOfRange(uint8 response);
    error PQCValidationFailed();

    constructor(address keyRegistry_, address did_) {
        if (keyRegistry_ == address(0) || did_ == address(0)) revert ZeroAddress();
        KEY_REGISTRY = IAerePQCKeyRegistryValidation(keyRegistry_);
        DID = IAereAgentDIDValidation(did_);
    }

    // ==========================================================================
    //                              Request
    // ==========================================================================

    /// @inheritdoc IERC8004Validation
    function requestValidation(uint256 validatorKeyId, uint256 serverAgentId, bytes32 dataHash)
        external
        returns (bytes32 requestId)
    {
        uint8 scheme = _requireActivePQCValidator(validatorKeyId);
        if (!DID.agentExists(serverAgentId)) revert ServerAgentUnknown(serverAgentId);

        uint256 n = requestNonce[validatorKeyId];
        requestNonce[validatorKeyId] = n + 1;
        requestId = keccak256(
            abi.encode(VALIDATION_DOMAIN, block.chainid, address(this), validatorKeyId, serverAgentId, dataHash, n)
        );

        _requests[requestId] = Request({
            validatorKeyId: validatorKeyId,
            serverAgentId: serverAgentId,
            dataHash: dataHash,
            status: Status.Pending,
            response: 0,
            pqcScheme: scheme,
            requestedBlock: uint64(block.number),
            respondedBlock: 0
        });
        _requestIds.push(requestId);

        emit ValidationRequest(requestId, validatorKeyId, serverAgentId, dataHash);
    }

    // ==========================================================================
    //                              Respond (PQC-verified)
    // ==========================================================================

    /// @inheritdoc IERC8004Validation
    /// @notice Respond to a pending validation with a score and a POST-QUANTUM proof. The proof is a
    ///         Falcon-512 or ML-DSA-44 signature by the validator key over this contract's challenge,
    ///         verified in full on-chain by the live precompile. Reverts (records nothing) on an
    ///         invalid or tampered proof (FAIL-CLOSED).
    /// @param requestId the pending request being answered.
    /// @param response  the validation score, 0..100 (ERC-8004 convention).
    /// @param pqcProof  the validator's PQC signature envelope for the key's scheme.
    function respondValidation(bytes32 requestId, uint8 response, bytes calldata pqcProof) external {
        Request storage r = _requests[requestId];
        if (r.status == Status.None) revert UnknownRequest(requestId);
        if (r.status != Status.Pending) revert RequestNotPending(requestId);
        if (response > MAX_RESPONSE) revert ResponseOutOfRange(response);

        // The validator key must still be an ACTIVE Falcon-512 / ML-DSA-44 key (it could have been
        // rotated or revoked between request and response).
        uint8 scheme = _requireActivePQCValidator(r.validatorKeyId);

        bytes32 challenge = responseChallenge(requestId, response);
        // Full on-chain post-quantum verification via the live precompile through the registry.
        if (!KEY_REGISTRY.verifyWithKey(r.validatorKeyId, challenge, pqcProof)) revert PQCValidationFailed();

        // Effects: record exactly once.
        r.status = Status.Completed;
        r.response = response;
        r.pqcScheme = scheme;
        r.respondedBlock = uint64(block.number);

        emit ValidationResponse(requestId, r.validatorKeyId, r.serverAgentId, response, scheme);
    }

    /**
     * @notice The exact 32-byte challenge a validator must PQC-sign to answer `requestId` with
     *         `response`. Off-chain validators query this to know what to sign; the contract binds
     *         the request tuple so a proof cannot be moved to a different request or response.
     */
    function responseChallenge(bytes32 requestId, uint8 response) public view returns (bytes32) {
        Request storage r = _requests[requestId];
        return keccak256(
            abi.encode(
                VALIDATION_DOMAIN,
                block.chainid,
                address(this),
                requestId,
                r.dataHash,
                r.serverAgentId,
                r.validatorKeyId,
                response
            )
        );
    }

    // ==========================================================================
    //                                  Views
    // ==========================================================================

    /// @notice Read a validation request/response by id.
    function getValidation(bytes32 requestId)
        external
        view
        returns (
            uint256 validatorKeyId,
            uint256 serverAgentId,
            bytes32 dataHash,
            Status status,
            uint8 response,
            uint8 pqcScheme,
            uint64 requestedBlock,
            uint64 respondedBlock
        )
    {
        Request storage r = _requests[requestId];
        if (r.status == Status.None) revert UnknownRequest(requestId);
        return (
            r.validatorKeyId,
            r.serverAgentId,
            r.dataHash,
            r.status,
            r.response,
            r.pqcScheme,
            r.requestedBlock,
            r.respondedBlock
        );
    }

    /// @notice True iff `requestId` has a recorded, PQC-verified validation response.
    function isValidated(bytes32 requestId) external view returns (bool) {
        return _requests[requestId].status == Status.Completed;
    }

    /// @notice Total number of validation requests ever recorded.
    function requestCount() external view returns (uint256) {
        return _requestIds.length;
    }

    /// @notice The requestId at an index in the append-only list.
    function requestIdAt(uint256 index) external view returns (bytes32) {
        return _requestIds[index];
    }

    /// @notice The live precompile address that verifies a validator key's scheme (informational).
    function precompileFor(uint8 scheme) external pure returns (address) {
        if (scheme == SCHEME_FALCON512) return address(0x0AE1);
        if (scheme == SCHEME_MLDSA44) return address(0x0AE3);
        return address(0);
    }

    // ==========================================================================
    //                                 Internal
    // ==========================================================================

    /// @dev Require `keyId` is an ACTIVE Falcon-512 or ML-DSA-44 key in the live registry. Returns
    ///      the scheme.
    function _requireActivePQCValidator(uint256 keyId) internal view returns (uint8 scheme) {
        if (KEY_REGISTRY.statusOf(keyId) != REGISTRY_STATUS_ACTIVE) revert ValidatorKeyNotActive(keyId);
        scheme = KEY_REGISTRY.schemeOf(keyId);
        if (scheme != SCHEME_FALCON512 && scheme != SCHEME_MLDSA44) {
            revert ValidatorSchemeUnsupported(keyId, scheme);
        }
    }
}
