import fs from "node:fs/promises";
import type { Book } from "./library";
import type { Browser } from "./browser";
import type { Database } from "./database";
import { GooglePlayBooks } from "./platforms/google-play-books";
import { DmmBooks } from "./platforms/dmm-books";
import { DlsiteManiax } from "./platforms/dlsite-maniax";
import { FanzaDoujin } from "./platforms/fanza-doujin";

export const platforms = {
  "google-play-books": GooglePlayBooks,
  "dmm-books": DmmBooks,
  "dlsite-maniax": DlsiteManiax,
  "fanza-doujin": FanzaDoujin,
};

export type TPlatform = keyof typeof platforms;

export function site(url: string): TPlatform {
  for (const [platform, { siteUrl }] of Object.entries(platforms)) {
    if (siteUrl(new URL(url))) return platform as TPlatform;
  }

  throw new Error(`Unsupported URL: ${url}`);
}

export function createPlatform(opts: {
  platform: TPlatform;
  db: Database;
  browser: Browser;
}) {
  if (!(opts.platform in platforms)) {
    throw new Error(
      `The value must be a platform type: ${[...Object.keys(platforms)].join(
        ", ",
      )}.`,
    );
  }

  const platform = platforms[opts.platform](opts.browser);

  return {
    ...platform,

    async download(dir: string, book: Book): Promise<void> {
      await fs.mkdir(dir, { recursive: true });

      const files: Array<() => Promise<Blob>> = await platform.getFiles(book);
      const digits = String(files.length).length;

      function pad(n: string) {
        return n.padStart(digits, "0");
      }

      const supportedTypes = {
        "image/png": "png",
        "image/jpeg": "jpg",
        "application/zip": "zip",
        "application/vnd.comicbook+zip": "cbz",
      };

      for (const [n, getBlob] of Object.entries(files)) {
        const blob = await getBlob();
        const extension = supportedTypes[blob.type];
        if (!extension) {
          throw new Error(
            `It was ${blob.type}. The image must be a file of type: ${[
              ...Object.keys(supportedTypes),
            ].join(", ")}.`,
          );
        }

        const buffer = Buffer.from(await blob.arrayBuffer());
        await fs.writeFile(`${dir}/${pad(n)}.${extension}`, buffer);
      }

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

    async login() {
      const ctx = await opts.browser.newContext();
      const page = await ctx.newPage();

      for (const loginEndpoint of platform.loginEndpoints) {
        await page.goto(loginEndpoint);
        await page.waitForURL(platform.loginSuccessUrl, { timeout: 0 });
      }

      await opts.browser.saveBrowserContext(opts.platform, ctx);
    },

    async logout() {
      try {
        const ctx = await opts.browser.loadBrowserContext(opts.platform);
        const page = await ctx.newPage();

        for (const logoutEndpoint of platform.logoutEndpoints) {
          await page.goto(logoutEndpoint);
        }
      } catch (error) {
        process.stderr.write(`Warning: ${(error as Error).message}\n`);
      }

      await opts.db.run(
        `update platforms set secrets = 'null' where name = ?`,
        opts.platform,
      );
    },
  };
}