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

vi.mock('./logger.js', () => ({
  logger: {
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  },
}));

const mockExecSync = vi.fn();
vi.mock('child_process', () => ({
  execSync: (...args: unknown[]) => mockExecSync(...args),
}));

import {
  readonlyMountArgs,
  stopJail,
  ensureJailRunning,
  cleanupStaleJails,
} from './jail-ops.js';
import { logger } from './logger.js';

beforeEach(() => {
  vi.clearAllMocks();
});

describe('readonlyMountArgs', () => {
  it('returns nullfs readonly mount args', () => {
    const args = readonlyMountArgs('/host/path', '/jail/path');
    expect(args).toEqual(['-o', 'ro', '/host/path', '/jail/path']);
  });
});

describe('stopJail', () => {
  it('returns service jail stop command', () => {
    expect(stopJail('clawdie-cp-test-123')).toBe(
      'service jail stop clawdie-cp-test-123',
    );
  });
});

describe('ensureJailRunning', () => {
  it('does nothing (no daemon needed for jails)', () => {
    ensureJailRunning();
    expect(mockExecSync).not.toHaveBeenCalled();
    expect(logger.debug).toHaveBeenCalledWith(
      'Using FreeBSD jail runtime (no daemon needed)',
    );
  });
});

describe('cleanupStaleJails', () => {
  it('logs no stale mounts when output is empty', () => {
    mockExecSync.mockReturnValueOnce(''); // mount | grep clawdie-cp
    mockExecSync.mockReturnValueOnce(''); // jls -n

    cleanupStaleJails();

    expect(logger.debug).toHaveBeenCalledWith('No stale jail mounts found');
  });

  it('warns when stale nullfs mounts are found', () => {
    mockExecSync.mockReturnValueOnce(
      '/home/clawdie/clawdie-cp on /jails/clawdie-cp/home/clawdie/clawdie-cp (nullfs)\n',
    );
    mockExecSync.mockReturnValueOnce(''); // jls -n

    cleanupStaleJails();

    expect(logger.warn).toHaveBeenCalledWith(
      expect.objectContaining({ count: 1 }),
      expect.stringContaining('stale nullfs mounts'),
    );
  });

  it('warns when unexpected running jails are found', () => {
    mockExecSync.mockReturnValueOnce(''); // mount
    mockExecSync.mockReturnValueOnce(
      'name=clawdie-cp-group1-123 path=/jails/clawdie-cp-group1-123\n',
    );

    cleanupStaleJails();

    expect(logger.warn).toHaveBeenCalledWith(
      expect.objectContaining({ jails: expect.any(Array) }),
      expect.stringContaining('unexpected running jails'),
    );
  });

  it('continues when jls is unavailable', () => {
    mockExecSync.mockReturnValueOnce(''); // mount
    mockExecSync.mockImplementationOnce(() => {
      throw new Error('jls not found');
    });

    cleanupStaleJails(); // should not throw
  });
});
