//! Pack source tracking for policy attribution.
//!
//! Tracks which Trust Pack each assertion came from, enabling policy source
//! attribution in scan output (e.g., "Source: Acme Security Standard (a1b2c3d4)").
//!
//! # Storage Layout
//!
//! | Key Pattern | Value | Purpose |
//! |-------------|-------|---------|
//! | `{subject}\x00PKS:` | JSON-serialized PackSourceInfo | Pack source for assertion |
//!
//! Subject-prefixed for co-location with assertion data during range scans.

use crate::error::Result;
use crate::key_codec;
use crate::traits::KVStore;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tracing::{debug, instrument};

/// Information about a Trust Pack that provided a policy assertion.
///
/// Used to show provenance in conflict reports, e.g.:
/// "Source: Acme Security Standard v0.1.0 (Platform Security Team)"
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackSourceInfo {
    /// Name of the Trust Pack (e.g., "Acme Security Standard").
    pub pack_name: String,

    /// Version of the Trust Pack (e.g., "0.1.0").
    pub pack_version: String,

    /// First 8 hex characters of the issuer's public key.
    pub issuer_hex: String,

    /// Human-readable name of the signer (e.g., "Platform Security Team").
    /// Falls back to issuer_hex for display if None.
    #[serde(default)]
    pub signer_name: Option<String>,

    /// Contact info for the signer (e.g., "#security-policy", "security@acme.com").
    #[serde(default)]
    pub contact: Option<String>,
}

/// Specialized storage trait for pack source tracking.
///
/// Enables policy source attribution by storing which Trust Pack
/// each assertion came from.
///
/// # Example
///
/// ```ignore
/// let pack_source_store = GenericPackSourceStore::new(kv_store);
///
/// // Store pack source during policy import
/// let info = PackSourceInfo {
///     pack_name: "Acme Security Standard".to_string(),
///     pack_version: "1.0.0".to_string(),
///     issuer_hex: "a1b2c3d4".to_string(),
/// };
/// pack_source_store.set_pack_source("rfc://5246/tls/cert_verification", &info).await?;
///
/// // Look up pack source during conflict detection
/// let source = pack_source_store.get_pack_source("rfc://5246/tls/cert_verification").await?;
/// ```
#[async_trait]
pub trait PackSourceStore: Send + Sync {
    /// Store the pack source for an assertion.
    ///
    /// Associates a subject (assertion path) with the Trust Pack it came from.
    /// Idempotent: storing the same subject again overwrites the previous value.
    ///
    /// # Arguments
    /// * `subject` - The assertion subject (concept path)
    /// * `info` - Information about the Trust Pack
    async fn set_pack_source(&self, subject: &str, info: &PackSourceInfo) -> Result<()>;

    /// Get the pack source for an assertion.
    ///
    /// Returns the Trust Pack info if the assertion came from a policy,
    /// or None if it came from the hardcoded corpus or wasn't found.
    ///
    /// # Arguments
    /// * `subject` - The assertion subject (concept path)
    async fn get_pack_source(&self, subject: &str) -> Result<Option<PackSourceInfo>>;
}

/// PackSourceStore implementation backed by a generic KVStore.
///
/// Uses JSON serialization for simplicity and debuggability.
pub struct GenericPackSourceStore<S> {
    store: S,
}

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

