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

var NFBR: any;

export async function getImageFiles(): Promise<Array<ImageFile>> {
  const params = new URLSearchParams(location.search);
  const model = new NFBR.a6G.Model({
    settings: new NFBR.Settings("NFBR.SettingData"),
    viewerFontSize: NFBR.a0X.a3K,
    viewerFontFace: NFBR.a0X.a3k,
    viewerSpreadDouble: true,
    viewerSpread: {},
  });
  const a6l = new NFBR.a6G.a6L(model);
  const a2f = new NFBR.a2F();
  const a5w = await a2f.a5W({
    contentId: params.get(NFBR.a5q.Key.CONTENT_ID),
    a6m: params.get(NFBR.a5q.Key.a6M),
    preview:
      params.get(NFBR.a5q.Key.LOOK_INSIDE) !== NFBR.a5q.LookInsideType.DISABLED,
    previewType:
      params.get(NFBR.a5q.Key.LOOK_INSIDE) ?? NFBR.a5q.LookInsideType.DISABLED,
    contentType: a6l.getContentType(),
    title: true,
  });
  const content = new NFBR.a6i.Content(a5w.url);
  const a5n = new NFBR.a5n();
  await a5n.a5s(content, "configuration", a6l);

  const imageFiles: Array<ImageFile> = [];

  for (const index of Object.keys(content.files)) {
    const file = content.files[index];
    const conf = content.configuration.contents[index];
    const {
      No,
      DummyWidth,
      DummyHeight,
      Size: { Width, Height },
    } = file.FileLinkInfo.PageLinkInfoList[0].Page;

    const page = new NFBR.a6i.Page(
      `${conf.file}/${No}.jpeg`,
      index,
      `${conf["original-file-path"]}#-acs-position-${file.PageToBookmark[0][0]}-${file.PageToBookmark[0][1]}`,
    );

    const w = [...`${conf.file}/${No}`]
      .map((c) => c.charCodeAt(0))
      .reduce((a, cc) => a + cc, 0);

    const pattern = (w % NFBR.a0X.a3h) + 1;
    const blocks = NFBR.a3E.a3f(
      Width + DummyWidth,
      Height + DummyHeight,
      NFBR.a0X.a3g,
      NFBR.a0X.a3G,
      pattern,
    );

    const url = `${a5w.url}${page.url}`;

    imageFiles.push({
      url,
      blocks,
      width: Width,
      height: Height,
    });
  }

  return imageFiles;
}

export function DmmBooks(browser: Browser) {
  async function* getSeriesBooks(
    ctx: BrowserContext,
    series: {
      seriesId: string;
      shopName: string;
      title: string;
      authors: Array<string>;
    },
  ): AsyncGenerator<Book> {
    const endpoint = `https://book.dmm.com/ajax/bff/contents/?shop_name=${series.shopName}&series_id=${series.seriesId}`;
    const pager = {
      page: 1,
      perPage: 0,
      totalCount: Infinity,
    };

    while ((pager.page - 1) * pager.perPage <= pager.totalCount) {
      const res = await ctx.request.get(`${endpoint}&page=${pager.page}`);

      if (!res.ok()) {
        throw new Error(`${res.status()} ${res.statusText()}`);
      }

      const body: {
        volume_books: Array<{
          title: string;
          purchased?: {
            streaming_url: string;
          };
        }>;
        pager: {
          page: number;
          per_page: number;
          total_count: number;
        };
      } = await res.json();

      for (const book of body.volume_books.filter((b) => b.purchased)) {
        yield {
          id: NaN,
          platform: "dmm-books",
          readerUrl: book.purchased?.streaming_url!,
          title: book.title || series.title || "",
          authors: series.authors,
        };

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

      pager.page += 1;
      pager.perPage = body.pager.per_page;
      pager.totalCount = body.pager.total_count;
    }
  }

  async function* getAllBooks(ctx: BrowserContext): AsyncGenerator<Book> {
    const endpoint = "https://book.dmm.com/ajax/bff/library/?shop_name=all";
    const pager = {
      page: 1,
      perPage: 0,
      totalCount: Infinity,
    };

    while ((pager.page - 1) * pager.perPage <= pager.totalCount) {
      const res = await ctx.request.get(`${endpoint}&page=${pager.page}`);

      if (!res.ok()) {
        throw new Error(`${res.status()} ${res.statusText()}`);
      }

      const body: {
        series_books: Array<{
          shop_name: string;
          series_id: string;
          title: string;
          author: Array<string>;
        }>;
        pager: {
          page: number;
          per_page: number;
          total_count: number;
        };
      } = await res.json();

      for (const series of body.series_books) {
        yield* getSeriesBooks(ctx, {
          seriesId: series.series_id,
          shopName: series.shop_name,
          title: series.title,
          authors: series.author,
        });
      }

      pager.page += 1;
      pager.perPage = body.pager.per_page;
      pager.totalCount = body.pager.total_count;
    }
  }

  return {
    async *pull(): AsyncGenerator<Book> {
      const ctx = await browser.loadBrowserContext("dmm-books");

      yield* getAllBooks(ctx);

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

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

      await page.goto(book.readerUrl);

      const imageFiles = await page.evaluate(getImageFiles);

      return imageFiles.map((imageFile) => async () => {
        const blob = await browser.drawImage(page, imageFile);

        process.stderr.write(".");

        return blob;
      });
    },
    loginEndpoints: [
      "https://accounts.dmm.com/service/login/password",
      "https://www.dmm.com/service/-/exchange",
    ],
    loginSuccessUrl: (url: URL) =>
      ["https://www.dmm.com/", "https://www.dmm.co.jp/top/"].includes(url.href),
    logoutEndpoints: [
      "https://accounts.dmm.com/service/logout",
      "https://accounts.dmm.co.jp/service/logout",
    ],
  };
}

DmmBooks.siteUrl = (url: URL) =>
  ["https://book.dmm.com", "https://book.dmm.co.jp"].includes(url.origin);