//! QoE aggregation (`extensions/telemetry-aggregation.md`): `wai.telemetry.aggregate`.
//!
//! A sink's quality-of-experience measures reach the service only as aggregates. Each
//! session's measurement is secret-shared to two aggregators under DAP
//! (`draft-ietf-ppm-dap-20`) with a Prio3 VDAF (`draft-irtf-cfrg-vdaf-22`), whose range proof
//! lets the aggregators refuse an out-of-range measurement without either seeing it; each
//! aggregator signs a statement of its aggregate share with the batch's parameters, and the
//! collector's aggregate receipt carries both, so a reader recomputes the result and knows
//! what was summed, over how many reports and by whom. A batch below the task's minimum,
//! never under fifty sessions, is refused by each aggregator.
//!
//! - [`field`], [`xof`], [`flp`], [`prio3`]: Prio3Count, Prio3Sum, Prio3SumVec and
//!   Prio3Histogram, written from the VDAF draft; [`vdaf_vectors`] runs the draft's published
//!   test vectors;
//! - [`hpke`]: RFC 9180 base mode for DAP's mandatory suite (the crate's [`crate::hpke`]);
//! - [`dap`]: DAP's messages; [`aggregator`]: a reference client, leader, helper and collector;
//! - [`measure`]: the QoE measures and the task layout; [`receipt`]: the statement, the
//!   aggregate receipt, their verification and a room's cell;
//! - [`corpus`]: the conformance corpus `live-conformance/aggregate/`.

pub mod aggregator;
pub mod corpus;
pub mod dap;
pub mod field;
pub mod flp;
pub use crate::hpke;
pub mod measure;
pub mod prio3;
pub mod receipt;
pub mod vdaf_vectors;
pub mod xof;

/// Why this extension refuses (§11), and the codes it reuses.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Reason {
    TaskInvalid,
    MeasureUnknown,
    MeasureMismatch,
    FloorTooLow,
    WrongSigner,
    CollectionInvalid,
    StatementInvalid,
    AggregatorAlone,
    TaskMismatch,
    BatchInvalid,
    StatementsDisagree,
    BatchTooSmall,
    ShareInvalid,
    ResultMismatch,
    BatchMismatch,
    CellMismatch,
    MeasurementKeyInDelivery,
    // Reused: the receipt families' reading and signature codes (SPEC §7).
    Truncated,
    Malformed,
    UnknownVersion,
    BadKey,
    BadSignature,
}

impl Reason {
    /// The codes this extension adds, in §11's order.
    pub const ALL: [Reason; 17] = [
        Reason::TaskInvalid,
        Reason::MeasureUnknown,
        Reason::MeasureMismatch,
        Reason::FloorTooLow,
        Reason::WrongSigner,
        Reason::CollectionInvalid,
        Reason::StatementInvalid,
        Reason::AggregatorAlone,
        Reason::TaskMismatch,
        Reason::BatchInvalid,
        Reason::StatementsDisagree,
        Reason::BatchTooSmall,
        Reason::ShareInvalid,
        Reason::ResultMismatch,
        Reason::BatchMismatch,
        Reason::CellMismatch,
        Reason::MeasurementKeyInDelivery,
    ];

    /// The codes of other texts this extension raises: the reading and signature codes,
    /// and DAP's report errors and problem types the reference aggregators raise.
    pub const REUSED: [&'static str; 11] = [
        "truncated",
        "malformed",
        "unknown_version",
        "bad_signature",
        "vdaf_verify_error",
        "hpke_decrypt_error",
        "report_replayed",
        "invalidBatchSize",
        "batchMismatch",
        "batchOverlap",
        "batchInvalid",
    ];

    pub fn code(self) -> &'static str {
        match self {
            Reason::TaskInvalid => "task_invalid",
            Reason::MeasureUnknown => "measure_unknown",
            Reason::MeasureMismatch => "measure_mismatch",
            Reason::FloorTooLow => "floor_too_low",
            Reason::WrongSigner => "wrong_signer",
            Reason::CollectionInvalid => "collection_invalid",
            Reason::StatementInvalid => "statement_invalid",
            Reason::AggregatorAlone => "aggregator_alone",
            Reason::TaskMismatch => "task_mismatch",
            Reason::BatchInvalid => "batch_invalid",
            Reason::StatementsDisagree => "statements_disagree",
            Reason::BatchTooSmall => "batch_too_small",
            Reason::ShareInvalid => "share_invalid",
            Reason::ResultMismatch => "result_mismatch",
            Reason::BatchMismatch => "batch_mismatch",
            Reason::CellMismatch => "cell_mismatch",
            Reason::MeasurementKeyInDelivery => "measurement_key_in_delivery",
            Reason::Truncated => "truncated",
            Reason::Malformed => "malformed",
            Reason::UnknownVersion => "unknown_version",
            Reason::BadKey => "bad_key",
            Reason::BadSignature => "bad_signature",
        }
    }
}

impl std::fmt::Display for Reason {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.code())
    }
}

impl std::error::Error for Reason {}
