import { describe, it, expect, afterEach } from 'vitest';
import { mkdtempSync, writeFileSync, existsSync, rmSync } from 'fs';
import { tmpdir, hostname } from 'os';
import { join } from 'path';
import { acquireWorkerLock } from './instance-lock.js';

const dirs: string[] = [];
function tmpDb(): string {
  const d = mkdtempSync(join(tmpdir(), 'maestro-lock-'));
  dirs.push(d);
  return join(d, 'maestro.db');
}

afterEach(() => {
  for (const d of dirs) rmSync(d, { recursive: true, force: true });
  dirs.length = 0;
  delete process.env['MAESTRO_FORCE_WORKER_LOCK'];
});

describe('acquireWorkerLock', () => {
  it('writes a lock file and release() removes it', () => {
    const db = tmpDb();
    const lock = acquireWorkerLock(db);
    expect(existsSync(lock.path)).toBe(true);
    lock.release();
    expect(existsSync(lock.path)).toBe(false);
    lock.release(); // idempotent
  });

  it('throws when a live process on this host already holds the lock', () => {
    const db = tmpDb();
    // process.ppid is alive (it spawned us) and is not our pid.
    writeFileSync(`${db}.worker.lock`, JSON.stringify({
      pid: process.ppid, host: hostname(), startedAt: new Date().toISOString(),
    }));
    expect(() => acquireWorkerLock(db)).toThrow(/already running/);
  });

  it('reclaims a stale lock from a dead pid', () => {
    const db = tmpDb();
    writeFileSync(`${db}.worker.lock`, JSON.stringify({
      pid: 2147483646, host: hostname(), startedAt: new Date().toISOString(),
    }));
    const lock = acquireWorkerLock(db); // must not throw
    expect(existsSync(lock.path)).toBe(true);
    lock.release();
  });

  it('MAESTRO_FORCE_WORKER_LOCK=1 overrides a live lock', () => {
    const db = tmpDb();
    writeFileSync(`${db}.worker.lock`, JSON.stringify({
      pid: process.ppid, host: hostname(), startedAt: new Date().toISOString(),
    }));
    process.env['MAESTRO_FORCE_WORKER_LOCK'] = '1';
    const lock = acquireWorkerLock(db);
    expect(existsSync(lock.path)).toBe(true);
    lock.release();
  });
});
