import {describe, expect, test} from 'bun:test'

import {createKeyedPagedStore} from '@/lib/keyed-paged-resource'
import {
  createKeyedSnapshot,
  type KeyedSnapshot,
} from '@/lib/keyed-snapshot'
import {
  emptyPaged,
  toSnapshot,
  type PagedSlice,
  type PagedSnapshot,
} from '@/lib/paged-resource'
import {type Snapshot} from '@/lib/snapshot-cache'
import {resetAllStores} from '@/lib/store-reset'

type Item = {id: string}
const idOf = (i: Item) => i.id
const ids = (entries?: [string, unknown][]) =>
  entries?.map(([id]) => id)

const fakeEntries = (
  initial?: [string, PagedSnapshot<Item>][],
): Snapshot<[string, PagedSnapshot<Item>][]> & {
  current: () => [string, PagedSnapshot<Item>][] | undefined
} => {
  let value = initial
  return {
    read: () => value,
    write: (next) => {
      value = next
    },
    clear: () => {
      value = undefined
    },
    current: () => value,
  }
}

// a keyed snapshot capped low, backed by the real createKeyedSnapshot so the
// factory wiring is tested against the same lru the app ships
const pagedSnap = (
  entries: Snapshot<[string, PagedSnapshot<Item>][]>,
): KeyedSnapshot<PagedSlice<Item>> =>
  createKeyedSnapshot<PagedSlice<Item>, PagedSnapshot<Item>>(
    entries,
    {
      max: 2,
      project: toSnapshot,
      revive: (snap) => ({...emptyPaged<Item>(), ...snap}),
    },
  )

// the store never actually fetches here — persistence rides the same set()
// path as a fetch commit, exercised through the optimistic helpers
const neverFetch = () => async () => ({
  items: [] as Item[],
  nextCursor: null,
})

describe('createKeyedPagedStore snapshot', () => {
  test('seeds byId from the snapshot', () => {
    const entries = fakeEntries([
      ['t1', {items: [{id: 'a'}], cursor: 'c', reachedEnd: false}],
    ])
    const store = createKeyedPagedStore<Item>(
      neverFetch,
      pagedSnap(entries),
    )
    expect(store.getItems('t1').map(idOf)).toEqual(['a'])
  })

  test('persists an optimistic prepend', () => {
    const entries = fakeEntries()
    const store = createKeyedPagedStore<Item>(
      neverFetch,
      pagedSnap(entries),
    )
    store.prependItems('t1', [{id: 'a'}], idOf)
    expect(entries.current()).toEqual([
      ['t1', {items: [{id: 'a'}], cursor: null, reachedEnd: false}],
    ])
  })

  test('caps the number of persisted ids, evicting the coldest', () => {
    const entries = fakeEntries()
    const store = createKeyedPagedStore<Item>(
      neverFetch,
      pagedSnap(entries),
    )
    store.prependItems('t1', [{id: 'a'}], idOf)
    store.prependItems('t2', [{id: 'b'}], idOf)
    store.prependItems('t3', [{id: 'c'}], idOf)
    expect(ids(entries.current())).toEqual(['t3', 't2'])
  })

  test('dropId removes the slice; later loads still work', async () => {
    const entries = fakeEntries()
    const store = createKeyedPagedStore<Item>(
      (id) => async () => ({
        items: [{id: `${id}-a`}],
        nextCursor: null,
      }),
      pagedSnap(entries),
    )
    await store.load('t1')
    expect(store.getItems('t1').map(idOf)).toEqual(['t1-a'])

    store.dropId('t1')
    expect(store.getItems('t1')).toEqual([])
    expect(ids(entries.current())).toEqual([])

    await store.load('t1')
    expect(store.getItems('t1').map(idOf)).toEqual(['t1-a'])
  })

  test('reset clears memory and disk', () => {
    const entries = fakeEntries()
    const store = createKeyedPagedStore<Item>(
      neverFetch,
      pagedSnap(entries),
    )
    store.prependItems('t1', [{id: 'a'}], idOf)
    resetAllStores()
    expect(store.getItems('t1')).toEqual([])
    expect(entries.current()).toBeUndefined()
  })
})
