//! 客户端打点（10H）：内存队列，满 20 条或 60s 批量上报；失败丢弃不影响功能。

use parking_lot::Mutex;
use serde_json::{json, Value};
use std::sync::Arc;
use std::time::Duration;
use tauri::{AppHandle, Manager};

/// 协议平台标识（18H）：mac / win / linux（区别于 std::env::consts::OS 的 macos/windows）。
const PLATFORM: &str = if cfg!(target_os = "macos") {
    "mac"
} else if cfg!(target_os = "windows") {
    "win"
} else {
    "linux"
};

#[derive(Clone)]
pub struct Metrics {
    queue: Arc<Mutex<Vec<Value>>>,
}

impl Metrics {
    pub fn record(&self, event: &str, props: Value) {
        let mut q = self.queue.lock();
        q.push(json!({
            "event": event,
            "props": props,
            "client_ts": std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH).map(|d| d.as_millis() as i64).unwrap_or(0),
        }));
        if q.len() > 1000 {
            let overflow = q.len() - 1000;
            q.drain(..overflow); // 上限丢最旧
        }
    }
}

pub fn spawn(app: AppHandle) -> Metrics {
    let m = Metrics {
        queue: Arc::new(Mutex::new(Vec::new())),
    };
    let queue = m.queue.clone();
    tauri::async_runtime::spawn(async move {
        let client = reqwest::Client::new();
        let mut tick = tokio::time::interval(Duration::from_secs(10));
        loop {
            tick.tick().await;
            let (events, url, device_id) = {
                let mut q = queue.lock();
                if q.is_empty() || (q.len() < 20 && tickcount_skip(&q)) {
                    continue;
                }
                let events: Vec<Value> = q.drain(..).collect();
                let s = app.state::<crate::settings::SettingsStore>().get();
                (events, format!("{}/v1/metrics/batch", s.server_url), s.device_id)
            };
            let body = json!({
                "device_id": device_id,
                // 协议平台标识 mac/win/linux（18H，区别于 std::env::consts::OS 的 macos/windows）。
                "platform": PLATFORM,
                "app_version": env!("CARGO_PKG_VERSION"),
                "os_version": "",
                "events": events,
            });
            let _ = client.post(&url).json(&body).send().await; // 失败即丢弃
        }
    });
    m
}

// 不满 20 条时按 60s 节奏（tick 10s × 6 次取模近似）上报；保持实现极简。
fn tickcount_skip(_q: &[Value]) -> bool {
    use std::sync::atomic::{AtomicU64, Ordering};
    static N: AtomicU64 = AtomicU64::new(0);
    N.fetch_add(1, Ordering::Relaxed) % 6 != 0
}
