//! Authoritative corpus creation for Aphoria.
//!
//! Provides functions to create pre-built authoritative assertions
//! for common security patterns (TLS, JWT, CORS, etc.).

use std::time::{SystemTime, UNIX_EPOCH};

use blake3::Hasher;
use ed25519_dalek::{Signer, SigningKey};
use stemedb_core::types::{
    Assertion, HlcTimestamp, LifecycleStage, ObjectValue, SignatureEntry, SourceClass,
};

/// Get the current Unix timestamp in seconds.
///
/// This is the canonical timestamp function for Aphoria. Use this instead of
/// inline `SystemTime::now()` or `Utc::now().timestamp()` calls.
///
/// For millisecond precision, use `current_timestamp_millis()`.
pub fn current_timestamp() -> u64 {
    SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}

/// Get the current Unix timestamp in milliseconds.
///
/// Use this when millisecond precision is needed (e.g., performance timing).
pub fn current_timestamp_millis() -> u128 {
    SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis()).unwrap_or(0)
}

/// Create authoritative corpus from configured sources (RFC, OWASP, Vendor).
///
/// This builds the corpus using CorpusRegistry with offline mode enabled
/// to avoid network delays.
///
/// **NOTE FOR TESTS**: This function is deprecated for test use. Tests should use
/// `create_test_corpus()` instead to get a minimal, predictable corpus without
/// network dependencies.
pub async fn create_authoritative_corpus(signing_key: &SigningKey) -> Vec<Assertion> {
    use crate::config::CorpusConfig;
    use crate::corpus::CorpusRegistry;

    let config = CorpusConfig::default();
    let registry = CorpusRegistry::with_defaults(&config);
    let timestamp = current_timestamp();

    // Build in offline mode - vendor corpus should still work
    match registry.build_all(signing_key, timestamp, &config, true).await {
        Ok(result) => result.assertions,
        Err(e) => {
            tracing::warn!(error = %e, "Failed to build authoritative corpus, using empty corpus");
            Vec::new()
        }
    }
}

/// Create a signed authoritative assertion with additional metadata fields.
///
/// Like `create_authoritative_assertion`, but merges `extra_metadata` into the
/// `source_metadata` JSON. Use this to attach RFC section references, CWE IDs,
/// or other structured provenance data.
#[allow(clippy::too_many_arguments)]
pub fn create_authoritative_assertion_with_metadata(
    signing_key: &SigningKey,
    subject: &str,
    predicate: &str,
    object: ObjectValue,
    source_class: SourceClass,
    description: &str,
    timestamp: u64,
    extra_metadata: serde_json::Value,
) -> Assertion {
    // Compute source hash
    let mut hasher = Hasher::new();
    hasher.update(subject.as_bytes());
    hasher.update(predicate.as_bytes());
    hasher.update(description.as_bytes());
    let source_hash = *hasher.finalize().as_bytes();

    // Create signature
    let message = format!("{}:{}", subject, predicate);
    let signature = signing_key.sign(message.as_bytes());
    let verifying_key = signing_key.verifying_key();

    let signature_entry = SignatureEntry {
        agent_id: verifying_key.to_bytes(),
        signature: signature.to_bytes(),
        timestamp,
        version: 1,
    };

    // Build source_metadata: start with extras, then overwrite with base fields
    // so that base fields ("description", "source") can never be overridden.
    let mut metadata = if let serde_json::Value::Object(extra) = extra_metadata {
        serde_json::Value::Object(extra)
    } else {
        serde_json::json!({})
    };
    if let serde_json::Value::Object(ref mut map) = metadata {
        map.insert("description".to_string(), serde_json::Value::String(description.to_string()));
        map.insert(
            "source".to_string(),
            serde_json::Value::String("authoritative_corpus".to_string()),
        );
    }

    Assertion {
        subject: subject.to_string(),
        predicate: predicate.to_string(),
        object,
        parent_hash: None,
        source_hash,
        source_class,
        visual_hash: None,
        epoch: None,
        source_metadata: serde_json::to_vec(&metadata).ok(),
        narrative: None,
        lifecycle: LifecycleStage::Approved,
        signatures: vec![signature_entry],
        confidence: 1.0,
        timestamp,
        hlc_timestamp: HlcTimestamp::default(),
        vector: None,
    }
}

