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

/// @dev The one AerePQCKeyRegistry read used to verify a mandate's post-quantum signature. Live at
///      0x1eCa…3691 on chain 2800; verifyWithKey routes to the native precompile for the key's
///      scheme (0x0AE1 Falcon-512) and verifies in full on-chain, fail-closed.
interface IAerePQCKeyRegistryMandate {
    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);
}

/**
 * @title AereAP2MandateVerifier, a post-quantum x402 / AP2 payment-mandate verifier
 *
 * @notice A minimal verifier for AP2-style payment MANDATES whose authorizing signature is
 *         POST-QUANTUM (Falcon-512), verified in full on-chain by Aere Network's LIVE native
 *         precompile (0x0AE1) through the already-deployed AerePQCKeyRegistry. Settlement of the
 *         authorized spend is routed through the EXISTING AERE402 rail (AERE402FacilitatorPQC /
 *         AERE402FacilitatorV2), which is NOT redeployed or modified here.
 *
 *         WHAT AN AP2 / x402 MANDATE IS. A signed, standing authorization of the form
 *         "agent X may spend up to N of token T per period P for purpose Q". In AP2 (Google) and
 *         x402 (Coinbase) today that authorization is an ECDSA signature, which a quantum computer
 *         forges. Here the authorizing key is a Falcon-512 key in AerePQCKeyRegistry, so the mandate
 *         is quantum-durable. The struct and the verify path are below.
 *
 *         THE STRUCT. A Mandate binds the paying agent's Falcon key, the token, the spend cap, the
 *         validity window, an opaque purpose tag, and a mandate nonce (so an agent can issue several
 *         independent standing mandates). Its `mandateHash` binds chainId + this contract so a
 *         signed mandate cannot be replayed against another verifier or chain.
 *
 *         THE VERIFY PATH.
 *           - verifyMandate(m, falconSig): pure on-chain PQC check that the agent's Falcon key
 *             signed `m` (view; anyone can check for free via eth_call).
 *           - authorizeSpend(m, amount, falconSig): the enforced path. Verifies the Falcon mandate
 *             signature via the live precompile, checks the validity window and the cumulative cap,
 *             records the spend against the mandate, and emits MandateAuthorized. Reverts fail-closed
 *             on an invalid / tampered signature, an out-of-window mandate, or a cap overflow.
 *
 *         SETTLEMENT ROUTING. authorizeSpend records that `amount` is authorized under the mandate;
 *         the actual token movement + 25-bps fee to AereSink is performed by the live AERE402 rail.
 *         To keep the mandate's cumulative cap honest (only real settlements should consume it),
 *         authorizeSpend is gated to an immutable SETTLEMENT_EXECUTOR when one is configured (the
 *         AERE402 facilitator, which calls authorizeSpend atomically with settlement). When
 *         SETTLEMENT_EXECUTOR is the zero address the accounting path is open (for verification and
 *         testing); the pure PQC check verifyMandate is always open.
 *
 *         [DEPLOY CONSTRAINT, accepted-and-documented risk] A Falcon mandate signature is a STANDING
 *         authorization by design: one signature authorizes repeated spends up to the cap, and the
 *         signature is public. So in the OPEN accounting path (SETTLEMENT_EXECUTOR == address(0)) ANY
 *         address that observes the signature can replay authorizeSpend to consume the mandate's
 *         remaining cap WITHOUT a real settlement (cap-exhaustion griefing; no custody, no theft, since
 *         this contract moves no funds). The mitigation is built in and MUST be used in production: set
 *         SETTLEMENT_EXECUTOR to the AERE402 facilitator so cap consumption only ever happens ALONGSIDE
 *         a real settlement (the gate is enforced in authorizeSpend and proven in the tests). The open
 *         path is intended ONLY for eth_call verification and unit testing and MUST NOT be used for a
 *         production deployment. A per-authorization nonce is deliberately NOT added here because it
 *         would break the intended standing-mandate ("sign once, spend repeatedly up to the cap")
 *         semantics; the executor gate closes the vector without that trade-off.
 *
 * @dev    [VERIFY: confirm against the finalized x402 / AP2 mandate schema] The x402 and AP2 mandate
 *         field sets are still evolving (AP2 distinguishes Intent vs Cart mandates; x402 carries an
 *         HTTP-402 payment payload). This struct captures the common "spend up to N per period for
 *         purpose Q" authorization; reconcile the exact field set and encoding with the final specs
 *         before claiming wire-level conformance.
 *
 *         HONEST SCOPE. Application-layer payment authorization only. Does NOT change AERE consensus
 *         (still Besu QBFT classical ECDSA). Holds NO funds (the cap is accounting, not custody; the
 *         AERE402 rail custodies and moves value). No admin.
 */
