/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />

import {
  getPage,
  getProject,
  readLinks,
  type FetchError,
  type LinksError,
  type PageError,
} from "jsr:@cosense/std@0.29/rest";
import type { Page, UnixTime } from "jsr:@cosense/types@0.10/rest";
import { useStatusBar } from "jsr:@cosense/std@0.29/browser/dom";
import { pooledMap } from "jsr:@std/async@1/pool";
import { createOk, isErr, type Result, unwrapOk, unwrapErr } from "npm:option-t@51/plain_result";
import { getUnixTime } from "npm:date-fns@4/getUnixTime";

export type ExportPage = Pick<Page, "id" | "title" | "created" | "updated" | "lines">;
export interface ExportData {
  name: string;
  displayName: string;
  exported: UnixTime;
  pages: ExportPage[];
}

export const createExport = async (
  project: string,
  threshold = 100,
): Promise<ExportData> => {
  const { render, dispose } = useStatusBar();
  render(
    { type: "spinner"},
    { type: "text", text: `Loading pages from "${project}"` },
  );
  
  try {
    const result = await getProject(project);
    if (isErr(result)) throw new Error(unwrapErr(result).name, { cause: unwrapErr(result) });
    const { name, displayName } = unwrapOk(result);
    
    const pages: ExportPage[] = [];
    const { render, dispose } = useStatusBar();
    
    try {
      for await (
        const result of pooledMap(
          threshold,
          readLinks(project),
          async (result): Promise<Result<string, FetchError | LinksError | PageError>> => {
            if (isErr(result)) return result;
            const page = unwrapOk(result);
            const result2 = await getPage(project, page.title);
            if (isErr(result2)) return result2;
            const { id, title, created, updated, lines } = unwrapOk(result2);
            pages.push({ id, title, created, updated, lines });
            return createOk(`[${pages.length}] ${title}`);
          },
        )
      ) {
        if (isErr(result)) throw new Error(unwrapErr(result).name, { cause: unwrapErr(result) });
        let animationId: number | undefined;
        if (animationId !== undefined) cancelAnimationFrame(animationId);
        animationId = requestAnimationFrame(
          () => render({ type: "text", text: unwrapOk(result) })
        );
      }
    } finally {
      dispose();
    }
    const exported = getUnixTime(new Date());
    render(
      { type: "check-circle" },
      { type: "text", text: `Exported ${pages.length} pages from /${name}`}
    );
    return { name, displayName, exported, pages };
  } catch(e) {
    render(
      { type: "exclamation-triangle" },
      {
        type: "text",
        text: e instanceof Error
          ? `${e.name} ${e.message}`
          : "Unexpected error! (see developer console)",
      },
    );
    throw e;
  } finally {
    setTimeout(() => dispose(), 1000);
  }
};