import Storage from 'expo-sqlite/kv-store'

import {emptySlot, type Slot} from '@/lib/async-slot'
import {
  createKeyedSnapshot,
  type KeyedSnapshot,
} from '@/lib/keyed-snapshot'
import {
  emptyPaged,
  parsePagedSnapshot,
  toSnapshot,
  type PagedSlice,
  type PagedSnapshot,
} from '@/lib/paged-resource'
import {
  arrayOf,
  createSnapshot,
  parseEntries,
  type Parse,
  type Snapshot,
  type SnapshotStorage,
} from '@/lib/snapshot-cache'

// the app's snapshots: sqlite-backed via expo-sqlite/kv-store. sync reads so
// stores seed at module eval, before anything renders — no async hydration
// gate, no flash of empty state. sync writes keep same-key ordering trivial;
// values are small json envelopes
const storage: SnapshotStorage = {
  getItem: (key) => Storage.getItemSync(key),
  setItem: (key, value) => {
    Storage.setItemSync(key, value)
  },
  removeItem: (key) => {
    Storage.removeItemSync(key)
  },
}

export const snapshot = <T>(
  key: string,
  version: number,
  parse: Parse<T>,
): Snapshot<T> =>
  createSnapshot(storage, {key: `cache:${key}`, version, parse})

export const listSnapshot = <T>(
  key: string,
  version: number,
  parseItem: Parse<T>,
): Snapshot<T[]> => snapshot(key, version, arrayOf(parseItem))

export const pagedSnapshot = <T>(
  key: string,
  version: number,
  parseItem: Parse<T>,
): Snapshot<PagedSnapshot<T>> =>
  snapshot(key, version, parsePagedSnapshot(parseItem))

// keyed caches keep only the most-recently-used ids so browsing details can't
// grow the disk without bound (a detail cache is unbounded in memory)
export const KEYED_MAX_IDS = 30

// a per-id detail cache (KeyedDetail.byId): persists each id's data only,
// revived into a settled slot
export const keyedDetailSnapshot = <T>(
  key: string,
  version: number,
  parseItem: Parse<T>,
  max: number = KEYED_MAX_IDS,
): KeyedSnapshot<Slot<T | undefined>> =>
  createKeyedSnapshot<Slot<T | undefined>, T>(
    snapshot(key, version, parseEntries(parseItem)),
    {
      max,
      project: (slot) => slot.data,
      revive: (data) => emptySlot<T | undefined>(data),
    },
  )

// a per-id paged cache (KeyedPaged.byId): persists each id's items/cursor/
// reachedEnd (item-capped like pagedSnapshot), revived into a settled slice
export const keyedPagedSnapshot = <T>(
  key: string,
  version: number,
  parseItem: Parse<T>,
  max: number = KEYED_MAX_IDS,
): KeyedSnapshot<PagedSlice<T>> =>
  createKeyedSnapshot<PagedSlice<T>, PagedSnapshot<T>>(
    snapshot(
      key,
      version,
      parseEntries(parsePagedSnapshot(parseItem)),
    ),
    {
      max,
      project: toSnapshot,
      revive: (snap) => ({...emptyPaged<T>(), ...snap}),
    },
  )
