//! Basic ingestion tests for assertions, votes, and epochs.

use super::*;
use stemedb_storage::key_codec;

#[tokio::test]
async fn test_ingest_assertion() {
    let dir = tempdir().expect("Failed to create temp dir");
    let wal_dir = dir.path().join("wal");
    let db_dir = dir.path().join("db");

    let mut journal = Journal::open(&wal_dir).expect("Failed to open journal");
    let store = HybridStore::open(&db_dir).expect("Failed to open store");

    let assertion = create_test_assertion();
    let payload = serialize_assertion(&assertion).expect("Failed to serialize");
    journal.append(payload).expect("Failed to append");

    let journal = Arc::new(Mutex::new(journal));
    let store = Arc::new(store);
    let mut worker =
        IngestWorker::new(journal, store.clone()).await.expect("Failed to create worker");

    let bytes = worker.step().await.expect("Failed to step");
    assert!(bytes > 0, "Should have processed data");

    // Verify assertion was stored via subjects discovery index
    let subj_key = key_codec::subjects_index_key("Tesla_Inc");
    let entry = store.get(&subj_key).await.expect("Failed to get");
    assert!(entry.is_some(), "Subjects index should contain Tesla_Inc");

    // Verify assertion count was incremented
    let count_key = key_codec::assertion_count_key();
    let count_bytes = store.get(&count_key).await.expect("count").expect("count should exist");
    let count = u64::from_le_bytes(count_bytes.try_into().expect("8 bytes"));
    assert_eq!(count, 1, "Should have one assertion");
}

#[tokio::test]
async fn test_ingest_vote() {
    let dir = tempdir().expect("Failed to create temp dir");
    let wal_dir = dir.path().join("wal");
    let db_dir = dir.path().join("db");

    let mut journal = Journal::open(&wal_dir).expect("Failed to open journal");
    let store = HybridStore::open(&db_dir).expect("Failed to open store");

    // First ingest an assertion so the vote has a valid target
    let assertion = create_test_assertion();
    let a_payload = serialize_assertion(&assertion).expect("Failed to serialize");
    journal.append(a_payload).expect("Failed to append assertion");

    // Now create a vote - we need the assertion's hash to reference it.
    // The worker computes BLAKE3 of the serialized data, so we do the same
    // to get the correct hash for the vote's assertion_hash field.
    let assertion_data = serialize_assertion(&assertion).expect("ser");
    // The record_types module prepends an 8-byte header [type_byte, padding...],
    // and the worker hashes only the data portion (after the header).
    // We need to extract what the worker will hash. The header is 8 bytes.
    let data_portion = &assertion_data[8..];
    let assertion_hash: [u8; 32] = *blake3::hash(data_portion).as_bytes();

    let vote = Vote {
        assertion_hash,
        agent_id: [2u8; 32],
        weight: 0.8,
        signature: [3u8; 64],
        timestamp: 2000,
        source_url: None,
        observed_context: None,
    };
    let v_payload = serialize_vote(&vote).expect("Failed to serialize vote");
    journal.append(v_payload).expect("Failed to append vote");

    let journal = Arc::new(Mutex::new(journal));
    let store = Arc::new(store);
    let mut worker =
        IngestWorker::new(journal, store.clone()).await.expect("Failed to create worker");

    // Process assertion first
    let bytes1 = worker.step().await.expect("Failed to step assertion");
    assert!(bytes1 > 0, "Should have processed assertion");

    // Process vote
    let bytes2 = worker.step().await.expect("Failed to step vote");
    assert!(bytes2 > 0, "Should have processed vote");

    // Verify vote was stored via subject-prefixed vote scan
    let vote_prefix = key_codec::vote_scan_prefix("Tesla_Inc", &hex::encode(assertion_hash));
    let votes = store.scan_prefix(&vote_prefix).await.expect("Failed to scan");
    assert_eq!(votes.len(), 1, "Should have one vote");
}

#[tokio::test]
async fn test_ingest_epoch() {
    let dir = tempdir().expect("Failed to create temp dir");
    let wal_dir = dir.path().join("wal");
    let db_dir = dir.path().join("db");

    let mut journal = Journal::open(&wal_dir).expect("Failed to open journal");
    let store = HybridStore::open(&db_dir).expect("Failed to open store");

    let epoch = create_test_epoch();
    let payload = serialize_epoch(&epoch).expect("Failed to serialize");
    journal.append(payload).expect("Failed to append");

    let journal = Arc::new(Mutex::new(journal));
    let store = Arc::new(store);
    let mut worker =
        IngestWorker::new(journal, store.clone()).await.expect("Failed to create worker");

    let bytes = worker.step().await.expect("Failed to step");
    assert!(bytes > 0);

    // Verify epoch was stored with \x00E: prefix
    let epoch_key = key_codec::epoch_key(&hex::encode(epoch.id));
    let entry = store.get(&epoch_key).await.expect("Failed to get");
    assert!(entry.is_some(), "Epoch should be stored");
}

#[tokio::test]
async fn test_ingest_multiple_records() {
    let dir = tempdir().expect("Failed to create temp dir");
    let wal_dir = dir.path().join("wal");
    let db_dir = dir.path().join("db");

    let mut journal = Journal::open(&wal_dir).expect("Failed to open journal");
    let store = HybridStore::open(&db_dir).expect("Failed to open store");

    let assertion = create_test_assertion();
    let epoch = create_test_epoch();

    journal.append(serialize_assertion(&assertion).expect("ser")).expect("append");
    journal.append(serialize_epoch(&epoch).expect("ser")).expect("append");

    let journal = Arc::new(Mutex::new(journal));
    let store = Arc::new(store);
    let mut worker =
        IngestWorker::new(journal, store.clone()).await.expect("Failed to create worker");

    let mut total = 0;
    loop {
        let bytes = worker.step().await.expect("Failed to step");
        if bytes == 0 {
            break;
        }
        total += bytes;
    }

    assert!(total > 0, "Should have processed data");

    // Verify assertion was stored
    let count_key = key_codec::assertion_count_key();
    let count_bytes = store.get(&count_key).await.expect("count").expect("count exists");
    let count = u64::from_le_bytes(count_bytes.try_into().expect("8 bytes"));
    assert_eq!(count, 1, "Should have one assertion");

    // Verify epoch was stored
    let epoch_key = key_codec::epoch_key(&hex::encode(epoch.id));
    let entry = store.get(&epoch_key).await.expect("get");
    assert!(entry.is_some(), "Epoch should be stored");
}
