// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use serde::{Deserialize, Serialize};

use super::rpc_response_context::RpcResponseContext;

/// One active feature: its name and the bank slot at which the serving node's
/// per-block snapshot recorded it active.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ActiveFeatureEntry {
    /// Feature name.
    pub name: String,
    /// Bank slot at snapshot time (informational, not the historical
    /// activation slot).
    pub snapshot_slot: u64,
}

/// Response for `getActiveFeatures`: the serving node's current active-feature
/// snapshot.
///
/// `value` is sorted by `name` so the active-feature *membership* is directly
/// comparable across honest replicas — the basis for the cross-validator
/// agreement check. The wire response also carries per-entry `snapshot_slot`
/// and the wrapper `context.slot`, both node-local values that legitimately
/// differ across replicas; the agreement check compares only the name set.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct GetActiveFeaturesResponse {
    pub context: RpcResponseContext,
    pub value: Vec<ActiveFeatureEntry>,
}

impl GetActiveFeaturesResponse {
    pub fn new(slot: u64, value: Vec<ActiveFeatureEntry>) -> Self {
        Self {
            context: RpcResponseContext::new(slot),
            value,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serializes_with_context_and_value() {
        let resp = GetActiveFeaturesResponse::new(
            100,
            vec![ActiveFeatureEntry {
                name: "feature_a".to_string(),
                snapshot_slot: 50,
            }],
        );
        let json = serde_json::to_string(&resp).unwrap();
        assert!(json.contains("\"feature_a\""));
        assert!(json.contains("\"snapshot_slot\":50"));
    }

    #[test]
    fn round_trips() {
        let resp = GetActiveFeaturesResponse::new(
            7,
            vec![
                ActiveFeatureEntry {
                    name: "a".to_string(),
                    snapshot_slot: 1,
                },
                ActiveFeatureEntry {
                    name: "b".to_string(),
                    snapshot_slot: 2,
                },
            ],
        );
        let json = serde_json::to_string(&resp).unwrap();
        let back: GetActiveFeaturesResponse = serde_json::from_str(&json).unwrap();
        assert_eq!(resp, back);
    }
}
