/// <reference no-default-lib="true />
/// <reference lib="esnext" />
/// <reference lib="dom" />
import { openDB, DBSchema } from "../idb/mod.ts";
import { getLinks } from "../scrapbox-userscript-std/rest.ts";
import { sumOf } from "../deno_std%2Fcollections/mod.ts";

interface LinkSchema extends DBSchema {
  links: {
    key: [string, string];
    value: {
      path: [string, string];
      id: string;
      updated: number;
      image?: string;
      links: string[];
    };
  };
}

const db = await openDB<LinkSchema>("multi-key-test", 1, {
  upgrade: (db) => {
    db.createObjectStore("links", { keyPath: "path" });
  }
});

/*
const laps: number[] = [];
for (const project of ["takker", "villagepump"]) {
  let followingId: string | undefined;
  const query = IDBKeyRange.bound([project, ""], [project, []]);
  while (true) {
    const result = await getLinks(project, { followingId });
    if (!result.ok) break;
    const pages = result.value.pages;
    
    const now = Date.now();
    const tx = db.transaction("links", "readwrite");
    await Promise.all(
      result.value.pages.map(
        ({ title, ...page }) => tx.store.put({
          path: [project, title],
          ...page,
        })
      )
    );
    await tx.done;
    laps.push(Date.now() - now);
    if (!result.value.followingId) {
      followingId = undefined;
      break;
    }
    followingId = result.value.followingId;
  }
  await tx.done;
}
console.debug(`Opened ${laps.length} transactions in ${sumOf(laps, (v) => v)}ms, ave: ${(sumOf(laps, (v) => v) / laps.length).toFixed(1)}ms, max: ${Math.max(...laps)}ms, min: ${Math.min(...laps)}ms.`);
*/

const laps: number[] = [];
for (const project of ["takker", "villagepump"]) {
  let followingId: string | undefined;
  const updatedLinks = new Set<string>();
  const query = IDBKeyRange.bound([project, ""], [project, []]);
  const LinksToDelete = new Set(
    (await db.getAllKeys("links", query)).map(([,title]) => title)
  );
  while (true) {
    const result = await getLinks(project, { followingId });
    if (!result.ok) break;
    const pages = result.value.pages;
    
    const now = Date.now();
    const tx = db.transaction("links", "readwrite");
    await Promise.all(
      result.value.pages.map(
        async ({ title, ...page }) => {
          const prev = await tx.store.get([project, title]);
          if (prev) LinksToDelete.delete(title);
          if (prev?.updated >= page.updated) return;
          updatedLinks.add(title);
          await tx.store.put({
            path: [project, title],
            ...page,
          });
        }
      )
    );
    await tx.done;
    laps.push(Date.now() - now);
    if (!result.value.followingId) {
      followingId = undefined;
      break;
    }
    followingId = result.value.followingId;
  }
  const tx = db.transaction("links", "readwrite");
  await Promise.all(
    [...LinksToDelete].map(
      (title) => tx.store.delete([project, title])
    )
  );
  await tx.done;
  console.debug(`Update ${updatedLinks.size} links and Delete ${LinksToDelete.size} links in "${project}"`);
}
console.debug(`Opened ${laps.length} transactions in ${sumOf(laps, (v) => v)}ms, ave: ${(sumOf(laps, (v) => v) / laps.length).toFixed(1)}ms, max: ${Math.max(...laps)}ms, min: ${Math.min(...laps)}ms.`);
/*
const laps: number[] = [];
for (const project of ["takker", "villagepump"]) {
  let followingId: string | undefined;
  while (true) {
    const result = await getLinks(project, { followingId });
    if (!result.ok) break;
    const pages = result.value.pages;
    
    const now = Date.now();
    const tx = db.transaction("links", "readwrite");
    await Promise.all(
      result.value.pages.map(
        ({ title, ...page }) => tx.store.put({
          path: [project, title],
          ...page,
        })
      )
    );
    await tx.done;
    laps.push(Date.now() - now);
    if (!result.value.followingId) {
      followingId = undefined;
      break;
    }
    followingId = result.value.followingId;
  }
}
console.debug(`Opened ${laps.length} transactions in ${sumOf(laps, (v) => v)}ms, ave: ${(sumOf(laps, (v) => v) / laps.length).toFixed(1)}ms, max: ${Math.max(...laps)}ms, min: ${Math.min(...laps)}ms.`);
*/