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

import {
  arrayOf,
  createSnapshot,
  fromSchema,
  type Parse,
  type SnapshotStorage,
} from '@/lib/snapshot-cache'

const memoryStorage = (): SnapshotStorage & {
  keys: () => string[]
  writes: () => number
} => {
  const map = new Map<string, string>()
  let writes = 0
  return {
    getItem: (key) => map.get(key) ?? null,
    setItem: (key, value) => {
      writes++
      map.set(key, value)
    },
    removeItem: (key) => {
      map.delete(key)
    },
    keys: () => [...map.keys()],
    writes: () => writes,
  }
}

const parseStrings: Parse<string[]> = arrayOf((raw) =>
  typeof raw === 'string' ? raw : undefined,
)

const stringsSnapshot = (storage: SnapshotStorage, version = 1) =>
  createSnapshot(storage, {
    key: 'cache:test',
    version,
    parse: parseStrings,
  })

describe('createSnapshot', () => {
  test('round-trips a written value', () => {
    const snapshot = stringsSnapshot(memoryStorage())
    snapshot.write(['a', 'b'])
    expect(snapshot.read()).toEqual(['a', 'b'])
  })

  test('reads undefined when nothing was written', () => {
    expect(stringsSnapshot(memoryStorage()).read()).toBeUndefined()
  })

  test('drops corrupt json', () => {
    const storage = memoryStorage()
    storage.setItem('cache:test', 'not json {')
    expect(stringsSnapshot(storage).read()).toBeUndefined()
    expect(storage.keys()).toEqual([])
  })

  test('drops a snapshot written under another version', () => {
    const storage = memoryStorage()
    stringsSnapshot(storage, 1).write(['a'])
    expect(stringsSnapshot(storage, 2).read()).toBeUndefined()
    expect(storage.keys()).toEqual([])
  })

  test('drops a snapshot the parser rejects', () => {
    const storage = memoryStorage()
    storage.setItem(
      'cache:test',
      JSON.stringify({v: 1, data: [1, 2]}),
    )
    expect(stringsSnapshot(storage).read()).toBeUndefined()
    expect(storage.keys()).toEqual([])
  })

  test('clear removes the entry', () => {
    const storage = memoryStorage()
    const snapshot = stringsSnapshot(storage)
    snapshot.write(['a'])
    snapshot.clear()
    expect(snapshot.read()).toBeUndefined()
    expect(storage.keys()).toEqual([])
  })

  test('skips rewriting an identical value', () => {
    const storage = memoryStorage()
    const snapshot = stringsSnapshot(storage)
    snapshot.write(['a'])
    snapshot.write(['a'])
    expect(storage.writes()).toBe(1)
    snapshot.write(['b'])
    expect(storage.writes()).toBe(2)
  })

  test('writes again after a clear even if the value matches', () => {
    const storage = memoryStorage()
    const snapshot = stringsSnapshot(storage)
    snapshot.write(['a'])
    snapshot.clear()
    snapshot.write(['a'])
    expect(snapshot.read()).toEqual(['a'])
    expect(storage.writes()).toBe(2)
  })
})

describe('fromSchema', () => {
  const schema = {
    safeParse: (raw: unknown) =>
      typeof raw === 'number'
        ? ({success: true, data: raw} as const)
        : ({success: false} as const),
  }

  test('accepts what the schema accepts', () => {
    expect(fromSchema(schema)(3)).toBe(3)
  })

  test('rejects what the schema rejects', () => {
    expect(fromSchema(schema)('3')).toBeUndefined()
  })
})

describe('arrayOf', () => {
  test('rejects non-arrays', () => {
    expect(parseStrings('a')).toBeUndefined()
  })

  test('rejects an array with one bad item', () => {
    expect(parseStrings(['a', 1])).toBeUndefined()
  })

  test('accepts an empty array', () => {
    expect(parseStrings([])).toEqual([])
  })
})
