//! Agent types for simulation testing.

use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use stemedb_core::types::{
    Assertion, Hash, HlcTimestamp, LifecycleStage, ObjectValue, SignatureEntry, SourceClass, Vote,
};

/// A simulated agent with a cryptographic identity.
pub(crate) struct Agent {
    pub id: String,
    signing_key: SigningKey,
    verifying_key: VerifyingKey,
}

impl Agent {
    pub fn new(id: &str) -> Self {
        let mut csprng = OsRng;
        let signing_key = SigningKey::generate(&mut csprng);
        let verifying_key = VerifyingKey::from(&signing_key);
        Self { id: id.to_string(), signing_key, verifying_key }
    }

    /// Get the agent's public key as a 32-byte array.
    pub fn public_key(&self) -> [u8; 32] {
        self.verifying_key.to_bytes()
    }

    /// Create and sign an assertion with custom lifecycle and timestamp.
    pub fn sign_assertion_with_options(
        &self,
        subject: &str,
        predicate: &str,
        object: ObjectValue,
        lifecycle: LifecycleStage,
        custom_timestamp: Option<u64>,
    ) -> Assertion {
        let timestamp = custom_timestamp.unwrap_or_else(|| {
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_secs())
                .unwrap_or(0)
        });

        // For simulation, we sign the concatenation of subject and predicate.
        // In a real system, we'd sign the hash of the fact data.
        let message = format!("{}:{}", subject, predicate);
        let signature: Signature = self.signing_key.sign(message.as_bytes());

        Assertion {
            subject: subject.to_string(),
            predicate: predicate.to_string(),
            object,
            parent_hash: None,
            source_hash: [0u8; 32],
            source_class: SourceClass::Expert,
            visual_hash: None,
            epoch: None,
            source_metadata: None,
            lifecycle,
            signatures: vec![SignatureEntry {
                agent_id: self.verifying_key.to_bytes(),
                signature: signature.to_bytes(),
                timestamp,
                version: 1,
            }],
            confidence: 1.0,
            timestamp,
            hlc_timestamp: HlcTimestamp::default(),
            vector: None,
        }
    }

    /// Create and sign a vote for an assertion.
    ///
    /// The agent signs the assertion_hash to prove authenticity.
    pub fn vote(&self, assertion_hash: Hash, weight: f32) -> Vote {
        let timestamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);

        // Sign the assertion hash to prove we're voting on this specific assertion
        let signature: Signature = self.signing_key.sign(&assertion_hash);

        Vote {
            assertion_hash,
            agent_id: self.verifying_key.to_bytes(),
            weight,
            signature: signature.to_bytes(),
            timestamp,
            source_url: None,
            observed_context: None,
        }
    }
}
