//! Global audit event collector.
//!
//! Subscribes to `provisioning.audit.>` via JetStream durable consumer,
//! buffers events, and batch-inserts into SurrealDB `audit:events`.
//! Flush triggers: 100 events accumulated OR 1-second timer, whichever first.

use std::sync::Arc;
use std::time::Duration;

use futures::StreamExt;
use platform_db::SurrealPool;
use platform_nats::NatsBridge;
use serde::{Deserialize, Serialize};
use tokio::time;
use tracing::{error, info, warn};

const BATCH_SIZE: usize = 100;
const FLUSH_INTERVAL: Duration = Duration::from_secs(1);
const STREAM_NAME: &str = "AUDIT";
const CONSUMER_NAME: &str = "audit-collector";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditRecord {
    pub source_service: String,
    pub event_type: String,
    pub actor: Option<String>,
    pub target: Option<String>,
    pub session_id: Option<String>,
    pub workspace_id: Option<String>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub payload: serde_json::Value,
}

/// Runs the audit collector loop until the process shuts down.
///
/// Intended to be spawned as a background `tokio::task`.
pub async fn run_audit_collector(nats: Arc<NatsBridge>, db: Arc<SurrealPool>) {
    let mut ticker = time::interval(FLUSH_INTERVAL);
    let mut buffer: Vec<AuditRecord> = Vec::with_capacity(BATCH_SIZE);

    let mut messages = match nats.subscribe_pull(STREAM_NAME, CONSUMER_NAME).await {
        Ok(m) => m,
        Err(e) => {
            error!("audit collector: failed to subscribe — {e}");
            return;
        }
    };

    info!("audit collector running (batch={BATCH_SIZE}, flush=1s)");

    loop {
        tokio::select! {
            msg = messages.next() => {
                match msg {
                    Some(Ok(m)) => {
                        match serde_json::from_slice::<AuditRecord>(&m.payload) {
                            Ok(event) => {
                                buffer.push(event);
                                if buffer.len() >= BATCH_SIZE {
                                    let batch = std::mem::take(&mut buffer);
                                    flush_batch(batch, Arc::clone(&db)).await;
                                }
                                if let Err(e) = m.ack().await {
                                    warn!("audit collector: ack failed — {e}");
                                }
                            }
                            Err(e) => {
                                warn!("audit collector: deserialize failed — {e}");
                                let _ = m.ack().await;
                            }
                        }
                    }
                    Some(Err(e)) => {
                        error!("audit collector: message error — {e}");
                    }
                    None => break,
                }
            }
            _ = ticker.tick() => {
                if !buffer.is_empty() {
                    let batch = std::mem::take(&mut buffer);
                    flush_batch(batch, Arc::clone(&db)).await;
                }
            }
        }
    }
}

async fn flush_batch(events: Vec<AuditRecord>, db: Arc<SurrealPool>) {
    let surreal = db.db();
    let count = events.len();

    for event in events {
        let id = uuid::Uuid::new_v4().to_string();
        let record = match serde_json::to_value(&event) {
            Ok(v) => v,
            Err(e) => {
                error!("audit flush: serialize failed — {e}");
                continue;
            }
        };

        let result = surreal
            .query("USE NS audit DB provisioning; CREATE type::thing('events', $id) CONTENT $record")
            .bind(("id", id))
            .bind(("record", record))
            .await
            .map_err(|e| format!("transport: {e}"))
            .and_then(|r| r.check().map_err(|e| format!("statement: {e}")));

        if let Err(e) = result {
            error!("audit flush: INSERT failed — {e}");
        }
    }

    info!(count = count, "audit events flushed to SurrealDB");
}
