import { beforeEach, describe, expect, it, vi } from "vitest"
import { PrecalculatedLruCache } from "./LruCache"

describe("PrecalculatedLruCache", () => {
  const N = 2

  let calculateValue: any
  let cache: PrecalculatedLruCache<number>

  beforeEach(() => {
    calculateValue = vi.fn(async (key: string) => {
      return parseInt(key, 10) * 2
    })

    cache = new PrecalculatedLruCache<number>(calculateValue, N)
  })

  it("should calculate and cache values", async () => {
    await cache.initKey("1")
    expect(calculateValue).toHaveBeenCalledWith("1")
    expect(cache.get("1")).toBe(2)
  })

  it("should not recalculate cached values", async () => {
    await cache.initKey("1")
    calculateValue.mockClear()

    await cache.initKey("1")
    expect(calculateValue).not.toHaveBeenCalled()
    expect(cache.get("1")).toBe(2)
  })

  it("should evict least recently used items when capacity is exceeded", async () => {
    await cache.initKey("1")
    await cache.initKey("2")
    await cache.initKey("3") // Exceeds capacity, should evict "1"

    expect(cache.get("1")).toBeUndefined()
    expect(cache.get("2")).toBe(4)
    expect(cache.get("3")).toBe(6)
  })

  it("should update LRU order when keys are accessed", async () => {
    await cache.initKey("1")
    await cache.initKey("2")

    // Access "1" to make it most recently used
    await cache.initKey("1")
    await cache.initKey("3") // Exceeds capacity, should evict "2"

    expect(cache.get("1")).toBe(2)
    expect(cache.get("2")).toBeUndefined()
    expect(cache.get("3")).toBe(6)
  })

  it("should handle get for keys not initialized", () => {
    expect(cache.get("not-initialized")).toBeUndefined()
  })

  it("should propagate errors from calculateValue", async () => {
    calculateValue.mockRejectedValue(new Error("Calculation failed"))
    await expect(cache.initKey("error")).rejects.toThrow("Calculation failed")
    expect(cache.get("error")).toBeUndefined()
  })
})
