//! Voting and Trust Pack data structures.

use rkyv::{Archive, Deserialize, Serialize};

use super::{Hash, PackId};

/// A vote cast by an agent on an existing assertion.
///
/// This enables high-velocity consensus gathering without modifying the immutable assertion itself.
///
/// # Provenance Witness Fields
///
/// A vote is not just "I agree" - it's a cryptographic witness: "I saw this exact text
/// at this URL at this time." This enables browser extension products where votes
/// represent observations, not opinions.
///
/// - `source_url`: The URL where the claim was observed (e.g., "https://example.com/article")
/// - `observed_context`: Optional page snippet or surrounding text stored as bytes for
///   rkyv zero-copy compatibility (same pattern as `source_metadata` on Assertion)
#[derive(Archive, Deserialize, Serialize, Debug, Clone, PartialEq)]
#[archive(check_bytes)]
pub struct Vote {
    /// The hash of the assertion being voted on.
    pub assertion_hash: Hash,
    /// The Ed25519 Public Key of the voting agent.
    pub agent_id: [u8; 32],
    /// The weight of the vote (Confidence), 0.0 to 1.0.
    pub weight: f32,
    /// The Ed25519 Signature of the `assertion_hash`.
    pub signature: [u8; 64],
    /// The timestamp when the vote was cast (Unix epoch).
    pub timestamp: u64,
    /// The URL where the claim was observed (optional for backward compatibility).
    pub source_url: Option<String>,
    /// Optional context (page snippet, etc.) stored as bytes for rkyv zero-copy.
    /// Use `Vec<u8>` for rkyv compatibility, same pattern as `source_metadata` on Assertion.
    pub observed_context: Option<Vec<u8>>,
}

/// A curated list of trusted agents for filtering consensus.
///
/// Trust Packs are the "App Store for Trust" - users subscribe to packs
/// curated by domain experts to filter reality through trusted lenses.
///
/// # Storage Layout
///
/// | Key Pattern | Value | Purpose |
/// |-------------|-------|---------|
/// | `TP:{pack_id}` | Serialized TrustPack | Pack definition and agent list |
///
/// # Example
///
/// ```ignore
/// let pack = TrustPack::new([1u8; 32], "Science_Pack".to_string(), maintainer_key);
/// pack.add_agent(agent_key_1);
/// pack.add_agent(agent_key_2);
///
/// // Query with pack filter
/// GET /query?lens=Authority&trust_pack=Science_Pack
/// ```
#[derive(Archive, Deserialize, Serialize, Debug, Clone, PartialEq)]
#[archive(check_bytes)]
pub struct TrustPack {
    /// Content-addressed pack ID (BLAKE3 hash).
    pub id: PackId,

    /// Human-readable name (e.g., "Mayo_Clinic_Experts").
    pub name: String,

    /// Ed25519 public key of the pack maintainer/curator.
    pub maintainer: [u8; 32],

    /// Agent public keys in this pack.
    /// Future optimization: Replace with RoaringBitmap for O(1) membership.
    pub agents: Vec<[u8; 32]>,

    /// Unix timestamp when pack was created.
    pub created_at: u64,

    /// Unix timestamp of last modification.
    pub updated_at: u64,
}

impl TrustPack {
    /// Create a new TrustPack with the given ID, name, and maintainer.
    ///
    /// # Arguments
    /// * `id` - Content-addressed pack ID (BLAKE3 hash)
    /// * `name` - Human-readable name
    /// * `maintainer` - Ed25519 public key of the curator
    ///
    /// # Returns
    /// A new TrustPack with current timestamp and empty agent list
    pub fn new(id: PackId, name: String, maintainer: [u8; 32]) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);

        Self { id, name, maintainer, agents: Vec::new(), created_at: now, updated_at: now }
    }

    /// Add an agent to this pack (idempotent).
    ///
    /// If the agent is already in the pack, this is a no-op.
    /// Updates the `updated_at` timestamp.
    ///
    /// # Arguments
    /// * `agent_id` - Ed25519 public key of the agent to add
    pub fn add_agent(&mut self, agent_id: [u8; 32]) {
        if !self.agents.contains(&agent_id) {
            self.agents.push(agent_id);
            self.updated_at = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_secs())
                .unwrap_or(0);
        }
    }

    /// Remove an agent from this pack.
    ///
    /// If the agent is not in the pack, this is a no-op.
    /// Updates the `updated_at` timestamp if an agent was removed.
    ///
    /// # Arguments
    /// * `agent_id` - Ed25519 public key of the agent to remove
    pub fn remove_agent(&mut self, agent_id: &[u8; 32]) {
        if let Some(pos) = self.agents.iter().position(|id| id == agent_id) {
            self.agents.remove(pos);
            self.updated_at = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_secs())
                .unwrap_or(0);
        }
    }

    /// Check if an agent is a member of this pack.
    ///
    /// # Arguments
    /// * `agent_id` - Ed25519 public key to check
    ///
    /// # Returns
    /// `true` if the agent is in the pack, `false` otherwise
    pub fn contains_agent(&self, agent_id: &[u8; 32]) -> bool {
        self.agents.contains(agent_id)
    }

    /// Get the number of agents in this pack.
    ///
    /// # Returns
    /// The count of agents in the pack
    pub fn agent_count(&self) -> usize {
        self.agents.len()
    }
}
