import type { Book } from "../library";
import type { Browser, ImageFile } from "../browser";

async function getImageFiles(): Promise<Array<ImageFile>> {
  const pages: NodeListOf<HTMLElement> = await new Promise(async function (
    resolve,
    reject,
  ) {
    const timeout = setTimeout(() => {
      reject(new Error("Page loading timeout."));
    }, 60_000);

    let pages: NodeListOf<HTMLElement>;

    while (true) {
      pages = document.querySelectorAll("reader-page");

      const loaded =
        pages.length > 0 &&
        [...pages].every((page) => page.classList.contains("-gb-loaded"));

      if (loaded) {
        break;
      } else {
        await new Promise((resolve) => setTimeout(resolve, 100));
      }
    }

    resolve(pages);
    clearTimeout(timeout);
  });

  const images: Array<SVGImageElement> = [...pages].map(
    (el) => el.querySelector("svg image")!,
  );

  return [...images].map((image) => ({ url: image.href.baseVal }));
}

export function GooglePlayBooks(browser: Browser) {
  return {
    async *pull(): AsyncGenerator<Book> {
      const ctx = await browser.loadBrowserContext("google-play-books");
      const page = await ctx.newPage();
      await page.goto(
        "https://play.google.com/books?type=comics&source=purchases",
      );
      await page.waitForSelector("gpb-library-card");

      for (const metadata of await page.$$("gpb-library-card .metadata")) {
        const readerUrl = await metadata.$eval("a", (a) => a.href);
        const [title, author] = (await metadata.innerText()).split("\n");

        yield {
          id: NaN,
          platform: "google-play-books",
          readerUrl,
          title,
          authors: [author],
        };

        process.stderr.write(".");
      }

      process.stderr.write(`\n`);
    },

    async getFiles(book: Book): Promise<Array<() => Promise<Blob>>> {
      const ctx = await browser.loadBrowserContext("google-play-books");
      const page = await ctx.newPage();

      await page.goto(book.readerUrl);
      await page.waitForSelector(".display");

      const frame = page.frames().at(-1);

      if (!frame) {
        throw new Error("Frame not found.");
      }

      await frame.evaluate(function scrollToTop() {
        const viewport = document.querySelector("cdk-virtual-scroll-viewport");
        viewport?.scroll({ top: 0 });
      });

      async function next(): Promise<boolean> {
        return await frame!.evaluate(function scroll() {
          const viewport = document.querySelector(
            "cdk-virtual-scroll-viewport",
          );

          if (!viewport) throw new Error("Viewport not found.");

          const hasNext =
            1 <=
            Math.abs(
              viewport.scrollHeight -
                viewport.clientHeight -
                viewport.scrollTop,
            );

          if (hasNext) {
            viewport.scrollBy({ top: viewport.clientHeight });
          }

          return hasNext;
        });
      }

      const fileMap: Map<string, () => Promise<Blob>> = new Map();

      while (await next()) {
        const imageFiles = await frame.evaluate(getImageFiles);

        for (const imageFile of imageFiles) {
          if (fileMap.has(imageFile.url)) continue;

          const blob = await browser.drawImage(frame, imageFile);

          process.stderr.write(".");

          fileMap.set(imageFile.url, async () => blob);
        }
      }

      return [...fileMap.values()];
    },
    loginEndpoints: ["https://accounts.google.com"],
    loginSuccessUrl: (url: URL) =>
      url.origin === "https://myaccount.google.com",
    logoutEndpoints: ["https://accounts.google.com/Logout"],
  };
}

GooglePlayBooks.siteUrl = (url: URL) =>
  url.origin === "https://play.google.com";