/**
 * versionRetention.ts 단위 테스트
 *
 * 테스트 대상: getVersionsToDelete
 */

import { describe, it, expect } from 'vitest';
import { getVersionsToDelete } from '../versionRetention';
import { VersionInfo, VersionRetentionConfig } from '../types/version';

const DAY_MS = 24 * 60 * 60 * 1000;

function makeVersions(count: number, now: Date, startDaysAgo: number = 0): VersionInfo[] {
  return Array.from({ length: count }, (_, i) => ({
    id: `v${i + 1}`,
    version: i + 1,
    createdAt: new Date(now.getTime() - (startDaysAgo + i) * DAY_MS),
  }));
}

describe('getVersionsToDelete', () => {
  const now = new Date('2026-04-11T00:00:00Z');
  const config: VersionRetentionConfig = { maxVersions: 50, maxAgeDays: 365 };

  it('60개 버전, maxVersions=50 → 오래된 10개 삭제 대상 (모두 maxAgeDays 이내)', () => {
    // 60개, 가장 오래된 것이 59일 전 (365일 이내)
    const versions = makeVersions(60, now);
    const toDelete = getVersionsToDelete(versions, config, now);
    // 최신 50개 보존, 나머지 10개가 maxAgeDays(365일) 이내이므로 age 조건 충족 → 보존
    // 두 조건 모두 해당하지 않는 것만 삭제
    // 59일이면 age 조건 충족 → 삭제 0개
    expect(toDelete).toHaveLength(0);
  });

  it('60개 버전, maxVersions=50, 마지막 10개가 maxAgeDays 초과 → 10개 삭제', () => {
    // 최신 50개는 1일~50일 전, 오래된 10개는 400일~409일 전 (365일 초과)
    const recent = makeVersions(50, now, 1);
    const old = Array.from({ length: 10 }, (_, i) => ({
      id: `old${i + 1}`,
      version: 100 + i,
      createdAt: new Date(now.getTime() - (400 + i) * DAY_MS),
    }));
    const versions = [...recent, ...old];
    const toDelete = getVersionsToDelete(versions, config, now);
    expect(toDelete).toHaveLength(10);
    expect(toDelete).toEqual(expect.arrayContaining(old.map((v) => v.id)));
  });

  it('모든 버전이 maxAgeDays 이내 → maxVersions 초과분이라도 age 조건으로 보존 → 삭제 0', () => {
    // 60개 모두 10일~69일 전 (365일 이내)
    const versions = makeVersions(60, now, 10);
    const toDelete = getVersionsToDelete(versions, config, now);
    expect(toDelete).toHaveLength(0);
  });

  it('maxVersions 이내 + 일부 maxAgeDays 초과 → 초과분 삭제', () => {
    // 30개, 최신 20개는 1~20일, 오래된 10개는 400~409일 전
    const smallConfig: VersionRetentionConfig = { maxVersions: 30, maxAgeDays: 365 };
    const recent = makeVersions(20, now, 1);
    const old = Array.from({ length: 10 }, (_, i) => ({
      id: `old${i + 1}`,
      version: 50 + i,
      createdAt: new Date(now.getTime() - (400 + i) * DAY_MS),
    }));
    const versions = [...recent, ...old];
    // maxVersions=30이므로 모두 count 조건 충족 → age 조건 초과인 10개도 count로 보존
    const toDelete = getVersionsToDelete(versions, smallConfig, now);
    expect(toDelete).toHaveLength(0);
  });

  it('빈 배열 → 빈 배열', () => {
    expect(getVersionsToDelete([], config, now)).toHaveLength(0);
  });

  it('정확히 maxVersions(50개) → 삭제 없음', () => {
    const versions = makeVersions(50, now);
    const toDelete = getVersionsToDelete(versions, config, now);
    expect(toDelete).toHaveLength(0);
  });

  it('maxVersions=50이고 51개 중 51번째(가장 오래됨)가 maxAgeDays 이내 → 보존 (age 조건)', () => {
    // 51개, 모두 1일~51일 전 (365일 이내)
    const versions = makeVersions(51, now, 1);
    const toDelete = getVersionsToDelete(versions, config, now);
    // 51번째(51일 전)는 count 조건 탈락이지만 age 조건 충족(365일 이내) → 보존
    expect(toDelete).toHaveLength(0);
  });

  it('삭제 대상 버전의 id가 반환됨 (id 정확성)', () => {
    const smallConfig: VersionRetentionConfig = { maxVersions: 2, maxAgeDays: 1 };
    // 5개, 최신 2개는 오늘, 나머지 3개는 400일 전
    const recent: VersionInfo[] = [
      { id: 'r1', version: 5, createdAt: new Date(now.getTime() - 1 * 60 * 60 * 1000) },
      { id: 'r2', version: 4, createdAt: new Date(now.getTime() - 2 * 60 * 60 * 1000) },
    ];
    const old: VersionInfo[] = [
      { id: 'o1', version: 3, createdAt: new Date(now.getTime() - 400 * DAY_MS) },
      { id: 'o2', version: 2, createdAt: new Date(now.getTime() - 401 * DAY_MS) },
      { id: 'o3', version: 1, createdAt: new Date(now.getTime() - 402 * DAY_MS) },
    ];
    const versions = [...recent, ...old];
    const toDelete = getVersionsToDelete(versions, smallConfig, now);
    expect(toDelete).toHaveLength(3);
    expect(toDelete).toEqual(expect.arrayContaining(['o1', 'o2', 'o3']));
  });
});
