//! Specialized storage for TrustPack curation lists.
//!
//! The TrustPack system enables the "App Store for Trust" model where users
//! subscribe to curated lists of trusted agents to filter consensus through
//! domain expertise.
//!
//! # Storage Layout
//!
//! | Key Pattern | Value | Purpose |
//! |-------------|-------|---------|
//! | `\x00TP:{pack_id}` | Serialized TrustPack | Pack definition and agent membership |
//!
//! # Design Philosophy
//!
//! This module follows the "Deep Module" principle from Philosophy of Software Design:
//! - Simple interface hiding pack management complexity
//! - Content-addressed pack IDs (BLAKE3 hash)
//! - Idempotent agent add/remove operations
//! - Efficient membership checks
//!
//! All operations are defensive against missing data (missing pack returns None).

use crate::error::{Result, StorageError};
use crate::key_codec;
use crate::traits::KVStore;
use async_trait::async_trait;
use stemedb_core::types::{PackId, TrustPack};
use tracing::{debug, instrument};

/// Specialized storage trait for TrustPack operations.
///
/// This trait provides pack-specific operations on top of a generic KVStore,
/// enabling efficient trust pack management for curated consensus filtering.
///
/// # Example
///
/// ```ignore
/// let pack_store = GenericTrustPackStore::new(kv_store);
/// let pack_id = pack_store.put_pack(&pack).await?;
/// pack_store.add_agent_to_pack(&pack_id, &agent_id).await?;
/// let is_member = pack_store.is_agent_in_pack(&pack_id, &agent_id).await?;
/// ```
#[async_trait]
pub trait TrustPackStore: Send + Sync {
    /// Store a TrustPack and return its ID.
    ///
    /// This operation serializes the pack and stores it at `TP:{pack_id}`.
    ///
    /// # Arguments
    /// * `pack` - The TrustPack to store
    ///
    /// # Returns
    /// The pack's ID (from the pack itself, not computed)
    async fn put_pack(&self, pack: &TrustPack) -> Result<PackId>;

    /// Get a TrustPack by its ID.
    ///
    /// # Arguments
    /// * `pack_id` - Content-addressed pack ID
    ///
    /// # Returns
    /// The pack if found, None otherwise
    async fn get_pack(&self, pack_id: &PackId) -> Result<Option<TrustPack>>;

    /// Add an agent to a pack.
    ///
    /// This operation:
    /// 1. Loads the pack
    /// 2. Adds the agent (idempotent)
    /// 3. Stores the updated pack
    ///
    /// # Arguments
    /// * `pack_id` - ID of the pack to modify
    /// * `agent_id` - Agent to add
    ///
    /// # Errors
    /// Returns error if pack does not exist
    async fn add_agent_to_pack(&self, pack_id: &PackId, agent_id: &[u8; 32]) -> Result<()>;

    /// Remove an agent from a pack.
    ///
    /// This operation:
    /// 1. Loads the pack
    /// 2. Removes the agent (no-op if not present)
    /// 3. Stores the updated pack
    ///
    /// # Arguments
    /// * `pack_id` - ID of the pack to modify
    /// * `agent_id` - Agent to remove
    ///
    /// # Errors
    /// Returns error if pack does not exist
    async fn remove_agent_from_pack(&self, pack_id: &PackId, agent_id: &[u8; 32]) -> Result<()>;

    /// Check if an agent is a member of a pack.
    ///
    /// # Arguments
    /// * `pack_id` - Pack to check
    /// * `agent_id` - Agent to check for
    ///
    /// # Returns
    /// `true` if agent is in pack, `false` if not or pack doesn't exist
    async fn is_agent_in_pack(&self, pack_id: &PackId, agent_id: &[u8; 32]) -> Result<bool>;

    /// List all TrustPack IDs in the store.
    ///
    /// Scans all keys with prefix `TP:` and extracts pack IDs.
    ///
    /// # Returns
    /// Vector of pack IDs, empty if no packs exist
    async fn list_packs(&self) -> Result<Vec<PackId>>;
}

