use std::io::Write;
use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use second_brain_core::kuzu_store::KuzuStore;
use second_brain_core::schema::{SyncNodeType, SyncOp};
#[derive(Debug, Serialize, Deserialize)]
pub struct SyncRecord {
pub seq: u64,
pub op: SyncOp,
pub node_type: SyncNodeType,
pub node_id: String,
pub machine_id: String,
pub timestamp: DateTime<Utc>,
pub data: Option<serde_json::Value>,
}
pub fn export_changes<W: Write>(store: &KuzuStore, after_seq: u64, writer: &mut W) -> Result<u64> {
let entries = store.sync_log_since(after_seq)?;
let mut max_seq = after_seq;
let mut records: Vec<SyncRecord> = entries
.into_iter()
.map(|e| {
let data = e.data.as_ref().and_then(|d| serde_json::from_str(d).ok());
SyncRecord {
seq: e.seq,
op: e.op,
node_type: e.node_type,
node_id: e.node_id,
machine_id: e.machine_id,
timestamp: e.timestamp,
data,
}
})
.collect();
sort_for_import(&mut records);
for record in &records {
serde_json::to_writer(&mut *writer, record)?;
writer.write_all(b"\n")?;
if record.seq > max_seq {
max_seq = record.seq;
}
}
Ok(max_seq)
}
pub fn export_to_stdout(store: &KuzuStore, after_seq: u64) -> Result<u64> {
let stdout = std::io::stdout();
let mut handle = stdout.lock();
export_changes(store, after_seq, &mut handle)
}
fn sort_for_import(records: &mut [SyncRecord]) {
records.sort_by(|a, b| {
let type_order = |r: &SyncRecord| -> u8 {
match (&r.op, &r.node_type) {
(SyncOp::Delete, SyncNodeType::Relation) => 0,
(SyncOp::Delete, SyncNodeType::Memory) => 1,
(SyncOp::Delete, SyncNodeType::Entity) => 2,
(SyncOp::Delete, SyncNodeType::Conversation) => 3,
(SyncOp::Create, SyncNodeType::Conversation) => 4,
(SyncOp::Create, SyncNodeType::Entity) => 5,
(SyncOp::Create, SyncNodeType::Memory) => 6,
(SyncOp::Create, SyncNodeType::Relation) => 7,
(SyncOp::Update, SyncNodeType::Conversation) => 8,
(SyncOp::Update, SyncNodeType::Entity) => 9,
(SyncOp::Update, SyncNodeType::Memory) => 10,
(SyncOp::Update, SyncNodeType::Relation) => 11,
}
};
type_order(a).cmp(&type_order(b)).then(a.seq.cmp(&b.seq))
});
}