/**
 * Unit tests for the worker-mode shutdown wiring extracted from
 * worker-bootstrap.ts. The full start() path is too IO-heavy to run
 * inside vitest (DB, ConfigManager, WorkerManager threads), so we
 * exercise the small registry-binding helper in isolation.
 *
 * What we verify:
 *  - Both hooks register against the shared shutdown registry.
 *  - Running shutdown drains them in the documented order
 *    (scheduler.stop → workerManager.stop → repo.close).
 *  - installSignalHandlers is idempotent so subsequent calls don't
 *    leak listeners (regression: the old per-handler pairs would.).
 */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { installWorkerShutdownHooks } from './worker-bootstrap.js';
import {
  runShutdown,
  __resetShutdownForTests,
  __getRegisteredHookCountForTests,
} from './bridge/shutdown.js';

describe('installWorkerShutdownHooks', () => {
  beforeEach(() => {
    __resetShutdownForTests({ exitFn: () => undefined });
  });

  it('registers two hooks against the shared shutdown registry', () => {
    const scheduler = { stop: vi.fn() };
    const workerManager = { stop: vi.fn(async () => undefined) };
    const repo = { close: vi.fn() };

    installWorkerShutdownHooks({ scheduler, workerManager, repo });

    expect(__getRegisteredHookCountForTests()).toBe(2);
  });

  it('drains both hooks on shutdown in the documented order', async () => {
    const calls: string[] = [];
    const scheduler = { stop: vi.fn(() => { calls.push('scheduler.stop'); }) };
    const workerManager = {
      stop: vi.fn(async () => { calls.push('workerManager.stop'); }),
    };
    const repo = { close: vi.fn(() => { calls.push('repo.close'); }) };

    installWorkerShutdownHooks({ scheduler, workerManager, repo });
    await runShutdown('TEST');

    // workerManager.stop MUST settle before repo.close — closing SQLite
    // mid-write would surface as SQLITE_BUSY on the in-flight job row.
    const wmIdx = calls.indexOf('workerManager.stop');
    const repoIdx = calls.indexOf('repo.close');
    expect(wmIdx).toBeGreaterThanOrEqual(0);
    expect(repoIdx).toBeGreaterThan(wmIdx);
    // scheduler is its own hook — runs concurrently with the worker
    // hook, no ordering requirement vs repo.close.
    expect(calls).toContain('scheduler.stop');
  });

  it('still drains gracefully when a hook throws', async () => {
    const scheduler = {
      stop: vi.fn(() => { throw new Error('boom'); }),
    };
    const workerManager = { stop: vi.fn(async () => undefined) };
    const repo = { close: vi.fn() };

    installWorkerShutdownHooks({ scheduler, workerManager, repo });
    // The shutdown driver catches per-hook errors and continues with the
    // remaining hooks — a busted scheduler can't keep the DB open.
    await runShutdown('TEST');

    expect(workerManager.stop).toHaveBeenCalledTimes(1);
    expect(repo.close).toHaveBeenCalledTimes(1);
  });
});