#[async_trait]
impl<S: KVStore + 'static> PackSourceStore for GenericPackSourceStore<S> {
    #[instrument(skip(self, info), fields(subject = %subject, pack_name = %info.pack_name))]
    async fn set_pack_source(&self, subject: &str, info: &PackSourceInfo) -> Result<()> {
        let key = key_codec::pack_source_key(subject);

        // Serialize to JSON
        let value = serde_json::to_vec(info).map_err(|e| {
            crate::error::StorageError::Serialization(format!(
                "Failed to serialize pack source: {}",
                e
            ))
        })?;

        self.store.put(&key, &value).await?;

        debug!(subject, pack_name = %info.pack_name, "Stored pack source");
        Ok(())
    }

    #[instrument(skip(self), fields(subject = %subject))]
    async fn get_pack_source(&self, subject: &str) -> Result<Option<PackSourceInfo>> {
        let key = key_codec::pack_source_key(subject);

        match self.store.get(&key).await? {
            Some(value) => {
                let info: PackSourceInfo = serde_json::from_slice(&value).map_err(|e| {
                    crate::error::StorageError::Serialization(format!(
                        "Failed to deserialize pack source: {}",
                        e
                    ))
                })?;
                debug!(subject, pack_name = %info.pack_name, "Found pack source");
                Ok(Some(info))
            }
            None => {
                debug!(subject, "No pack source found");
                Ok(None)
            }
        }
    }
}

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

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

        let subject = "rfc://5246/tls/cert_verification";
        let info = PackSourceInfo {
            pack_name: "Acme Security Standard".to_string(),
            pack_version: "1.0.0".to_string(),
            issuer_hex: "a1b2c3d4".to_string(),
            signer_name: Some("Platform Security Team".to_string()),
            contact: Some("#security-policy".to_string()),
        };

        // Store pack source
        pack_source_store.set_pack_source(subject, &info).await.expect("set");

        // Retrieve pack source
        let retrieved = pack_source_store.get_pack_source(subject).await.expect("get");
        assert!(retrieved.is_some());

        let retrieved_info = retrieved.expect("info");
        assert_eq!(retrieved_info.pack_name, "Acme Security Standard");
        assert_eq!(retrieved_info.pack_version, "1.0.0");
        assert_eq!(retrieved_info.issuer_hex, "a1b2c3d4");
    }

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

        let result = pack_source_store.get_pack_source("nonexistent://path").await.expect("get");
        assert!(result.is_none(), "Should return None for nonexistent subject");
    }

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

        let subject = "code://rust/myapp/config";
        let info1 = PackSourceInfo {
            pack_name: "Pack V1".to_string(),
            pack_version: "1.0.0".to_string(),
            issuer_hex: "aaaabbbb".to_string(),
            signer_name: None,
            contact: None,
        };
        let info2 = PackSourceInfo {
            pack_name: "Pack V2".to_string(),
            pack_version: "2.0.0".to_string(),
            issuer_hex: "ccccdddd".to_string(),
            signer_name: Some("New Team".to_string()),
            contact: Some("new@example.com".to_string()),
        };

        // Store first version
        pack_source_store.set_pack_source(subject, &info1).await.expect("set 1");

        // Overwrite with second version
        pack_source_store.set_pack_source(subject, &info2).await.expect("set 2");

        // Should retrieve second version
        let retrieved = pack_source_store.get_pack_source(subject).await.expect("get");
        let retrieved_info = retrieved.expect("info");
        assert_eq!(retrieved_info.pack_name, "Pack V2");
        assert_eq!(retrieved_info.pack_version, "2.0.0");
    }

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

        let info1 = PackSourceInfo {
            pack_name: "TLS Policy".to_string(),
            pack_version: "1.0.0".to_string(),
            issuer_hex: "11112222".to_string(),
            signer_name: None,
            contact: None,
        };
        let info2 = PackSourceInfo {
            pack_name: "JWT Policy".to_string(),
            pack_version: "2.0.0".to_string(),
            issuer_hex: "33334444".to_string(),
            signer_name: None,
            contact: None,
        };

        pack_source_store.set_pack_source("rfc://5246/tls", &info1).await.expect("set 1");
        pack_source_store.set_pack_source("rfc://7519/jwt", &info2).await.expect("set 2");

        // Each subject should have its own pack source
        let tls_info = pack_source_store.get_pack_source("rfc://5246/tls").await.expect("get 1");
        assert_eq!(tls_info.expect("tls").pack_name, "TLS Policy");

        let jwt_info = pack_source_store.get_pack_source("rfc://7519/jwt").await.expect("get 2");
        assert_eq!(jwt_info.expect("jwt").pack_name, "JWT Policy");
    }
}
