import { describe, it, expect } from 'vitest'
import { selectPruneCandidates, selectUpgradeCandidates } from '../src/shared/retention'
import { qualityRank, type MediaItem } from '@shared/ipc'

const DAY = 86_400_000
const NOW = 1_700_000_000_000

function item(over: Partial<MediaItem>): MediaItem {
  return {
    id: over.id ?? 'x',
    sourceId: 's',
    videoId: over.id ?? 'x',
    title: over.title ?? 'V',
    url: `https://y/${over.id ?? 'x'}`,
    playlistTitle: 'Uploads',
    playlistIndex: 1,
    downloaded: true,
    downloadedAt: NOW,
    filePath: `C:/out/${over.id ?? 'x'}.mp4`,
    ...over
  }
}

describe('selectPruneCandidates (retention)', () => {
  it('no policy → nothing pruned', () => {
    const items = [item({ id: 'a', downloadedAt: NOW - 100 * DAY })]
    expect(selectPruneCandidates(items, {}, NOW)).toEqual([])
  })

  it('maxAgeDays prunes items older than the cutoff', () => {
    const items = [
      item({ id: 'fresh', downloadedAt: NOW - 5 * DAY }),
      item({ id: 'old', downloadedAt: NOW - 40 * DAY })
    ]
    const pruned = selectPruneCandidates(items, { maxAgeDays: 30 }, NOW).map((i) => i.id)
    expect(pruned).toEqual(['old'])
  })

  it('maxCount keeps the N most-recent, prunes the rest', () => {
    const items = [
      item({ id: 'a', downloadedAt: NOW - 1 * DAY }),
      item({ id: 'b', downloadedAt: NOW - 2 * DAY }),
      item({ id: 'c', downloadedAt: NOW - 3 * DAY }),
      item({ id: 'd', downloadedAt: NOW - 4 * DAY })
    ]
    const pruned = selectPruneCandidates(items, { maxCount: 2 }, NOW)
      .map((i) => i.id)
      .sort()
    expect(pruned).toEqual(['c', 'd'])
  })

  it('age + count combine — an item over EITHER limit is pruned', () => {
    const items = [
      item({ id: 'a', downloadedAt: NOW - 1 * DAY }), // within both
      item({ id: 'b', downloadedAt: NOW - 2 * DAY }), // within age, over count(1)
      item({ id: 'c', downloadedAt: NOW - 40 * DAY }) // over age
    ]
    const pruned = selectPruneCandidates(items, { maxAgeDays: 30, maxCount: 1 }, NOW)
      .map((i) => i.id)
      .sort()
    expect(pruned).toEqual(['b', 'c'])
  })

  it('never prunes un-downloaded items or ones without a filePath', () => {
    const items = [
      item({ id: 'pending', downloaded: false, filePath: undefined, downloadedAt: undefined }),
      item({ id: 'nofile', filePath: undefined }),
      item({ id: 'real', downloadedAt: NOW - 99 * DAY })
    ]
    expect(selectPruneCandidates(items, { maxAgeDays: 30 }, NOW).map((i) => i.id)).toEqual(['real'])
  })
})

describe('selectUpgradeCandidates (retention)', () => {
  it('selects items downloaded below the target quality', () => {
    const items = [
      item({ id: 'lo', downloadedQuality: '480p' }),
      item({ id: 'ok', downloadedQuality: '1080p' }),
      item({ id: 'hi', downloadedQuality: 'Best available' })
    ]
    const up = selectUpgradeCandidates(items, '1080p', qualityRank).map((i) => i.id)
    expect(up).toEqual(['lo'])
  })

  it('skips items with no recorded downloadedQuality (unknown, not re-churned)', () => {
    const items = [
      item({ id: 'unknown', downloadedQuality: undefined }),
      item({ id: 'lo', downloadedQuality: '360p' })
    ]
    expect(selectUpgradeCandidates(items, '720p', qualityRank).map((i) => i.id)).toEqual(['lo'])
  })

  it('skips un-downloaded items', () => {
    const items = [item({ id: 'pending', downloaded: false, downloadedQuality: '360p' })]
    expect(selectUpgradeCandidates(items, 'Best available', qualityRank)).toEqual([])
  })

  it('a "Best" target upgrades anything not already Best', () => {
    const items = [
      item({ id: 'a', downloadedQuality: '1080p' }),
      item({ id: 'b', downloadedQuality: 'Best available' })
    ]
    expect(selectUpgradeCandidates(items, 'Best', qualityRank).map((i) => i.id)).toEqual(['a'])
  })
})

describe('qualityRank', () => {
  it('ranks Best above all numeric qualities', () => {
    expect(qualityRank('Best available')).toBeGreaterThan(qualityRank('1080p'))
    expect(qualityRank('Best')).toBeGreaterThan(qualityRank('320 kbps'))
  })

  it('orders numeric qualities bigger-is-better', () => {
    expect(qualityRank('1080p')).toBeGreaterThan(qualityRank('720p'))
    expect(qualityRank('320 kbps')).toBeGreaterThan(qualityRank('128 kbps'))
  })

  it('unknown labels rank lowest', () => {
    expect(qualityRank('mystery')).toBe(0)
    expect(qualityRank('mystery')).toBeLessThan(qualityRank('360p'))
  })
})
