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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/**
 * @title AereRandomnessBeacon
 * @notice Permissionless drand verifiable randomness beacon for AERE Network.
 *
 *   drand (https://drand.love) is the distributed randomness beacon operated
 *   by the League of Entropy — Cloudflare, EPFL, Protocol Labs, U.S. Naval
 *   Research Lab, and ~15 other independent organisations. Every 3 seconds
 *   they collectively sign a fresh random value via threshold BLS12-381.
 *   Anyone can fetch the current and historical rounds for free at
 *   https://api.drand.sh — and anyone can submit any round to this contract
 *   to make it on-chain readable.
 *
 *   Phase 1 (this deployment): randomness is submitted by anyone, paid by the
 *   submitter, with off-chain auditability — the canonical drand signature
 *   for any round is public and verifiable at api.drand.sh. The contract
 *   stores randomness keyed by drand round number and emits the submitter
 *   address in every event so the chain has a public submitter ledger.
 *
 *   Phase 2 (after audit): swap to on-chain BLS pairing verification using
 *   AERE's EIP-2537 precompiles (activated in Pectra, Tier 1.1). The
 *   submission interface is forward-compatible — Phase 2 just enables the
 *   pairing check inside `_verify()`.
 *
 *   Zero Foundation cost: no relayer to run, no subscription, no validators
 *   to coordinate. Consumers (dApps that want a random number) submit the
 *   round they need when they need it. Permissionless.
 *
 *   Reference drand chain (Quicknet, bls-unchained-g1-rfc9380):
 *     Chain hash: 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971
 *     Period: 3 seconds  ·  Genesis: 1692803367
 *     Public key (G2, compressed): 0x83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a
 */
contract AereRandomnessBeacon is Ownable, ReentrancyGuard {
    /// drand Quicknet chain hash (the chain we currently consume from).
    bytes32 public constant DRAND_CHAIN_HASH = 0x52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971;

    /// drand Quicknet period (seconds between rounds).
    uint64 public constant DRAND_PERIOD = 3;

    /// drand Quicknet genesis unix timestamp.
    uint64 public constant DRAND_GENESIS = 1692803367;

    /// drand Quicknet aggregated public key (G2, compressed). 96 bytes.
    bytes public DRAND_PUBKEY = hex"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a";

    /// Phase flag: true = on-chain BLS pairing verification required (Phase 2);
    /// false = off-chain auditability only (Phase 1).
    bool public strictVerifyEnabled = false;

    /// Per-round storage. Once written, immutable.
    mapping(uint64 => bytes32) public randomnessOfRound;
    mapping(uint64 => bytes) public signatureOfRound;
    mapping(uint64 => address) public submitterOfRound;

    event RoundSubmitted(uint64 indexed round, bytes32 randomness, address indexed submitter);
    event StrictVerifyChanged(bool enabled);
    event DrandPubkeyChanged(bytes newPubkey);

    // ───────────────────── Admin ─────────────────────

    /// Foundation enables strict on-chain BLS pairing verification in Phase 2.
    function setStrictVerify(bool _enabled) external onlyOwner {
        strictVerifyEnabled = _enabled;
        emit StrictVerifyChanged(_enabled);
    }

    /// Allow Foundation to rotate to a new drand chain (e.g. if Quicknet is succeeded by a faster chain).
    function setDrandPubkey(bytes calldata _pubkey) external onlyOwner {
        DRAND_PUBKEY = _pubkey;
        emit DrandPubkeyChanged(_pubkey);
    }

    // ───────────────────── Submit ─────────────────────

    /**
     * @notice Submit a drand round. Anyone can call. Submitter pays the gas.
     * @param round Drand round number (monotonic, 1 = first round after genesis).
     * @param signature drand BLS signature for this round, fetched from api.drand.sh.
     *                  Format: 48-byte compressed G1 point (drand Quicknet uses G1 sigs).
     * @return randomness keccak256(signature) — matches drand's canonical randomness derivation.
     */
    function submitRound(uint64 round, bytes calldata signature) external nonReentrant returns (bytes32 randomness) {
        require(round > 0, "AereDrand: bad round");
        require(signature.length == 48, "AereDrand: sig must be 48 bytes (G1 compressed)");
        require(randomnessOfRound[round] == bytes32(0), "AereDrand: already submitted");

        // Round must not be in the future of drand's published timeline.
        uint64 roundTime = DRAND_GENESIS + (round * DRAND_PERIOD);
        require(roundTime <= block.timestamp + 30, "AereDrand: round not yet emitted by drand");

        // Phase 2: on-chain BLS pairing verification.
        // For now we trust the submitter (Phase 1). The signature can be
        // verified off-chain against api.drand.sh in O(1).
        if (strictVerifyEnabled) {
            require(_verify(round, signature), "AereDrand: BLS verification failed");
        }

        randomness = keccak256(signature);
        randomnessOfRound[round] = randomness;
        signatureOfRound[round] = signature;
        submitterOfRound[round] = msg.sender;

        emit RoundSubmitted(round, randomness, msg.sender);
    }

    /**
     * @notice Read randomness for a round. Returns bytes32(0) if not yet submitted.
     */
    function getRandomness(uint64 round) external view returns (bytes32) {
        return randomnessOfRound[round];
    }

    /**
     * @notice Convenience: get the drand round number that will be emitted at or after a given unix timestamp.
     */
    function roundAtTime(uint64 unixTimestamp) external pure returns (uint64) {
        require(unixTimestamp >= DRAND_GENESIS, "AereDrand: before drand genesis");
        // Round N is emitted at DRAND_GENESIS + N * PERIOD. Ceil-div to get the next round.
        return uint64((unixTimestamp - DRAND_GENESIS + DRAND_PERIOD - 1) / DRAND_PERIOD);
    }

    /// Returns true if randomness has been submitted for this round.
    function isAvailable(uint64 round) external view returns (bool) {
        return randomnessOfRound[round] != bytes32(0);
    }

    // ───────────────────── Phase 2 verification (placeholder) ─────────────────────

    /**
     * @dev Placeholder for Phase 2 on-chain BLS pairing verification using
     *      EIP-2537 precompiles (BLS_PAIRING_CHECK at 0x0f). The full
     *      implementation requires:
     *        1. Hash the round number to a G1 point following RFC 9380 SSWU_RO_
     *           (using BLS_MAP_FP_TO_G1 at 0x10 + BLS_G1ADD at 0x0b)
     *        2. Decompress the 48-byte G1 signature into a 128-byte uncompressed point
     *        3. Compute pairing check: e(sig, -G2_GEN) * e(msg_G1, DRAND_PK_G2) == 1
     *
     *      This is non-trivial Solidity (~300 lines) and deserves its own
     *      audit pass. Wired in Tier 1.7 Phase 2.
     */
    function _verify(uint64 /*round*/, bytes calldata /*signature*/) internal pure returns (bool) {
        // Phase 2 implementation goes here.
        return true;
    }
}
