use serde_json::{json, Value};
use std::time::Duration;
pub const POSTHOG_CAPTURE_URL: &str = "https://app.posthog.com/capture/";
pub const POSTHOG_API_KEY: &str = "phc_PIc9kLGrgCyqNVY2DrYeVS7mbDlw8Ywj7IDujZRjsY4";
pub fn build_capture_payload(
api_key: &str,
event: &str,
distinct_id: &str,
mut properties: Value,
) -> Value {
if let Some(obj) = properties.as_object_mut() {
obj.insert("$geoip_disable".to_string(), serde_json::Value::Bool(true));
}
json!({
"api_key": api_key,
"event": event,
"distinct_id": distinct_id,
"properties": properties,
})
}
pub fn send_event_background(
url: &str,
api_key: &str,
event: &str,
distinct_id: &str,
properties: Value,
) -> std::thread::JoinHandle<()> {
let payload = build_capture_payload(api_key, event, distinct_id, properties);
let url = url.to_string();
std::thread::spawn(move || {
let body = match serde_json::to_string(&payload) {
Ok(b) => b,
Err(_) => return,
};
let _ = crate::http::post_json(&url, &[], &body, Duration::from_secs(10));
})
}
pub fn capture(event: &str, distinct_id: &str, properties: Value) -> std::thread::JoinHandle<()> {
send_event_background(
POSTHOG_CAPTURE_URL,
POSTHOG_API_KEY,
event,
distinct_id,
properties,
)
}
static PENDING: std::sync::Mutex<Vec<std::thread::JoinHandle<()>>> =
std::sync::Mutex::new(Vec::new());
pub fn capture_queued(event: &str, distinct_id: &str, properties: Value) {
let handle = capture(event, distinct_id, properties);
if let Ok(mut pending) = PENDING.lock() {
pending.push(handle);
}
}
pub fn flush() {
if let Ok(mut pending) = PENDING.lock() {
let handles: Vec<_> = pending.drain(..).collect();
drop(pending); let deadline = std::time::Instant::now() + Duration::from_secs(5);
for handle in handles {
let remaining = deadline.saturating_duration_since(std::time::Instant::now());
if remaining.is_zero() {
break;
}
let _ = handle.join();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_build_capture_payload() {
let props = json!({ "score": 87.5, "grade": "B+" });
let payload = build_capture_payload("api-key-123", "analysis_complete", "user-abc", props);
assert_eq!(payload["api_key"], "api-key-123");
assert_eq!(payload["event"], "analysis_complete");
assert_eq!(payload["distinct_id"], "user-abc");
assert_eq!(payload["properties"]["score"], 87.5);
assert_eq!(payload["properties"]["grade"], "B+");
assert_eq!(payload["properties"]["$geoip_disable"], true);
}
#[test]
fn test_capture_does_not_block() {
let start = std::time::Instant::now();
send_event_background(
"http://127.0.0.1:1", "phc_test",
"test_event",
"test-user",
json!({ "test": true }),
);
let elapsed = start.elapsed();
assert!(
elapsed.as_millis() < 100,
"send_event_background blocked for {}ms",
elapsed.as_millis()
);
}
}