import { describe, it, expect } from 'vitest';
import {
  SessionManager,
  selectOldestTaskSessionId,
  findIdleTaskSessions,
  CAPTCHA_POOL_SESSION_ID,
  type BrowserSession,
} from './browser-session.js';

function fakeSession(partial: Partial<BrowserSession> & Pick<BrowserSession, 'id' | 'kind' | 'lastActiveAt'>): BrowserSession {
  return {
    id: partial.id,
    kind: partial.kind,
    taskId: partial.taskId,
    userId: partial.userId,
    browser: null,
    context: null,
    vncPort: 0,
    novncPort: 0,
    userDataDir: '',
    state: 'ready',
    xvfbProcess: null,
    x11vncProcess: null,
    websockifyProcess: null,
    display: ':99',
    createdAt: partial.createdAt ?? partial.lastActiveAt,
    lastActiveAt: partial.lastActiveAt,
    lockedByJobId: partial.lockedByJobId ?? null,
    captchaPending: partial.captchaPending,
  };
}

describe('SessionManager', () => {
  it('should report availability based on system deps', () => {
    const result = SessionManager.isAvailable();
    expect(typeof result).toBe('boolean');
  });
});

describe('selectOldestTaskSessionId', () => {
  it('returns null when no task sessions exist', () => {
    const sessions = [
      fakeSession({ id: CAPTCHA_POOL_SESSION_ID, kind: 'pool', lastActiveAt: new Date(0) }),
    ];
    expect(selectOldestTaskSessionId(sessions)).toBeNull();
  });

  it('picks the task session with the smallest lastActiveAt', () => {
    const t0 = new Date('2026-01-01T00:00:00Z');
    const t1 = new Date('2026-01-01T01:00:00Z');
    const t2 = new Date('2026-01-01T02:00:00Z');
    const sessions = [
      fakeSession({ id: 'pool', kind: 'pool', lastActiveAt: t0 }),  // pool excluded
      fakeSession({ id: 'newer', kind: 'task', taskId: 'a', lastActiveAt: t2 }),
      fakeSession({ id: 'oldest', kind: 'task', taskId: 'b', lastActiveAt: t0 }),
      fakeSession({ id: 'middle', kind: 'task', taskId: 'c', lastActiveAt: t1 }),
    ];
    expect(selectOldestTaskSessionId(sessions)).toBe('oldest');
  });

  it('skips locked task sessions', () => {
    const t0 = new Date('2026-01-01T00:00:00Z');
    const t1 = new Date('2026-01-01T01:00:00Z');
    const sessions = [
      fakeSession({ id: 'oldest-locked', kind: 'task', taskId: 'a', lastActiveAt: t0, lockedByJobId: 'job-1' }),
      fakeSession({ id: 'newer-unlocked', kind: 'task', taskId: 'b', lastActiveAt: t1 }),
    ];
    expect(selectOldestTaskSessionId(sessions)).toBe('newer-unlocked');
  });

  it('returns null when all task sessions are locked', () => {
    const sessions = [
      fakeSession({ id: 'a', kind: 'task', taskId: 't1', lastActiveAt: new Date(0), lockedByJobId: 'j1' }),
      fakeSession({ id: 'b', kind: 'task', taskId: 't2', lastActiveAt: new Date(1000), lockedByJobId: 'j2' }),
    ];
    expect(selectOldestTaskSessionId(sessions)).toBeNull();
  });
});

describe('findIdleTaskSessions', () => {
  const now = new Date('2026-01-01T01:00:00Z');

  it('returns sessions older than ttl', () => {
    const sessions = [
      fakeSession({ id: 'old', kind: 'task', taskId: 'a', lastActiveAt: new Date('2026-01-01T00:50:00Z') }), // 10 min ago
      fakeSession({ id: 'fresh', kind: 'task', taskId: 'b', lastActiveAt: new Date('2026-01-01T00:59:00Z') }), // 1 min ago
    ];
    expect(findIdleTaskSessions(sessions, 300, now)).toEqual(['old']); // ttl=5min
  });

  it('excludes pool sessions even when idle', () => {
    const sessions = [
      fakeSession({ id: CAPTCHA_POOL_SESSION_ID, kind: 'pool', lastActiveAt: new Date('2026-01-01T00:00:00Z') }),
    ];
    expect(findIdleTaskSessions(sessions, 300, now)).toEqual([]);
  });

  it('excludes locked task sessions even when idle', () => {
    const sessions = [
      fakeSession({
        id: 'locked-old', kind: 'task', taskId: 'a',
        lastActiveAt: new Date('2026-01-01T00:00:00Z'),
        lockedByJobId: 'job-1',
      }),
    ];
    expect(findIdleTaskSessions(sessions, 300, now)).toEqual([]);
  });

  it('returns empty when all sessions are fresh', () => {
    const sessions = [
      fakeSession({ id: 'a', kind: 'task', taskId: 'a', lastActiveAt: new Date('2026-01-01T00:59:00Z') }),
      fakeSession({ id: 'b', kind: 'task', taskId: 'b', lastActiveAt: new Date('2026-01-01T00:58:30Z') }),
    ];
    expect(findIdleTaskSessions(sessions, 300, now)).toEqual([]);
  });
});

describe('storageState injection', () => {
  it('createLoginSession returns a fresh session with kind=login and bound profileId', async () => {
    if (!SessionManager.isAvailable()) return; // skip if Xvfb / x11vnc / websockify not installed
    const sm = new SessionManager({ vncBasePort: 5900, sessionDataDir: '/tmp/bs-test', maxSessions: 5 });
    try {
      const s = await sm.createLoginSession({ ownerId: 'u1', profileId: 7 });
      expect(s.kind).toBe('login');
      expect(s.profileId).toBe(7);
    } finally {
      await sm.destroyAll();
    }
  });
});
