/**
 * Structural + behavioural tests for the WAL-flush / close helpers (#1376).
 *
 * After the review-driven refactor, the module exposes two layers:
 *   - flushWAL  — CHECKPOINT only (connection stays open)
 *   - safeClose — flushWAL + conn.close + db.close
 *
 * closeLbug delegates to safeClose for the CHECKPOINT + close step and
 * then resets module-level state (currentDbPath, ftsLoaded, etc.).
 *
 * The structural tests read the adapter source and verify delegation
 * contracts so a future refactor that inlines close logic is caught.
 *
 * The behavioural tests import flushWAL directly and exercise the
 * runtime null-guard path (conn is null at module load) so a future
 * refactor that accidentally throws is caught immediately.
 */
import { beforeAll, describe, expect, it } from 'vitest';
import fs from 'node:fs/promises';
import path from 'node:path';
import { flushWAL } from '../../src/core/lbug/lbug-adapter.js';

describe('flushWAL / safeClose — consolidation guard (#1376)', () => {
  let adapterSource: string;

  beforeAll(async () => {
    adapterSource = await fs.readFile(
      path.join(__dirname, '..', '..', 'src', 'core', 'lbug', 'lbug-adapter.ts'),
      'utf-8',
    );
  });

  it('exports flushWAL (CHECKPOINT-only helper)', () => {
    expect(adapterSource).toMatch(/export const flushWAL/);
  });

  it('exports safeClose (CHECKPOINT + close helper)', () => {
    expect(adapterSource).toMatch(/export const safeClose/);
  });

  it('safeClose delegates to flushWAL for the CHECKPOINT step', () => {
    const safeCloseBody = adapterSource.slice(adapterSource.indexOf('export const safeClose'));
    expect(safeCloseBody).toMatch(/await flushWAL\(\)/);
  });

  it('closeLbug delegates to safeClose instead of inlining conn.close/db.close', () => {
    const closeLbugBody = adapterSource.slice(adapterSource.indexOf('export const closeLbug'));
    expect(closeLbugBody).toMatch(/await safeClose\(\)/);
    // closeLbug must NOT contain its own conn.close() or db.close() — those
    // live exclusively inside safeClose now.
    const closeLbugBlock = closeLbugBody.slice(0, closeLbugBody.indexOf('export const', 1) >>> 0);
    expect(closeLbugBlock).not.toMatch(/conn\.close\(\)/);
    expect(closeLbugBlock).not.toMatch(/db\.close\(\)/);
  });

  it('CHECKPOINT is issued only by flushWAL (best-effort) and tryFlushWAL (rethrows for the retry driver)', () => {
    const matches = adapterSource.match(/conn\.query\('CHECKPOINT'\)/g) ?? [];
    // Two authorized sites: `flushWAL` (swallows errors — used by
    // `safeClose` and the server's best-effort flush) and `tryFlushWAL`
    // (rethrows so the manual checkpoint driver in `wal-checkpoint-driver.ts`
    // can apply its bounded retry). Any third occurrence is a regression —
    // a CHECKPOINT outside these two helpers will be invisible to the
    // retry/error policy.
    expect(matches.length).toBe(2);
  });

  it('exports tryFlushWAL (CHECKPOINT-with-rethrow for the manual retry driver)', () => {
    expect(adapterSource).toMatch(/export const tryFlushWAL/);
  });

  it('flushWAL drains and closes the CHECKPOINT result before returning', () => {
    const flushBody = adapterSource.slice(
      adapterSource.indexOf('export const flushWAL'),
      adapterSource.indexOf('export const safeClose'),
    );
    expect(flushBody).toMatch(/await drainQueryResult\(checkpointResult\)/);
  });

  it('conn.close() only appears inside safeClose (with eslint-disable)', () => {
    // Every conn.close() in the adapter must live inside safeClose, guarded
    // by the eslint-disable comment. Count occurrences to catch leaks.
    const matches = adapterSource.match(/await conn\.close\(\)/g) ?? [];
    expect(matches.length).toBe(1);
  });

  it('db.close() only appears inside safeClose (with eslint-disable)', () => {
    const matches = adapterSource.match(/await db\.close\(\)/g) ?? [];
    expect(matches.length).toBe(1);
  });
});

// Behavioural tests — exercise flushWAL at runtime rather than just
// grepping source text.  At module load `conn` is null, so these hit
// the early-return guard without needing a real LadybugDB instance.
describe('flushWAL — runtime behaviour', () => {
  it('resolves without error when no connection is open', async () => {
    // conn is null at module load — flushWAL must not throw.
    await expect(flushWAL()).resolves.toBeUndefined();
  });

  it('can be called repeatedly without throwing (idempotent)', async () => {
    await flushWAL();
    await flushWAL();
    // No assertion needed beyond "did not throw".
  });
});
