// Client-side accumulator + beacon for local move-preview adoption telemetry. It counts a
// few events — the app cold start, dictionary loads by result, and move previews by path —
// and posts the running deltas to the edge periodically and when the app is backgrounded,
// so the backend can chart adoption (docs/ARCHITECTURE.md §11). Best-effort throughout: a
// failed beacon keeps its counts to retry, and nothing here throws into the caller. It lives
// outside the lazy dict subsystem so the cold start is recorded even when no game is opened.

import { gateway } from './gateway';

interface Counts {
  cold_start: number;
  dict_fetched: number;
  dict_cache_hit: number;
  dict_miss: number;
  preview_local: number;
  preview_network: number;
}

function zero(): Counts {
  return { cold_start: 0, dict_fetched: 0, dict_cache_hit: 0, dict_miss: 0, preview_local: 0, preview_network: 0 };
}

let counts = zero();
let started = false;

function total(c: Counts): number {
  return c.cold_start + c.dict_fetched + c.dict_cache_hit + c.dict_miss + c.preview_local + c.preview_network;
}

// flush posts the accumulated deltas and resets them; on failure it folds the batch back in
// so the next flush retries. Best-effort.
async function flush(): Promise<void> {
  if (total(counts) === 0) return;
  const batch = counts;
  counts = zero();
  try {
    await gateway.reportLocalEval({ ...batch });
  } catch {
    counts.cold_start += batch.cold_start;
    counts.dict_fetched += batch.dict_fetched;
    counts.dict_cache_hit += batch.dict_cache_hit;
    counts.dict_miss += batch.dict_miss;
    counts.preview_local += batch.preview_local;
    counts.preview_network += batch.preview_network;
  }
}

/**
 * startLocalEvalMetrics records the app cold start and begins the periodic beacon. Called once
 * at launch (a repeat call is a no-op). It also flushes when the app is backgrounded, so a
 * batch is not lost when the tab is closed.
 */
export function startLocalEvalMetrics(): void {
  if (started) return;
  started = true;
  counts.cold_start += 1;
  setInterval(() => void flush(), 60_000);
  if (typeof document !== 'undefined') {
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') void flush();
    });
  }
}

/** noteDictFetched: a dictionary was downloaded and cached (an IndexedDB fill). */
export function noteDictFetched(): void {
  counts.dict_fetched += 1;
}
/** noteDictCacheHit: a dictionary loaded from the IndexedDB cache (no download). */
export function noteDictCacheHit(): void {
  counts.dict_cache_hit += 1;
}
/** noteDictMiss: a warm-up did not produce a usable dictionary (failed or timed out). */
export function noteDictMiss(): void {
  counts.dict_miss += 1;
}
/** notePreviewLocal: a move preview was computed on-device. */
export function notePreviewLocal(): void {
  counts.preview_local += 1;
}
/** notePreviewNetwork: a move preview fell back to the network. */
export function notePreviewNetwork(): void {
  counts.preview_network += 1;
}