/// Create a signed authoritative assertion.
///
/// This helper is used by corpus builders to create signed assertions with
/// consistent structure and metadata.
pub fn create_authoritative_assertion(
    signing_key: &SigningKey,
    subject: &str,
    predicate: &str,
    object: ObjectValue,
    source_class: SourceClass,
    description: &str,
    timestamp: u64,
) -> Assertion {
    // Compute source hash
    let mut hasher = Hasher::new();
    hasher.update(subject.as_bytes());
    hasher.update(predicate.as_bytes());
    hasher.update(description.as_bytes());
    let source_hash = *hasher.finalize().as_bytes();

    // Create signature
    let message = format!("{}:{}", subject, predicate);
    let signature = signing_key.sign(message.as_bytes());
    let verifying_key = signing_key.verifying_key();

    let signature_entry = SignatureEntry {
        agent_id: verifying_key.to_bytes(),
        signature: signature.to_bytes(),
        timestamp,
        version: 1,
    };

    let source_metadata = serde_json::json!({
        "description": description,
        "source": "authoritative_corpus",
    });

    Assertion {
        subject: subject.to_string(),
        predicate: predicate.to_string(),
        object,
        parent_hash: None,
        source_hash,
        source_class,
        visual_hash: None,
        epoch: None,
        source_metadata: serde_json::to_vec(&source_metadata).ok(),
        narrative: None,
        lifecycle: LifecycleStage::Approved,
        signatures: vec![signature_entry],
        confidence: 1.0,
        timestamp,
        hlc_timestamp: HlcTimestamp::default(),
        vector: None,
    }
}

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

    #[test]
    fn test_create_authoritative_assertion_with_metadata_merges_fields() {
        let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
        let timestamp = current_timestamp();

        let extra_metadata = serde_json::json!({
            "rfc_section": "7519-4.1.3",
            "cwe_references": ["CWE-345", "CWE-346"],
            "severity": "high"
        });

        let assertion = create_authoritative_assertion_with_metadata(
            &signing_key,
            "rfc://test/subject",
            "test_predicate",
            ObjectValue::Boolean(true),
            SourceClass::Regulatory,
            "Test description",
            timestamp,
            extra_metadata,
        );

        // Extract and parse source_metadata
        let metadata_bytes = assertion.source_metadata.expect("metadata should exist");
        let metadata: serde_json::Value =
            serde_json::from_slice(&metadata_bytes).expect("should parse JSON");

        // Verify base fields are present
        assert_eq!(metadata["description"], "Test description");
        assert_eq!(metadata["source"], "authoritative_corpus");

        // Verify extra fields are merged
        assert_eq!(metadata["rfc_section"], "7519-4.1.3");
        assert_eq!(metadata["cwe_references"][0], "CWE-345");
        assert_eq!(metadata["cwe_references"][1], "CWE-346");
        assert_eq!(metadata["severity"], "high");
    }

    #[test]
    fn test_create_authoritative_assertion_with_metadata_preserves_base_fields() {
        let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
        let timestamp = current_timestamp();

        // Extra metadata shouldn't overwrite base fields if key collision
        let extra_metadata = serde_json::json!({
            "description": "Malicious override attempt",
            "custom_field": "allowed"
        });

        let assertion = create_authoritative_assertion_with_metadata(
            &signing_key,
            "rfc://test/subject",
            "test_predicate",
            ObjectValue::Boolean(true),
            SourceClass::Regulatory,
            "Original description",
            timestamp,
            extra_metadata,
        );

        let metadata_bytes = assertion.source_metadata.expect("metadata should exist");
        let metadata: serde_json::Value =
            serde_json::from_slice(&metadata_bytes).expect("should parse JSON");

        // Base fields always win over extra metadata
        assert_eq!(metadata["description"], "Original description");
        assert_eq!(metadata["source"], "authoritative_corpus");
        assert_eq!(metadata["custom_field"], "allowed");
    }

    #[test]
    fn test_create_authoritative_assertion_with_empty_metadata() {
        let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
        let timestamp = current_timestamp();

        let assertion = create_authoritative_assertion_with_metadata(
            &signing_key,
            "rfc://test/subject",
            "test_predicate",
            ObjectValue::Boolean(true),
            SourceClass::Regulatory,
            "Test description",
            timestamp,
            serde_json::json!({}),
        );

        let metadata_bytes = assertion.source_metadata.expect("metadata should exist");
        let metadata: serde_json::Value =
            serde_json::from_slice(&metadata_bytes).expect("should parse JSON");

        // Should still have base fields
        assert_eq!(metadata["description"], "Test description");
        assert_eq!(metadata["source"], "authoritative_corpus");
    }
}
