import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';

vi.mock('../../src/env', () => ({ env: { boxIdleMs: 1000 } }));
vi.mock('../../src/runtime/docker', () => ({
  ensureUserContainer: vi.fn((args: { username: string }) =>
    Promise.resolve(`spoon-box-${args.username}`),
  ),
  stopWorkspaceContainer: vi.fn(() => Promise.resolve()),
  userContainerName: (username: string) => `spoon-box-${username}`,
  listWorkspaceContainerNames: vi.fn(() => Promise.resolve([])),
}));

const load = async () => await import('../../src/user-container');

describe('box registry', () => {
  beforeEach(() => vi.useFakeTimers());
  afterEach(async () => {
    const module = await load();
    module._resetBoxRegistryForTests();
    vi.useRealTimers();
    vi.resetModules();
    vi.clearAllMocks();
  });

  test('serializes concurrent acquire into a single docker run', async () => {
    const module = await load();
    const docker = await import('../../src/runtime/docker');
    const [a, b] = await Promise.all([
      module.acquireUserBox({
        username: 'alice',
        workdir: '/w',
        containerHome: '/home/alice',
      }),
      module.acquireUserBox({
        username: 'alice',
        workdir: '/w',
        containerHome: '/home/alice',
      }),
    ]);
    expect(docker.ensureUserContainer).toHaveBeenCalledTimes(1);
    expect(a.boxName).toBe('spoon-box-alice');
    a.release();
    vi.advanceTimersByTime(2000);
    expect(docker.stopWorkspaceContainer).not.toHaveBeenCalled();
    b.release();
    vi.advanceTimersByTime(2000);
    expect(docker.stopWorkspaceContainer).toHaveBeenCalledTimes(1);
  });

  test('double-release of one handle does not reap a box held elsewhere', async () => {
    const module = await load();
    const docker = await import('../../src/runtime/docker');
    const a = await module.acquireUserBox({
      username: 'bob',
      workdir: '/w',
      containerHome: '/home/bob',
    });
    const b = await module.acquireUserBox({
      username: 'bob',
      workdir: '/w',
      containerHome: '/home/bob',
    });
    a.release();
    a.release();
    vi.advanceTimersByTime(2000);
    expect(docker.stopWorkspaceContainer).not.toHaveBeenCalled();
    b.release();
    vi.advanceTimersByTime(2000);
    expect(docker.stopWorkspaceContainer).toHaveBeenCalledTimes(1);
  });

  test('reconcileExistingBoxes adopts orphans with zero refs and reaps them', async () => {
    const module = await load();
    const docker = await import('../../src/runtime/docker');
    (
      docker.listWorkspaceContainerNames as unknown as ReturnType<typeof vi.fn>
    ).mockResolvedValueOnce(['spoon-box-carol']);
    await module.reconcileExistingBoxes();
    expect(module.runningBoxUsernames().has('carol')).toBe(true);
    vi.advanceTimersByTime(2000);
    expect(docker.stopWorkspaceContainer).toHaveBeenCalledWith(
      'spoon-box-carol',
    );
  });
});
