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

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IDestinationSettler} from "./IERC7683.sol";

/// @notice The finality prover this settler's repayment path composes with. It is the
///         deployed AERE zk QBFT light client (contracts/interop/AereZkQbftLightClient.sol,
///         live on consuming chains at 0xCaDA54FA...6488), which verifies an SP1 Groth16
///         proof that ONE AERE block header is FINAL under Hyperledger Besu QBFT and, on
///         success, records the finalized (blockNumber, blockHash) for THIS pinned validator
///         set. This interface is declared here as the documented reference type so an
///         integrator reads the exact surface the origin-side verifier relies on. The
///         light client is deployed on the ORIGIN/consuming chain, not on AERE (AERE is
///         native there), so this settler does NOT call it on-chain; see the repayment
///         section of the NatSpec below and docs/AERE-ERC7683-DESTINATION.md.
interface IAereQbftFinalityLightClient {
    /// @notice Highest QBFT-final AERE block number advanced so far.
    function finalizedNumber() external view returns (uint64);

    /// @notice On-chain block hash of the block at finalizedNumber().
    function finalizedBlockHash() external view returns (bytes32);

    /// @notice keccak256 over the trusted AERE validator set, in stored order. Every
    ///         accepted finality proof must attest THIS set. A consumer MUST check this
    ///         still equals keccak256 over the live qbft_getValidatorsByBlockNumber set.
    function trustedValidatorSetRoot() external view returns (bytes32);

    /// @notice Stateless finality check: reverts on an invalid proof or a mismatched
    ///         anchor, otherwise returns the proven block fields. Records nothing.
    function verify(bytes calldata publicValues, bytes calldata proof)
        external
        view
        returns (
            bytes32 blockHash,
            uint64 blockNumber,
            bytes32 committedSealHash,
            bytes32 validatorSetRoot,
            bool finalized,
            bool falconVerified
        );
}

