// Per-request D1 timing accumulator + named phase timings.
// CF Workers executes one request at a time per isolate, so globalThis is safe.
// Reset by middleware before route handler runs, read after for Server-Timing header.

export interface D1TimingContext {
  queries: number;
  sqlMs: number; // cumulative meta.duration (D1 SQL execution time)
  wallMs: number; // cumulative performance.now() delta (includes D1 RTT)
  rowsRead: number;
  rowsWritten: number;
  /** Named phase timings — for fine-grained Server-Timing breakdown */
  phases: Record<string, number>;
}

const EMPTY: D1TimingContext = {
  queries: 0,
  sqlMs: 0,
  wallMs: 0,
  rowsRead: 0,
  rowsWritten: 0,
  phases: {},
};

export function resetD1Timing(): void {
  (globalThis as any).__d1Timing__ = { ...EMPTY, phases: {} };
}

export function getD1Timing(): D1TimingContext {
  return (globalThis as any).__d1Timing__ || { ...EMPTY, phases: {} };
}

/**
 * Measure a named phase and accumulate into the timing context.
 * Usage:
 *   const result = await measurePhase('chain', () => checkTokenBalance(...));
 *
 * Multiple calls to the same phase name accumulate (additive).
 */
export async function measurePhase<T>(name: string, fn: () => Promise<T>): Promise<T> {
  const t0 = performance.now();
  try {
    return await fn();
  } finally {
    const t: D1TimingContext = (globalThis as any).__d1Timing__;
    if (t) {
      const dur = performance.now() - t0;
      t.phases[name] = (t.phases[name] || 0) + dur;
    }
  }
}

/** Record a phase duration directly (for non-Promise code paths). */
export function recordPhase(name: string, durationMs: number): void {
  const t: D1TimingContext = (globalThis as any).__d1Timing__;
  if (!t) return;
  t.phases[name] = (t.phases[name] || 0) + durationMs;
}

/** Record a single D1 call (.all / .run / .first) */
export function recordQuery(meta: any, wallMs: number): void {
  const t: D1TimingContext = (globalThis as any).__d1Timing__;
  if (!t) return;
  t.queries++;
  t.sqlMs += meta?.duration ?? 0;
  t.wallMs += wallMs;
  t.rowsRead += meta?.rows_read ?? 0;
  t.rowsWritten += meta?.rows_written ?? 0;
}

/** Record a db.batch() call — each result in the array has its own meta */
export function recordBatch(results: any[], wallMs: number): void {
  const t: D1TimingContext = (globalThis as any).__d1Timing__;
  if (!t) return;
  for (const r of results) {
    t.queries++;
    t.sqlMs += r?.meta?.duration ?? 0;
    t.rowsRead += r?.meta?.rows_read ?? 0;
    t.rowsWritten += r?.meta?.rows_written ?? 0;
  }
  t.wallMs += wallMs;
}
