import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { PDFCacheManager } from '../cache-manager'

describe('PDFCacheManager', () => {
  let cache: PDFCacheManager

  beforeEach(() => {
    cache = new PDFCacheManager()
    vi.useFakeTimers()
  })

  afterEach(() => {
    cache.destroy()
    vi.restoreAllMocks()
  })

  describe('set and get', () => {
    it('should store and retrieve a PDF buffer', () => {
      const buffer = Buffer.from('test pdf content')
      const key = 'test_key_123'

      cache.set(key, buffer)
      const retrieved = cache.get(key)

      expect(retrieved).not.toBeNull()
      expect(retrieved?.pdfBuffer).toEqual(buffer)
    })

    it('should increment hits counter on get', () => {
      const buffer = Buffer.from('test pdf content')
      const key = 'test_key_123'

      cache.set(key, buffer)
      const entry1 = cache.get(key)

      expect(entry1?.hits).toBe(1)

      const entry2 = cache.get(key)
      expect(entry2?.hits).toBe(2)
    })

    it('should return null for non-existent key', () => {
      const result = cache.get('non_existent_key')
      expect(result).toBeNull()
    })
  })

  describe('expiry and cleanup', () => {
    it('should expire entries after TTL', () => {
      const buffer = Buffer.from('test pdf content')
      const key = 'test_key_123'

      cache.set(key, buffer)
      expect(cache.get(key)).not.toBeNull()

      // Advance time past TTL (5 minutes = 5 * 60 * 1000 ms)
      vi.advanceTimersByTime(5 * 60 * 1000 + 1000)

      expect(cache.get(key)).toBeNull()
    })

    it('should remove expired entries on get', () => {
      const buffer = Buffer.from('test pdf content')
      const key = 'test_key_123'

      cache.set(key, buffer)
      vi.advanceTimersByTime(5 * 60 * 1000 + 1000)

      const stats1 = cache.getStats()
      cache.get(key) // This should trigger cleanup
      const stats2 = cache.getStats()

      expect(stats1.size).toBe(1)
      expect(stats2.size).toBe(0)
    })

    it('should cleanup expired entries in background', () => {
      const buffer = Buffer.from('test pdf content')

      cache.set('key_1', buffer)
      cache.set('key_2', buffer)
      cache.set('key_3', buffer)

      expect(cache.getStats().size).toBe(3)

      // Advance time past TTL
      vi.advanceTimersByTime(5 * 60 * 1000 + 1000)
      // Trigger cleanup interval (60 seconds)
      vi.advanceTimersByTime(60000)

      expect(cache.getStats().size).toBe(0)
    })
  })

  describe('generateKey', () => {
    it('should generate consistent keys for same inputs', () => {
      const key1 = cache.generateKey(13335, 'report', '2026-04-29')
      const key2 = cache.generateKey(13335, 'report', '2026-04-29')

      expect(key1).toBe(key2)
    })

    it('should generate different keys for different ASNs', () => {
      const key1 = cache.generateKey(13335, 'report', '2026-04-29')
      const key2 = cache.generateKey(13336, 'report', '2026-04-29')

      expect(key1).not.toBe(key2)
    })

    it('should generate different keys for different formats', () => {
      const key1 = cache.generateKey(13335, 'report', '2026-04-29')
      const key2 = cache.generateKey(13335, 'executive', '2026-04-29')

      expect(key1).not.toBe(key2)
    })

    it('should generate different keys for different dates', () => {
      const key1 = cache.generateKey(13335, 'report', '2026-04-29')
      const key2 = cache.generateKey(13335, 'report', '2026-04-30')

      expect(key1).not.toBe(key2)
    })

    it('should generate MD5 hash format', () => {
      const key = cache.generateKey(13335, 'report', '2026-04-29')
      expect(key).toMatch(/^[a-f0-9]{32}$/)
    })
  })

  describe('getStats', () => {
    it('should return correct stats for empty cache', () => {
      const stats = cache.getStats()

      expect(stats.size).toBe(0)
      expect(stats.entries).toBe(0)
      expect(stats.totalMemory).toBe(0)
    })

    it('should calculate total memory correctly', () => {
      const buffer1 = Buffer.from('a'.repeat(1000))
      const buffer2 = Buffer.from('b'.repeat(2000))

      cache.set('key_1', buffer1)
      cache.set('key_2', buffer2)

      const stats = cache.getStats()

      expect(stats.size).toBe(2)
      expect(stats.entries).toBe(2)
      expect(stats.totalMemory).toBe(3000)
    })
  })

  describe('clear', () => {
    it('should clear all cached entries', () => {
      const buffer = Buffer.from('test pdf content')

      cache.set('key_1', buffer)
      cache.set('key_2', buffer)

      expect(cache.getStats().size).toBe(2)

      cache.clear()

      expect(cache.getStats().size).toBe(0)
    })

    it('should allow setting new entries after clear', () => {
      const buffer = Buffer.from('test pdf content')

      cache.set('key_1', buffer)
      cache.clear()
      cache.set('key_2', buffer)

      expect(cache.getStats().size).toBe(1)
      expect(cache.get('key_2')).not.toBeNull()
    })
  })

  describe('hash tracking', () => {
    it('should calculate SHA256 hash for stored PDF', () => {
      const buffer = Buffer.from('test pdf content')
      const key = 'test_key_123'

      cache.set(key, buffer)
      const entry = cache.get(key)

      expect(entry?.hash).toBeDefined()
      expect(entry?.hash).toMatch(/^[a-f0-9]{64}$/)
    })

    it('should generate same hash for same buffer content', () => {
      const buffer = Buffer.from('test pdf content')
      const key1 = cache.generateKey(13335, 'report', '2026-04-29')
      const key2 = cache.generateKey(13335, 'executive', '2026-04-29')

      cache.set(key1, buffer)
      cache.set(key2, buffer)

      const entry1 = cache.get(key1)
      const entry2 = cache.get(key2)

      expect(entry1?.hash).toBe(entry2?.hash)
    })
  })

  describe('timestamp tracking', () => {
    it('should track createdAt timestamp', () => {
      const buffer = Buffer.from('test pdf content')
      const now = Date.now()

      cache.set('test_key', buffer)
      const entry = cache.get('test_key')

      expect(entry?.createdAt).toBeGreaterThanOrEqual(now)
      expect(entry?.createdAt).toBeLessThanOrEqual(Date.now())
    })

    it('should track expiresAt timestamp correctly', () => {
      const buffer = Buffer.from('test pdf content')
      const now = Date.now()
      const ttl = 5 * 60 * 1000

      cache.set('test_key', buffer)
      const entry = cache.get('test_key')

      expect(entry?.expiresAt).toBeGreaterThanOrEqual(now + ttl)
      expect(entry?.expiresAt).toBeLessThanOrEqual(now + ttl + 1000)
    })
  })
})
