import { describe, it, expect } from 'vitest';
import { ErrorLogBuffer } from '../src/services/error-log-buffer.js';

const line = (o: Record<string, unknown>): string => JSON.stringify(o) + '\n';

describe('ErrorLogBuffer', () => {
  it('keeps error/fatal records and drops info/warn', () => {
    const b = new ErrorLogBuffer();
    b.recordLine(line({ level: 30, time: 1, msg: 'info' }));
    b.recordLine(line({ level: 40, time: 2, msg: 'warn' }));
    b.recordLine(line({ level: 50, time: 3, msg: 'an error' }));
    b.recordLine(line({ level: 60, time: 4, msg: 'fatal', kind: 'BACKEND_TOKEN_DEAD' }));
    const recent = b.recent();
    expect(recent.map((e) => e.msg)).toEqual(['fatal', 'an error']); // most-recent first
    expect(recent[0]?.kind).toBe('BACKEND_TOKEN_DEAD');
  });

  it('flattens an err object to its message', () => {
    const b = new ErrorLogBuffer();
    b.recordLine(line({ level: 50, time: 1, msg: 'boom', err: { type: 'Error', message: 'OpenBao write … 403' } }));
    expect(b.recent()[0]?.err).toBe('OpenBao write … 403');
  });

  it('ignores blank and non-JSON lines', () => {
    const b = new ErrorLogBuffer();
    b.recordLine('');
    b.recordLine('not json at all');
    b.recordLine('   ');
    expect(b.recent()).toHaveLength(0);
  });

  it('caps at capacity (ring buffer)', () => {
    const b = new ErrorLogBuffer(3);
    for (let i = 1; i <= 5; i++) b.recordLine(line({ level: 50, time: i, msg: `e${i}` }));
    const recent = b.recent(10);
    expect(recent.map((e) => e.msg)).toEqual(['e5', 'e4', 'e3']); // oldest two evicted
  });

  it('respects the limit argument', () => {
    const b = new ErrorLogBuffer();
    for (let i = 1; i <= 10; i++) b.recordLine(line({ level: 50, time: i }));
    expect(b.recent(2)).toHaveLength(2);
  });

  it('handles multiple records in one chunk via the stream', (ctx) => new Promise<void>((resolve) => {
    const b = new ErrorLogBuffer();
    const s = b.stream();
    s.write(line({ level: 50, time: 1, msg: 'a' }) + line({ level: 30, time: 2 }) + line({ level: 60, time: 3, msg: 'b' }));
    s.end(() => {
      expect(b.recent().map((e) => e.msg)).toEqual(['b', 'a']);
      resolve();
    });
  }));
});