/**
 * @title AereDestinationSettler. ERC-7683 IDestinationSettler (the FILLING side).
 *
 * @notice AERE Network already ships the ORIGIN side of the ERC-7683 cross-chain intent
 *         standard (contracts/intents/AereSpokePool.sol, an IOriginSettler): a user locks
 *         input on the origin chain and emits an Open event that declares the OUTPUT leg
 *         (token, amount, recipient) they want on the destination chain. This contract is
 *         the missing DESTINATION half. A solver calls {fill} to deliver those promised
 *         output tokens to the recipient on AERE, and this contract records the fill so the
 *         ORIGIN chain can later repay the solver against a proof of AERE's QBFT finality
 *         (see "Repayment proof path" below) instead of trusting a relayer.
 *
 *         WHY THIS REMOVES THE BRIDGE-LIQUIDITY BOOTSTRAP. A classic lock-and-mint bridge
 *         must pre-fund destination-side liquidity before it can serve a single transfer.
 *         An intent design does not: the SOLVER fronts their own liquidity to fill the
 *         order here, and is repaid the locked input on the origin chain after the fill is
 *         proven final. AERE therefore never has to seed or custody bridge liquidity; the
 *         market of solvers supplies it per intent.
 *
 * @dev    SCOPE. This is a settlement / interop-layer contract. It does not touch, gate, or
 *         change AERE consensus, which remains classical secp256k1 ECDSA QBFT. The finality
 *         proof referenced below is a proof ABOUT that QBFT finality, verified classically
 *         today; that is the honest and intended posture, not a post-quantum consensus claim.
 *
 * @dev    ON-CHAIN GUARANTEES (fail-closed). {fill}:
 *           1. binds the standard's orderId argument to the orderId carried inside originData
 *              (OrderIdMismatch otherwise);
 *           2. rejects a zero declared recipient (ZeroRecipient);
 *           3. rejects a second fill of an order already filled (AlreadyFilled), so an order
 *              can be filled at most once, so a solver cannot be repaid twice for one intent;
 *           4. rejects a delivered output that does not satisfy the order's declared output
 *              (OutputMismatch): the delivered token and recipient must match exactly and the
 *              delivered amount must be at least the declared amount. A fill that delivers the
 *              wrong token, to the wrong recipient, or short can never be recorded;
 *           5. delivers the output atomically (SafeERC20 pull from the solver straight to the
 *              recipient, so this contract never custodies user output), then records the fill
 *              and emits {Filled}.
 *
 * @dev    REPAYMENT PROOF PATH (how the origin chain repays the solver).
 *         After {fill}, the fill lives in this contract's storage and in the {Filled} log on
 *         AERE (chain 2800). To repay the solver, the origin chain must learn "this exact
 *         fill was included in a block that is FINAL on AERE" WITHOUT trusting a relayer. AERE
 *         already has the machinery for this, and this settler is built to plug into it:
 *
 *           * FINALITY: contracts/interop/AereZkQbftLightClient.sol proves, in the SP1 zkVM
 *             (guest `qbft-lightclient-guest`), that an AERE header is QBFT-final: it decodes
 *             the Besu QBFT extraData, recomputes both Besu hash pre-images, ecrecovers each
 *             committed seal, and asserts the distinct-validator count reaches the QBFT quorum
 *             ceil(2N/3) (5-of-7 for AERE's live N=7 set), binding the proof to an IMMUTABLE
 *             validator-set anchor. See {IAereQbftFinalityLightClient} above.
 *
 *           * INCLUSION + DELIVERY: contracts/interop/AereOutboundVerifierV2.sol is the
 *             origin-side pattern that CONSUMES such a proof. It accepts an SP1 proof that a
 *             specific AERE log was included in a QBFT-final AERE block (finality + receipt
 *             inclusion, one guest), binds the proof's committed validatorSetRoot to its own
 *             immutable anchor, enforces exactly-once replay, and only then acts. The origin
 *             chain runs an AereOutboundVerifierV2-style verifier whose trusted "source"
 *             address is THIS settler and whose event signature is {FILLED_EVENT_SIG}; it
 *             re-derives {fillCommitment} from the proven log and repays the solver the input
 *             AereSpokePool locked, against `repaymentAddress` from the fill record.
 *
 *         So the proof shape is exactly the one AereOutboundVerifierV2 already documents (an
 *         SP1 Groth16 proof over AERE QBFT finality + log inclusion, validator-set-anchored).
 *         The on-chain surface THIS contract must provide for that path is: the {Filled}
 *         event, the persisted {FillRecord}, and the {fillCommitment} binder. It provides all
 *         three. The end-to-end origin-side repayment (off-chain SP1 prover run over the
 *         `qbft-lightclient-guest` ELF, plus the origin-chain verifier deployment) is NOT
 *         exercised by this contract or its unit test and is marked [MEASURE]: it needs the
 *         separate off-chain prover and a second chain. The exact receipt-trie inclusion
 *         encoding is [VERIFY: confirm against the final AereOutboundVerifierV2 guest].
 *
 * @dev    TRUST MODEL. Read before quoting.
 *           * Recording a fill here is permissionless and does not, by itself, move the
 *             origin's locked input. Repayment happens ONLY on the origin chain and ONLY
 *             behind the finality+inclusion proof above. A forged or unproven fill record
 *             therefore cannot extract origin funds; the origin verifier's validator-set
 *             anchor is the gate.
 *           * Delivery correctness on AERE is enforced on-chain here (the four fail-closed
 *             checks). Repayment correctness on the origin chain rests on the honesty of
 *             >= 2f+1 of AERE's QBFT validators (5-of-7 today, all Foundation-operated: an
 *             operator assumption, not an economic one) plus a sound SP1/Groth16 verifier.
 */
