import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { setupFileRig, type FileTestRig } from './helpers/rig.js';
import {
  METRIC_OP_DURATION_MS,
  METRIC_OP_TOTAL,
  METRIC_RATE_LIMIT_REJECT_TOTAL,
  type MetricSink,
  type MetricTags,
  type FileEntry,
} from '../../src/index.js';

interface MetricEvent {
  name: string;
  value: number;
  tags: MetricTags;
}

describe('Metrics — onMetric on success', () => {
  let rig: FileTestRig;
  const events: MetricEvent[] = [];
  const sink: MetricSink = (name, value, tags) => events.push({ name, value, tags });

  beforeAll(async () => {
    rig = await setupFileRig({
      onMetric: sink,
      list: async () => ({ entries: [], hasMore: false }),
    });
  });
  afterAll(async () => { await rig.teardown(); });

  test('emits op_total + op_duration_ms with result=ok on success', async () => {
    events.length = 0;
    await rig.fs.list('/');
    const totals = events.filter((e) => e.name === METRIC_OP_TOTAL);
    expect(totals.length).toBeGreaterThanOrEqual(1);
    expect(totals[0]!.tags.result).toBe('ok');
    expect(totals[0]!.tags.op).toBe('list');
    const durations = events.filter((e) => e.name === METRIC_OP_DURATION_MS);
    expect(durations.length).toBeGreaterThanOrEqual(1);
    expect(durations[0]!.value).toBeGreaterThanOrEqual(0);
  });
});

describe('Metrics — onMetric rate-limit reject', () => {
  let rig: FileTestRig;
  const events: MetricEvent[] = [];
  const sink: MetricSink = (name, value, tags) => events.push({ name, value, tags });

  beforeAll(async () => {
    rig = await setupFileRig({
      onMetric: sink,
      rateLimits: { maxOpsPerMinutePerSender: 3 },
      stat: async (_ctx) => {
        const e: FileEntry = { name: 'x', kind: 'file', size: 1, mtime: 0, metadata: {} };
        return e;
      },
    });
  });
  afterAll(async () => { await rig.teardown(); });

  test('emits rate_limit_reject_total when capacity exhausted', async () => {
    events.length = 0;
    // Cap is 3; first 3 stats succeed, 4th rejected.
    await rig.fs.stat('/x');
    await rig.fs.stat('/x');
    await rig.fs.stat('/x');
    await expect(rig.fs.stat('/x')).rejects.toThrow();
    const rejects = events.filter((e) => e.name === METRIC_RATE_LIMIT_REJECT_TOTAL);
    expect(rejects.length).toBeGreaterThanOrEqual(1);
    expect(rejects[0]!.tags.op).toBe('stat');
  });
});