contract AereAP2MandateVerifier {
    /// @notice Falcon-512 is the mandate signing scheme (live precompile 0x0AE1).
    uint8 public constant SCHEME_FALCON512 = 1;
    uint8 internal constant REGISTRY_STATUS_ACTIVE = 1;

    /// @notice Domain separator binding a mandate hash to this verifier.
    bytes32 public constant MANDATE_DOMAIN = keccak256("AereAP2MandateVerifier.v1.mandate");

    /// @notice The LIVE AerePQCKeyRegistry that verifies the Falcon mandate signature via 0x0AE1.
    IAerePQCKeyRegistryMandate public immutable KEY_REGISTRY;

    /// @notice The AERE402 settlement executor allowed to consume a mandate's cap. Zero = open path.
    address public immutable SETTLEMENT_EXECUTOR;

    /// @notice An AP2 / x402 payment mandate: "agentKey may spend up to maxAmount of token per the
    ///         [periodStart, periodEnd] window for `purpose`". mandateNonce distinguishes an agent's
    ///         several standing mandates.
    struct Mandate {
        uint256 agentKeyId; // the paying agent's Falcon-512 key in AerePQCKeyRegistry
        address token; // the settlement token the cap is denominated in
        uint256 maxAmount; // cumulative spend cap over the window
        uint64 periodStart; // unix seconds, inclusive
        uint64 periodEnd; // unix seconds, inclusive (>= periodStart)
        bytes32 purpose; // opaque purpose tag, e.g. keccak256("inference:anthropic")
        uint256 mandateNonce; // per-agent mandate index (lets an agent hold several mandates)
    }

    /// @notice mandateHash => cumulative amount already authorized under that mandate.
    mapping(bytes32 => uint256) public spentUnder;

    event MandateAuthorized(
        bytes32 indexed mandateHash,
        uint256 indexed agentKeyId,
        address indexed token,
        uint256 amount,
        uint256 cumulative,
        bytes32 purpose
    );

    error ZeroAddress();
    error NotSettlementExecutor();
    error AgentKeyNotActiveFalcon(uint256 agentKeyId);
    error InvalidMandateWindow(uint64 periodStart, uint64 periodEnd);
    error MandateNotYetValid(uint64 periodStart);
    error MandateExpired(uint64 periodEnd);
    error ZeroAmount();
    error MandateCapExceeded(uint256 cap, uint256 attempted);
    error MandateSignatureInvalid();

    constructor(address keyRegistry_, address settlementExecutor_) {
        if (keyRegistry_ == address(0)) revert ZeroAddress();
        KEY_REGISTRY = IAerePQCKeyRegistryMandate(keyRegistry_);
        SETTLEMENT_EXECUTOR = settlementExecutor_; // zero allowed = open accounting path
    }

    // ==========================================================================
    //                          Mandate hash + pure verify
    // ==========================================================================

    /// @notice The canonical hash a Falcon mandate signature must sign. Binds chainId + this
    ///         verifier so a mandate cannot be replayed against another verifier or chain.
    function mandateHash(Mandate calldata m) public view returns (bytes32) {
        return keccak256(
            abi.encode(
                MANDATE_DOMAIN,
                block.chainid,
                address(this),
                m.agentKeyId,
                m.token,
                m.maxAmount,
                m.periodStart,
                m.periodEnd,
                m.purpose,
                m.mandateNonce
            )
        );
    }

    /// @notice Pure on-chain post-quantum check that the agent's Falcon key signed this mandate.
    ///         View, permissionless: anyone can verify a mandate via eth_call for free. Returns
    ///         false (never reverts) if the key is not an ACTIVE Falcon key or the signature is
    ///         invalid.
    function verifyMandate(Mandate calldata m, bytes calldata falconSig) external view returns (bool) {
        if (KEY_REGISTRY.statusOf(m.agentKeyId) != REGISTRY_STATUS_ACTIVE) return false;
        if (KEY_REGISTRY.schemeOf(m.agentKeyId) != SCHEME_FALCON512) return false;
        return KEY_REGISTRY.verifyWithKey(m.agentKeyId, mandateHash(m), falconSig);
    }

    // ==========================================================================
    //                          Authorize (enforced path)
    // ==========================================================================

    /**
     * @notice Authorize `amount` of spend under a Falcon-signed mandate. Verifies the post-quantum
     *         mandate signature on-chain via the live precompile, enforces the validity window and
     *         the cumulative cap, records the spend, and emits MandateAuthorized. Fail-closed on any
     *         invalid / tampered signature, an out-of-window mandate, or a cap overflow.
     *
     *         Gated to SETTLEMENT_EXECUTOR when one is configured, so a mandate's cap is only ever
     *         consumed alongside a real AERE402 settlement (the executor calls this atomically).
     *         PRODUCTION MUST configure SETTLEMENT_EXECUTOR: in the open path (executor == 0) the public
     *         standing signature is replayable by anyone to exhaust the cap (see the contract-level
     *         DEPLOY CONSTRAINT note). The open path is verification / test only.
     *
     * @param m         the mandate being drawn against.
     * @param amount    the spend to authorize now (charged against the mandate's cumulative cap).
     * @param falconSig the agent's Falcon-512 signature over mandateHash(m).
     * @return mh       the mandate hash the spend was recorded under.
     */
    function authorizeSpend(Mandate calldata m, uint256 amount, bytes calldata falconSig)
        external
        returns (bytes32 mh)
    {
        if (SETTLEMENT_EXECUTOR != address(0) && msg.sender != SETTLEMENT_EXECUTOR) revert NotSettlementExecutor();
        if (amount == 0) revert ZeroAmount();

        // Window checks.
        if (m.periodEnd < m.periodStart) revert InvalidMandateWindow(m.periodStart, m.periodEnd);
        if (block.timestamp < m.periodStart) revert MandateNotYetValid(m.periodStart);
        if (block.timestamp > m.periodEnd) revert MandateExpired(m.periodEnd);

        // The mandate signer must be an ACTIVE Falcon key (rotating / revoking it halts the mandate).
        if (
            KEY_REGISTRY.statusOf(m.agentKeyId) != REGISTRY_STATUS_ACTIVE
                || KEY_REGISTRY.schemeOf(m.agentKeyId) != SCHEME_FALCON512
        ) revert AgentKeyNotActiveFalcon(m.agentKeyId);

        mh = mandateHash(m);

        // Full on-chain post-quantum verification of the mandate authorization.
        if (!KEY_REGISTRY.verifyWithKey(m.agentKeyId, mh, falconSig)) revert MandateSignatureInvalid();

        // Cap check (overflow-safe) then record.
        uint256 already = spentUnder[mh];
        uint256 next = already + amount;
        if (next < already || next > m.maxAmount) revert MandateCapExceeded(m.maxAmount, next);
        spentUnder[mh] = next;

        emit MandateAuthorized(mh, m.agentKeyId, m.token, amount, next, m.purpose);
    }

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

    /// @notice Remaining spendable amount under a mandate (0 if exhausted). Does NOT check the
    ///         time window or signature; use verifyMandate / authorizeSpend for the full gate.
    function remainingUnder(Mandate calldata m) external view returns (uint256) {
        uint256 already = spentUnder[mandateHash(m)];
        return already >= m.maxAmount ? 0 : m.maxAmount - already;
    }

    /// @notice True iff `m` is within its validity window at the current block time.
    function isWithinWindow(Mandate calldata m) external view returns (bool) {
        return block.timestamp >= m.periodStart && block.timestamp <= m.periodEnd && m.periodEnd >= m.periodStart;
    }
}
