//! Transcript archive export and verification.
//!
//! Wraps `quicproquo_core::transcript` to provide SDK-level functions for
//! exporting conversation messages to an encrypted, tamper-evident archive
//! and verifying archive integrity.

use std::fs;
use std::path::Path;

use quicproquo_core::transcript::{
    read_transcript, validate_transcript_structure, ChainVerdict, TranscriptRecord,
    TranscriptWriter,
};

use crate::conversation::{ConversationId, ConversationStore};

/// Export all messages in a conversation to an encrypted, tamper-evident archive.
///
/// Uses Argon2id key derivation from `password` and ChaCha20-Poly1305 for AEAD
/// encryption. Each record is chained via SHA-256 of the previous ciphertext.
///
/// Returns the number of records written.
pub fn export_transcript(
    store: &ConversationStore,
    conversation_id: &ConversationId,
    path: &Path,
    password: &str,
) -> anyhow::Result<u64> {
    // Use a large limit to get all messages (sorted oldest-first by load_recent_messages).
    let messages = store.load_recent_messages(conversation_id, usize::MAX)?;

    let mut buf = Vec::new();
    let mut writer = TranscriptWriter::new(password, &mut buf)
        .map_err(|e| anyhow::anyhow!("create transcript writer: {e}"))?;

    let mut count = 0u64;
    for msg in &messages {
        let sender_key = if msg.sender_key.len() == 32 {
            &msg.sender_key[..]
        } else {
            &[0u8; 32][..]
        };

        writer
            .write_record(
                &TranscriptRecord {
                    seq: count,
                    sender_identity: sender_key,
                    timestamp_ms: msg.timestamp_ms,
                    plaintext: &msg.body,
                },
                &mut buf,
            )
            .map_err(|e| anyhow::anyhow!("write record {count}: {e}"))?;

        count += 1;
    }

    fs::write(path, &buf)?;

    Ok(count)
}

/// Verify an exported transcript archive.
///
/// If `password` is provided, performs full decryption and hash-chain
/// verification. Otherwise, only checks structural integrity (file format,
/// record boundaries).
///
/// Returns the chain verdict.
pub fn verify_transcript(
    path: &Path,
    password: Option<&str>,
) -> anyhow::Result<ChainVerdict> {
    let data = fs::read(path)?;

    if let Some(pw) = password {
        let (_records, verdict) = read_transcript(pw, &data)
            .map_err(|e| anyhow::anyhow!("verify transcript: {e}"))?;
        Ok(verdict)
    } else {
        let verdict = validate_transcript_structure(&data)
            .map_err(|e| anyhow::anyhow!("validate structure: {e}"))?;
        Ok(verdict)
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;
    use crate::conversation::{Conversation, ConversationKind, StoredMessage};
    use tempfile::TempDir;

    fn setup() -> (TempDir, ConversationStore) {
        let dir = TempDir::new().unwrap();
        let db_path = dir.path().join("test.db");
        let store = ConversationStore::open(&db_path, None).unwrap();
        (dir, store)
    }

    fn save_conv(store: &ConversationStore, conv_id: &ConversationId, name: &str) {
        let conv = Conversation {
            id: conv_id.clone(),
            kind: ConversationKind::Group {
                name: name.to_string(),
            },
            display_name: name.to_string(),
            mls_group_blob: None,
            keystore_blob: None,
            member_keys: vec![],
            unread_count: 0,
            last_activity_ms: 0,
            created_at_ms: 0,
            is_hybrid: false,
            last_seen_seq: 0,
        };
        store.save_conversation(&conv).unwrap();
    }

    fn save_msg(store: &ConversationStore, conv_id: &ConversationId, body: &str, ts: u64) {
        let msg = StoredMessage {
            conversation_id: conv_id.clone(),
            message_id: None,
            sender_key: vec![0xAAu8; 32],
            sender_name: None,
            body: body.to_string(),
            msg_type: "chat".to_string(),
            ref_msg_id: None,
            timestamp_ms: ts,
            is_outgoing: false,
        };
        store.save_message(&msg).unwrap();
    }

    #[test]
    fn export_and_verify_round_trip() {
        let (dir, store) = setup();

        let conv_id = ConversationId::from_group_name("test-group");
        save_conv(&store, &conv_id, "test-group");
        save_msg(&store, &conv_id, "Hello!", 1000);
        save_msg(&store, &conv_id, "World!", 2000);

        let archive_path = dir.path().join("transcript.qpqt");
        let count = export_transcript(&store, &conv_id, &archive_path, "test-pw").unwrap();
        assert_eq!(count, 2);

        // Full verification with password.
        let verdict = verify_transcript(&archive_path, Some("test-pw")).unwrap();
        assert_eq!(verdict, ChainVerdict::Ok { records: 2 });

        // Structural verification without password.
        let verdict = verify_transcript(&archive_path, None).unwrap();
        assert_eq!(verdict, ChainVerdict::Ok { records: 2 });
    }

    #[test]
    fn wrong_password_fails_verification() {
        let (dir, store) = setup();

        let conv_id = ConversationId::from_group_name("pw-test");
        save_conv(&store, &conv_id, "pw-test");
        save_msg(&store, &conv_id, "secret", 1000);

        let archive_path = dir.path().join("transcript_pw.qpqt");
        export_transcript(&store, &conv_id, &archive_path, "correct").unwrap();

        let result = verify_transcript(&archive_path, Some("wrong"));
        assert!(result.is_err());
    }

    #[test]
    fn empty_conversation_exports_empty_archive() {
        let (dir, store) = setup();

        let conv_id = ConversationId::from_group_name("empty");
        save_conv(&store, &conv_id, "empty");

        let archive_path = dir.path().join("empty.qpqt");
        let count = export_transcript(&store, &conv_id, &archive_path, "pw").unwrap();
        assert_eq!(count, 0);

        let verdict = verify_transcript(&archive_path, Some("pw")).unwrap();
        assert_eq!(verdict, ChainVerdict::Ok { records: 0 });
    }
}
