// Tests for session retention — enforceMaxSessions logic.
// Uses bun:test. Tests the retention count + age pruning from agent-store.svelte.ts.

import { describe, it, expect, beforeEach } from 'bun:test';

// ── Replicated types and retention logic ────────────────────────────────────

interface SessionEntry {
  sessionId: string;
  projectId: string;
  status: 'idle' | 'running' | 'done' | 'error';
  lastMessageTs: number;
}

interface RetentionConfig {
  count: number;
  days: number;
}

function setRetentionConfig(count: number, days: number): RetentionConfig {
  return {
    count: Math.max(1, Math.min(50, count)),
    days: Math.max(1, Math.min(365, days)),
  };
}

function enforceMaxSessions(
  sessions: SessionEntry[],
  projectId: string,
  config: RetentionConfig,
): string[] {
  const now = Date.now();
  const maxAgeMs = config.days * 24 * 60 * 60 * 1000;

  // Filter to this project's non-running sessions, sorted newest first
  const projectSessions = sessions
    .filter(s => s.projectId === projectId && s.status !== 'running')
    .sort((a, b) => b.lastMessageTs - a.lastMessageTs);

  const toPurge: string[] = [];

  // Prune by count
  if (projectSessions.length > config.count) {
    const excess = projectSessions.slice(config.count);
    for (const s of excess) toPurge.push(s.sessionId);
  }

  // Prune by age
  for (const s of projectSessions) {
    if (s.lastMessageTs > 0 && (now - s.lastMessageTs) > maxAgeMs) {
      if (!toPurge.includes(s.sessionId)) toPurge.push(s.sessionId);
    }
  }

  return toPurge;
}

// ── Tests ───────────────────────────────────────────────────────────────────

describe('setRetentionConfig', () => {
  it('clamps count to [1, 50]', () => {
    expect(setRetentionConfig(0, 30).count).toBe(1);
    expect(setRetentionConfig(100, 30).count).toBe(50);
    expect(setRetentionConfig(5, 30).count).toBe(5);
  });

  it('clamps days to [1, 365]', () => {
    expect(setRetentionConfig(5, 0).days).toBe(1);
    expect(setRetentionConfig(5, 500).days).toBe(365);
    expect(setRetentionConfig(5, 30).days).toBe(30);
  });
});

describe('enforceMaxSessions — count-based pruning', () => {
  it('keeps only N most recent sessions', () => {
    const now = Date.now();
    const sessions: SessionEntry[] = [
      { sessionId: 's1', projectId: 'p1', status: 'done', lastMessageTs: now - 50000 },
      { sessionId: 's2', projectId: 'p1', status: 'done', lastMessageTs: now - 40000 },
      { sessionId: 's3', projectId: 'p1', status: 'done', lastMessageTs: now - 30000 },
      { sessionId: 's4', projectId: 'p1', status: 'done', lastMessageTs: now - 20000 },
      { sessionId: 's5', projectId: 'p1', status: 'done', lastMessageTs: now - 10000 },
    ];
    const config: RetentionConfig = { count: 3, days: 365 };
    const toPurge = enforceMaxSessions(sessions, 'p1', config);
    expect(toPurge).toHaveLength(2);
    // s1 and s2 are oldest
    expect(toPurge).toContain('s1');
    expect(toPurge).toContain('s2');
  });

  it('does not purge when under limit', () => {
    const now = Date.now();
    const sessions: SessionEntry[] = [
      { sessionId: 's1', projectId: 'p1', status: 'done', lastMessageTs: now },
    ];
    const config: RetentionConfig = { count: 5, days: 365 };
    expect(enforceMaxSessions(sessions, 'p1', config)).toHaveLength(0);
  });
});

describe('enforceMaxSessions — age-based pruning', () => {
  it('prunes sessions older than retention days', () => {
    const now = Date.now();
    const oldTs = now - (31 * 24 * 60 * 60 * 1000); // 31 days ago
    const sessions: SessionEntry[] = [
      { sessionId: 's-old', projectId: 'p1', status: 'done', lastMessageTs: oldTs },
      { sessionId: 's-new', projectId: 'p1', status: 'done', lastMessageTs: now },
    ];
    const config: RetentionConfig = { count: 10, days: 30 };
    const toPurge = enforceMaxSessions(sessions, 'p1', config);
    expect(toPurge).toEqual(['s-old']);
  });

  it('keeps sessions within retention window', () => {
    const now = Date.now();
    const recentTs = now - (5 * 24 * 60 * 60 * 1000); // 5 days ago
    const sessions: SessionEntry[] = [
      { sessionId: 's1', projectId: 'p1', status: 'done', lastMessageTs: recentTs },
    ];
    const config: RetentionConfig = { count: 10, days: 30 };
    expect(enforceMaxSessions(sessions, 'p1', config)).toHaveLength(0);
  });
});

describe('enforceMaxSessions — running sessions protected', () => {
  it('never purges running sessions', () => {
    const now = Date.now();
    const sessions: SessionEntry[] = [
      { sessionId: 's-running', projectId: 'p1', status: 'running', lastMessageTs: now - 999999999 },
      { sessionId: 's-done', projectId: 'p1', status: 'done', lastMessageTs: now },
    ];
    const config: RetentionConfig = { count: 1, days: 1 };
    const toPurge = enforceMaxSessions(sessions, 'p1', config);
    expect(toPurge).not.toContain('s-running');
  });
});

describe('enforceMaxSessions — project isolation', () => {
  it('only prunes sessions for the specified project', () => {
    const now = Date.now();
    const sessions: SessionEntry[] = [
      { sessionId: 'p1-s1', projectId: 'p1', status: 'done', lastMessageTs: now - 1000 },
      { sessionId: 'p2-s1', projectId: 'p2', status: 'done', lastMessageTs: now - 1000 },
    ];
    const config: RetentionConfig = { count: 0, days: 365 }; // count 0 → clamped to 1
    const actualConfig = setRetentionConfig(0, 365);
    const toPurge = enforceMaxSessions(sessions, 'p1', actualConfig);
    expect(toPurge).not.toContain('p2-s1');
  });
});
