'use client';

import type { ActivityItem } from '@pac/types';
import { api } from './api-client';

/**
 * 行为埋点(通用)—— `track(surface, action, payload?)`。
 *
 * ── 三条纪律 ──────────────────────────────────────────────
 * ⛔ **不传身份**。userId / role / orgScope 由服务端从 JWT 取;上报接口是 strictObject,
 *    多传一个字段会被直接拒掉。想在 payload 里塞 userId 也别塞 —— 那只会得到一份
 *    和 token 不一致时无从分辨的脏数据。
 * ⚠️ **绝不 await、绝不 throw**。埋点是旁路:网络挂了、后端 500 了,用户该点的照样点。
 *    调用方写 `track(...)` 不用 `void`,函数本身返回 void。
 * ⚠️ **合并发送**:同一次交互可能连打几条(open + matrix_cell),用微批把它们并成一个请求,
 *    否则主管点一下矩阵能出三个 XHR,既吵又抢主流程的连接。
 *
 * 未登录时静默丢弃 —— 没有 token 的上报服务端也认不出是谁,发了也是 401 噪音。
 */

const FLUSH_DELAY_MS = 400;
/// 一批最多 50 条,与服务端 TrackActivityRequestSchema 的上限对齐
const MAX_BATCH = 50;

let queue: ActivityItem[] = [];
let timer: ReturnType<typeof setTimeout> | null = null;

function flush() {
  timer = null;
  if (!queue.length) return;
  const items = queue.splice(0, MAX_BATCH);
  // 失败就算了:重试会在后端抖动时把队列越堆越长,而埋点丢几条无所谓
  // ⚠️ 路径要带全局前缀 —— api-client 用 `new URL(path, base)` 拼,不会自动补 /pac/v1
  void api.post('/pac/v1/activities', { items }).catch(() => {});
  if (queue.length) schedule();
}

function schedule() {
  if (timer) return;
  timer = setTimeout(flush, FLUSH_DELAY_MS);
}

export function track(
  surface: string,
  action: string,
  payload?: Record<string, unknown>,
): void {
  try {
    queue.push({ surface, action, payload, occurredAt: new Date().toISOString() });
    if (queue.length >= MAX_BATCH) flush();
    else schedule();
  } catch {
    /* 埋点永不影响调用方 */
  }
}

/// 页面卸载前把队列冲掉 —— 否则「打开了工作台就关掉」这条最关键的漏斗首格会丢。
if (typeof window !== 'undefined') {
  window.addEventListener('pagehide', () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    flush();
  });
}