/// TrustPackStore implementation backed by a generic KVStore.
///
/// This implementation stores TrustPack data at `TP:{pack_id}` and provides
/// all operations for pack management and agent membership.
pub struct GenericTrustPackStore<S> {
    store: S,
}

impl<S: KVStore> GenericTrustPackStore<S> {
    /// Create a new TrustPackStore backed by the given KVStore.
    pub fn new(store: S) -> Self {
        Self { store }
    }

    /// Serialize a TrustPack using the canonical serde helpers.
    fn serialize_pack(pack: &TrustPack) -> Result<Vec<u8>> {
        crate::serde_helpers::serialize(pack)
    }

    /// Deserialize a TrustPack using the canonical serde helpers.
    fn deserialize_pack(data: &[u8]) -> Result<TrustPack> {
        crate::serde_helpers::deserialize(data)
    }
}

#[async_trait]
impl<S: KVStore + 'static> TrustPackStore for GenericTrustPackStore<S> {
    #[instrument(skip(self, pack), fields(pack_id = %hex::encode(pack.id), pack_name = %pack.name, agent_count = pack.agent_count()))]
    async fn put_pack(&self, pack: &TrustPack) -> Result<PackId> {
        let serialized = Self::serialize_pack(pack)?;
        let key = key_codec::trust_pack_key(&pack.id);
        self.store.put(&key, &serialized).await?;

        debug!(
            maintainer = %hex::encode(pack.maintainer),
            "Stored TrustPack"
        );

        Ok(pack.id)
    }

    #[instrument(skip(self), fields(pack_id = %hex::encode(pack_id)))]
    async fn get_pack(&self, pack_id: &PackId) -> Result<Option<TrustPack>> {
        let key = key_codec::trust_pack_key(pack_id);
        match self.store.get(&key).await? {
            Some(data) => {
                let pack = Self::deserialize_pack(&data)?;
                debug!(
                    pack_name = %pack.name,
                    agent_count = pack.agent_count(),
                    "Retrieved TrustPack"
                );
                Ok(Some(pack))
            }
            None => {
                debug!("TrustPack not found");
                Ok(None)
            }
        }
    }

    #[instrument(skip(self), fields(pack_id = %hex::encode(pack_id), agent_id = %hex::encode(agent_id)))]
    async fn add_agent_to_pack(&self, pack_id: &PackId, agent_id: &[u8; 32]) -> Result<()> {
        let mut pack = self.get_pack(pack_id).await?.ok_or_else(|| {
            StorageError::NotFound(format!("Pack not found: {}", hex::encode(pack_id)))
        })?;

        let old_count = pack.agent_count();
        pack.add_agent(*agent_id);
        let new_count = pack.agent_count();

        let serialized = Self::serialize_pack(&pack)?;
        let key = key_codec::trust_pack_key(pack_id);
        self.store.put(&key, &serialized).await?;

        debug!(old_count, new_count, added = new_count > old_count, "Updated pack membership");

        Ok(())
    }

    #[instrument(skip(self), fields(pack_id = %hex::encode(pack_id), agent_id = %hex::encode(agent_id)))]
    async fn remove_agent_from_pack(&self, pack_id: &PackId, agent_id: &[u8; 32]) -> Result<()> {
        let mut pack = self.get_pack(pack_id).await?.ok_or_else(|| {
            StorageError::NotFound(format!("Pack not found: {}", hex::encode(pack_id)))
        })?;

        let old_count = pack.agent_count();
        pack.remove_agent(agent_id);
        let new_count = pack.agent_count();

        let serialized = Self::serialize_pack(&pack)?;
        let key = key_codec::trust_pack_key(pack_id);
        self.store.put(&key, &serialized).await?;

        debug!(old_count, new_count, removed = new_count < old_count, "Updated pack membership");

        Ok(())
    }

    #[instrument(skip(self), fields(pack_id = %hex::encode(pack_id), agent_id = %hex::encode(agent_id)))]
    async fn is_agent_in_pack(&self, pack_id: &PackId, agent_id: &[u8; 32]) -> Result<bool> {
        match self.get_pack(pack_id).await? {
            Some(pack) => {
                let is_member = pack.contains_agent(agent_id);
                debug!(is_member, "Checked pack membership");
                Ok(is_member)
            }
            None => {
                debug!("Pack not found, returning false");
                Ok(false)
            }
        }
    }

    #[instrument(skip(self))]
    async fn list_packs(&self) -> Result<Vec<PackId>> {
        let prefix = key_codec::trust_pack_scan_prefix();
        let entries = self.store.scan_prefix(&prefix).await?;

        let pack_ids: Vec<PackId> = entries
            .into_iter()
            .filter_map(|(key, _data)| {
                // Extract pack_id from key: "\x00TP:{pack_id}"
                // Key format: \x00 (1 byte) + "TP:" (3 bytes) + pack_id (32 bytes) = 36 bytes
                if key.len() == 36 {
                    let mut pack_id = [0u8; 32];
                    pack_id.copy_from_slice(&key[4..]); // Skip \x00TP:
                    Some(pack_id)
                } else {
                    None
                }
            })
            .collect();

        debug!(count = pack_ids.len(), "Listed TrustPacks");
        Ok(pack_ids)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::HybridStore;
    use std::sync::Arc;

    fn create_test_pack(id: PackId, name: &str, maintainer: [u8; 32]) -> TrustPack {
        TrustPack::new(id, name.to_string(), maintainer)
    }

    #[tokio::test]
    async fn test_put_and_get_pack() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let pack_id = [1u8; 32];
        let maintainer = [2u8; 32];
        let pack = create_test_pack(pack_id, "Test_Pack", maintainer);

        // Put pack
        let returned_id = pack_store.put_pack(&pack).await.expect("Failed to put pack");
        assert_eq!(returned_id, pack_id);

        // Get pack back
        let retrieved = pack_store.get_pack(&pack_id).await.expect("Failed to get pack");

        assert!(retrieved.is_some());
        let retrieved_pack = retrieved.expect("Pack should exist");
        assert_eq!(retrieved_pack.id, pack_id);
        assert_eq!(retrieved_pack.name, "Test_Pack");
        assert_eq!(retrieved_pack.maintainer, maintainer);
    }

    #[tokio::test]
    async fn test_add_agent_idempotent() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let pack_id = [2u8; 32];
        let pack = create_test_pack(pack_id, "Agent_Pack", [3u8; 32]);
        pack_store.put_pack(&pack).await.expect("put");

        let agent_id = [10u8; 32];

        // Add agent first time
        pack_store.add_agent_to_pack(&pack_id, &agent_id).await.expect("add");
        let pack = pack_store.get_pack(&pack_id).await.expect("get").expect("pack exists");
        assert_eq!(pack.agent_count(), 1);
        assert!(pack.contains_agent(&agent_id));

        // Add same agent again (idempotent)
        pack_store.add_agent_to_pack(&pack_id, &agent_id).await.expect("add");
        let pack = pack_store.get_pack(&pack_id).await.expect("get").expect("pack exists");
        assert_eq!(pack.agent_count(), 1, "Should still have only 1 agent");
    }

    #[tokio::test]
    async fn test_remove_agent() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let pack_id = [3u8; 32];
        let mut pack = create_test_pack(pack_id, "Remove_Pack", [4u8; 32]);

        let agent1 = [20u8; 32];
        let agent2 = [21u8; 32];
        pack.add_agent(agent1);
        pack.add_agent(agent2);

        pack_store.put_pack(&pack).await.expect("put");

        // Remove agent1
        pack_store.remove_agent_from_pack(&pack_id, &agent1).await.expect("remove");

        let pack = pack_store.get_pack(&pack_id).await.expect("get").expect("pack exists");
        assert_eq!(pack.agent_count(), 1);
        assert!(!pack.contains_agent(&agent1));
        assert!(pack.contains_agent(&agent2));

        // Remove non-existent agent (no-op)
        let agent3 = [22u8; 32];
        pack_store.remove_agent_from_pack(&pack_id, &agent3).await.expect("remove");
        let pack = pack_store.get_pack(&pack_id).await.expect("get").expect("pack exists");
        assert_eq!(pack.agent_count(), 1, "Count should be unchanged");
    }

    #[tokio::test]
    async fn test_contains_agent() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let pack_id = [4u8; 32];
        let mut pack = create_test_pack(pack_id, "Contains_Pack", [5u8; 32]);

        let agent = [30u8; 32];
        pack.add_agent(agent);
        pack_store.put_pack(&pack).await.expect("put");

        // Check present agent
        let is_member = pack_store.is_agent_in_pack(&pack_id, &agent).await.expect("check");
        assert!(is_member);

        // Check absent agent
        let other_agent = [31u8; 32];
        let is_member = pack_store.is_agent_in_pack(&pack_id, &other_agent).await.expect("check");
        assert!(!is_member);
    }

    #[tokio::test]
    async fn test_list_packs() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        // Initially empty
        let packs = pack_store.list_packs().await.expect("list");
        assert!(packs.is_empty());

        // Add several packs
        for i in 0..5 {
            let pack_id = [i; 32];
            let pack = create_test_pack(pack_id, &format!("Pack_{}", i), [i + 100; 32]);
            pack_store.put_pack(&pack).await.expect("put");
        }

        // List all packs
        let packs = pack_store.list_packs().await.expect("list");
        assert_eq!(packs.len(), 5);

        // Verify IDs are correct
        for i in 0..5 {
            let expected_id = [i; 32];
            assert!(packs.contains(&expected_id), "Should contain pack {}", i);
        }
    }

    #[tokio::test]
    async fn test_missing_pack_returns_none() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let nonexistent_id = [99u8; 32];

        // Get non-existent pack
        let result = pack_store.get_pack(&nonexistent_id).await.expect("get");
        assert!(result.is_none(), "Should return None, not error");

        // is_agent_in_pack returns false for non-existent pack
        let agent = [1u8; 32];
        let is_member = pack_store.is_agent_in_pack(&nonexistent_id, &agent).await.expect("check");
        assert!(!is_member, "Should return false for non-existent pack");
    }

    #[tokio::test]
    async fn test_add_to_missing_pack_errors() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let nonexistent_id = [98u8; 32];
        let agent = [1u8; 32];

        // Attempting to add agent to non-existent pack should error
        let result = pack_store.add_agent_to_pack(&nonexistent_id, &agent).await;
        assert!(result.is_err(), "Should error when pack doesn't exist");
    }

    #[tokio::test]
    async fn test_remove_from_missing_pack_errors() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let nonexistent_id = [97u8; 32];
        let agent = [1u8; 32];

        // Attempting to remove agent from non-existent pack should error
        let result = pack_store.remove_agent_from_pack(&nonexistent_id, &agent).await;
        assert!(result.is_err(), "Should error when pack doesn't exist");
    }

    #[tokio::test]
    async fn test_serialization_roundtrip() {
        let pack_id = [42u8; 32];
        let maintainer = [43u8; 32];
        let mut original = create_test_pack(pack_id, "Roundtrip_Pack", maintainer);

        original.add_agent([1u8; 32]);
        original.add_agent([2u8; 32]);
        original.add_agent([3u8; 32]);

        let serialized =
            GenericTrustPackStore::<HybridStore>::serialize_pack(&original).expect("serialize");
        let deserialized = GenericTrustPackStore::<HybridStore>::deserialize_pack(&serialized)
            .expect("deserialize");

        assert_eq!(original, deserialized);
        assert_eq!(deserialized.agent_count(), 3);
    }

    #[tokio::test]
    async fn test_multiple_agents() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let pack_store = GenericTrustPackStore::new(store);

        let pack_id = [5u8; 32];
        let pack = create_test_pack(pack_id, "Multi_Agent_Pack", [6u8; 32]);
        pack_store.put_pack(&pack).await.expect("put");

        // Add multiple agents
        for i in 40..50 {
            let agent = [i; 32];
            pack_store.add_agent_to_pack(&pack_id, &agent).await.expect("add");
        }

        let pack = pack_store.get_pack(&pack_id).await.expect("get").expect("pack exists");
        assert_eq!(pack.agent_count(), 10);

        // Verify all agents are present
        for i in 40..50 {
            let agent = [i; 32];
            assert!(pack.contains_agent(&agent), "Should contain agent {}", i);
        }
    }
}
