import { openDB, IndexNames, StoreNames, StoreValue } from "idb";
import getDb, { NovelDb } from "./getDb";

export async function putItem<N extends StoreNames<NovelDb>>(
  storeNames: N,
  item: StoreValue<NovelDb, N>
) {
  const db = await getDb();

  const tx = db.transaction(storeNames, "readwrite");

  const store = tx.store;

  store.put(item);

  return tx.done;
}

export async function delItem<N extends StoreNames<NovelDb>>(
  storeNames: N,
  key: NovelDb[N]["key"]
) {
  const db = await getDb();

  const tx = db.transaction(storeNames, "readwrite");

  const store = tx.store;

  store.delete(key);

  return tx.done;
}

export async function getItem<N extends StoreNames<NovelDb>>(
  storeNames: N,
  key: NovelDb[N]["key"]
) {
  const db = await getDb();

  const tx = db.transaction(storeNames, "readonly");

  const store = tx.store;

  return store.get(key);
}

export const putHistoryItem = (item: HistoryItem) => putItem("history", item);

export const delHistoryItem = (key: string) => delItem("history", key);

export const getHistoryItem = (key: string) => getItem("history", key);

export async function getList<
  N extends StoreNames<NovelDb>,
  R extends [any, any, boolean?, boolean?]
>(
  storeName: N,
  index?: IndexNames<NovelDb, N>,
  page = 0,
  pageSize = 20,
  direction: IDBCursorDirection = "next",
  rage?: R
): Promise<PageList<NovelDb[N]["value"]>> {
  const db = await getDb();

  const tx = db.transaction(storeName, "readonly");

  const store = tx.store;

  const keyRangeValue = rage ? IDBKeyRange.bound.apply(void 0, rage) : null;
  let [cursor, total] = await Promise.all([
    index
      ? store.index(index).openCursor(keyRangeValue, direction)
      : store.openCursor(),
    store.count(),
  ]);
  type ValueItem = StoreValue<NovelDb, N>;
  const list: ValueItem[] = [];

  const res: PageList<ValueItem> = {
    list,
    total,
    page,
    pageSize,
  };

  if (!cursor) return res;

  if (page > 0) {
    cursor = await cursor.advance(page * pageSize);
  }

  while (cursor) {
    list.push(cursor.value);
    if (pageSize === list.length) {
      break;
    }
    cursor = await cursor.continue();
  }

  return res;
}

function genGetList<
  N extends StoreNames<NovelDb>,
  R extends [any, any, boolean?, boolean?]
>(
  storeName: N,
  index?: IndexNames<NovelDb, N>,
  direction: IDBCursorDirection = "next",
  defaultRange?: R
) {
  return async function (
    page = 0,
    pageSize = 20,
    rage = defaultRange
  ): Promise<PageList<NovelDb[N]["value"]>> {
    return getList(storeName, index, page, pageSize, direction, rage);
  };
}

export const getHistoryList = genGetList(
  "history",
  "historyIsReading",
  "prev",
  [
    [1, 0],
    [1, new Date()],
  ]
);

export const getFavoriteList = genGetList(
  "history",
  "historyIsFavorite",
  "prev",
  [1, 1]
);