contract AereDestinationSettler is ReentrancyGuard, IDestinationSettler {
    using SafeERC20 for IERC20;

    /* ------------------------------- constants ------------------------------ */

    /// @notice AERE mainnet chain id. The repayment commitment binds to this constant
    ///         (matching AereOutboundVerifierV2.AERE_CHAIN_ID) rather than block.chainid, so
    ///         the value the origin-side verifier re-derives is fixed to AERE mainnet.
    uint256 public constant AERE_CHAIN_ID = 2800;

    /// @notice topic0 preimage of the {Filled} event. An origin-side
    ///         AereOutboundVerifierV2-style verifier uses this to select this settler's
    ///         fill log out of a proven-final AERE block.
    bytes32 public constant FILLED_EVENT_SIG =
        keccak256("Filled(bytes32,address,address,uint256,address,bytes32,uint64,uint64)");

    /* --------------------------------- state -------------------------------- */

    struct FillRecord {
        address filler;            // solver who delivered the output (repaid on origin)
        address outputToken;       // token actually delivered on AERE
        uint256 outputAmount;      // amount actually delivered
        address recipient;         // who received the output on AERE
        uint64 filledAt;           // block.timestamp of the fill
        uint64 filledBlock;        // block.number of the fill (the inclusion height)
        bytes32 repaymentAddress;  // solver's repayment address on the ORIGIN chain (bytes32)
    }

    /// @notice orderId => recorded fill. A record with filler == address(0) means "unfilled".
    mapping(bytes32 => FillRecord) public fills;

    /// @notice Number of orders filled through this settler.
    uint64 public fillCount;

    /* --------------------------------- events ------------------------------- */

    /// @notice Emitted once per successful fill. This is the log an origin-side finality +
    ///         inclusion proof carries; its signature is {FILLED_EVENT_SIG}.
    event Filled(
        bytes32 indexed orderId,
        address indexed filler,
        address outputToken,
        uint256 outputAmount,
        address recipient,
        bytes32 repaymentAddress,
        uint64 filledAt,
        uint64 filledBlock
    );

    /* --------------------------------- errors ------------------------------- */

    error OrderIdMismatch(bytes32 fillArg, bytes32 inOriginData);
    error ZeroRecipient();
    error ZeroAmount();
    error ZeroAddress();
    error AlreadyFilled(bytes32 orderId);
    error OutputMismatch();
    error NotFilled(bytes32 orderId);

    /* ---------------------------------- fill -------------------------------- */

    /// @notice ERC-7683 IDestinationSettler. A solver (msg.sender, the filler) delivers the
    ///         order's promised output to the recipient on AERE and records the fill.
    /// @param  orderId    the canonical order id (must equal the id inside originData).
    /// @param  originData the origin settler's opaque output leg, produced by AereSpokePool
    ///                    as abi.encode(bytes32 orderId, address outputToken,
    ///                    uint256 outputAmount, bytes32 recipient), the DECLARED output.
    /// @param  fillerData the solver's supplied fill data, abi.encode(address deliveredToken,
    ///                    uint256 deliveredAmount, address deliveredRecipient,
    ///                    bytes32 repaymentAddress). deliveredToken / deliveredRecipient must
    ///                    match the declared output exactly and deliveredAmount must be >= the
    ///                    declared amount; repaymentAddress is where the solver is repaid on
    ///                    the origin chain.
    function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData)
        external
        override
        nonReentrant
    {
        // ---- Decode the origin's declared output leg. ----
        (
            bytes32 declOrderId,
            address declOutputToken,
            uint256 declOutputAmount,
            bytes32 declRecipient32
        ) = abi.decode(originData, (bytes32, address, uint256, bytes32));

        // Bind the standard's orderId argument to the id the origin actually signed.
        if (declOrderId != orderId) revert OrderIdMismatch(orderId, declOrderId);
        // Fail-closed on a zero recipient (the order could never be honestly delivered).
        if (declRecipient32 == bytes32(0)) revert ZeroRecipient();
        if (declOutputToken == address(0)) revert ZeroAddress();
        if (declOutputAmount == 0) revert ZeroAmount();
        // Fail-closed on a duplicate: an order can be filled at most once.
        if (fills[orderId].filler != address(0)) revert AlreadyFilled(orderId);

        address declRecipient = address(uint160(uint256(declRecipient32)));

        // ---- Decode the solver's actual delivery + repayment address. ----
        (
            address deliveredToken,
            uint256 deliveredAmount,
            address deliveredRecipient,
            bytes32 repaymentAddress
        ) = abi.decode(fillerData, (address, uint256, address, bytes32));

        // Fail-closed on a delivered output that does not satisfy the declared output:
        // exact token, exact recipient, at-least the declared amount.
        if (
            deliveredToken != declOutputToken ||
            deliveredRecipient != declRecipient ||
            deliveredAmount < declOutputAmount
        ) revert OutputMismatch();

        // ---- Effects before interaction (record the fill, then deliver). ----
        fills[orderId] = FillRecord({
            filler: msg.sender,
            outputToken: deliveredToken,
            outputAmount: deliveredAmount,
            recipient: deliveredRecipient,
            filledAt: uint64(block.timestamp),
            filledBlock: uint64(block.number),
            repaymentAddress: repaymentAddress
        });
        unchecked {
            fillCount += 1;
        }

        emit Filled(
            orderId,
            msg.sender,
            deliveredToken,
            deliveredAmount,
            deliveredRecipient,
            repaymentAddress,
            uint64(block.timestamp),
            uint64(block.number)
        );

        // ---- Deliver the output atomically: solver -> recipient, no custody here. ----
        IERC20(deliveredToken).safeTransferFrom(msg.sender, deliveredRecipient, deliveredAmount);
    }

    /* ------------------------------- fill views ----------------------------- */

    /// @notice True once `orderId` has been filled through this settler.
    function isFilled(bytes32 orderId) external view returns (bool) {
        return fills[orderId].filler != address(0);
    }

    /// @notice The full recorded fill for `orderId`. Reverts if unfilled.
    function getFill(bytes32 orderId)
        external
        view
        returns (
            address filler,
            address outputToken,
            uint256 outputAmount,
            address recipient,
            uint64 filledAt,
            uint64 filledBlock,
            bytes32 repaymentAddress
        )
    {
        FillRecord storage r = fills[orderId];
        if (r.filler == address(0)) revert NotFilled(orderId);
        return (
            r.filler,
            r.outputToken,
            r.outputAmount,
            r.recipient,
            r.filledAt,
            r.filledBlock,
            r.repaymentAddress
        );
    }

    /// @notice The canonical repayment commitment an origin-side verifier re-derives from a
    ///         proven-final {Filled} log to repay the solver. It binds AERE's chain id, this
    ///         settler, and every economically-relevant fill field (orderId, filler, output
    ///         token / amount / recipient, and the solver's origin-chain repaymentAddress).
    ///         An AereOutboundVerifierV2-style verifier on the origin chain, gated by AERE's
    ///         validator-set anchor, checks a finality+inclusion proof for this fill and then
    ///         repays `filler`/`repaymentAddress` the locked input. Reverts if unfilled.
    /// @dev    The exact receipt-trie inclusion encoding the origin guest proves is
    ///         [VERIFY: confirm against the final AereOutboundVerifierV2 guest]; this
    ///         commitment fixes the fields that must be bound, independent of that encoding.
    function fillCommitment(bytes32 orderId) external view returns (bytes32) {
        FillRecord storage r = fills[orderId];
        if (r.filler == address(0)) revert NotFilled(orderId);
        return keccak256(
            abi.encode(
                AERE_CHAIN_ID,
                address(this),
                orderId,
                r.filler,
                r.outputToken,
                r.outputAmount,
                r.recipient,
                r.repaymentAddress
            )
        );
    }
}
