import {
  fontkit,
  PDFDocument,
  PDFFont,
  PDFName,
  PDFPage,
  PDFRef,
  PDFString,
  rgb,
  shuffle,
} from "./deps.ts";
import { JSONType, Voca } from "../types.ts";

function downloadBuffer(buffer: Uint8Array, fileName: string) {
  return new Promise<void>((resolve) => {
    const a: HTMLAnchorElement = document.createElement("a");
    const blob = new Blob([buffer], { type: "application/octet-stream" });

    a.href = URL.createObjectURL(blob);
    a.download = fileName;
    document.body.appendChild(a);
    a.style.display = "none";
    a.click();
    a.remove();
    resolve();
  });
}

type DrawWordOption = {
  page: PDFPage;
  font: PDFFont;
  pdfUrlList: PDFRef[];
  num: number;
  voca: Voca;
  x: number;
  y: number;
  blind: boolean;
};
function drawWord(
  option: DrawWordOption,
) {
  const {
    page,
    font,
    pdfUrlList,
    num,
    voca,
    x,
    y,
    blind,
  } = option;
  const fontSize = 12;
  const margin = 20;
  const text = `${num}. ${voca.word}`;
  const textWidth = font.widthOfTextAtSize(text, fontSize);
  const textHeight = font.heightAtSize(fontSize);
  const textX = margin + (page.getWidth() / 2) * x;
  const textY = page.getHeight() - (fontSize + margin) * y;

  page.drawText(text, {
    x: textX,
    y: textY,
    size: fontSize,
    font,
    color: rgb(0, 0, 0),
  });
  page.drawText("_________________", {
    x: margin + (page.getWidth() / 2) * x + 140,
    y: page.getHeight() - (fontSize + margin) * y - 3,
    size: fontSize,
    font,
    color: rgb(0, 0, 0),
  });

  const pdfUrlDict = page.doc.context.obj({
    Type: "Annot",
    Subtype: "Link",
    Rect: [textX, textY, textX + textWidth, textY + textHeight],
    A: {
      Type: "Action",
      S: "URI",
      URI: PDFString.of(
        `https://en.dict.naver.com/#/search?range=all&query=${
          encodeURIComponent(voca.word)
        }`,
      ),
    },
  });
  const pdfUrl = page.doc.context.register(pdfUrlDict);
  pdfUrlList.push(pdfUrl);

  if (!blind) {
    page.drawText(voca.answer, {
      x: margin + (page.getWidth() / 2) * x + 140,
      y: page.getHeight() - (fontSize + margin) * y,
      size: 8,
      font,
      color: rgb(1, 0, 0),
    });
  }
}

export async function makePdf(
  voca_list: Voca[],
  type: "TEST" | "LIST",
) {
  const pdfDoc = await PDFDocument.create();

  if (type === "LIST") {
    voca_list.sort(wordCompare);
  } else {
    voca_list = shuffle(voca_list);
  }

  const fontBytes = await fetch("./NanumGothic.ttf").then((res) =>
    res.arrayBuffer()
  );
  pdfDoc.registerFontkit(fontkit);
  const customFont = await pdfDoc.embedFont(fontBytes);
  const perColumn = 25;
  const perPage = 50;

  let page!: PDFPage;
  let col = -1;
  let yCursor = 1;
  let pdfUrlList: PDFRef[] = [];

  // 문제 생성
  if (type === "TEST") {
    for (let i = 0; i < voca_list.length; ++i) {
      const data = voca_list[i];
      if (i % perPage === 0) {
        if (page) {
          page.node.set(PDFName.of("Annots"), pdfDoc.context.obj(pdfUrlList));
        }
        pdfUrlList = [];
        page = pdfDoc.addPage();

        yCursor = 1;
        col = -1;
      }
      if (i % perColumn === 0) {
        col++;
        yCursor = 1;
      }

      drawWord({
        page,
        font: customFont,
        pdfUrlList,
        num: i + 1,
        voca: data,
        x: col,
        y: yCursor,
        blind: true,
      });
      yCursor++;
    }
  }

  // 답 생성
  for (let i = 0; i < voca_list.length; ++i) {
    const data = voca_list[i];
    if (i % perPage === 0) {
      if (page) {
        page.node.set(PDFName.of("Annots"), pdfDoc.context.obj(pdfUrlList));
      }
      pdfUrlList = [];
      page = pdfDoc.addPage();
      yCursor = 1;
      col = -1;
    }
    if (i % perColumn === 0) {
      col++;
      yCursor = 1;
    }

    drawWord({
      page,
      font: customFont,
      pdfUrlList,
      num: i + 1,
      voca: data,
      x: col,
      y: yCursor,
      blind: false,
    });
    yCursor++;
  }

  const pdfBytes = await pdfDoc.save();
  return pdfBytes;
}

const wordCompare = (a: Voca, b: Voca) => {
  const [wordA, wordB] = [a.word.toUpperCase(), b.word.toUpperCase()];
  if (wordA < wordB) {
    return -1;
  }
  if (wordA > wordB) {
    return 1;
  }
  return 0;
};

export async function downloadVoca(
  levels: number[],
  day: number,
  type: "TEST" | "LIST",
) {
  if (levels.includes(10)) levels = [6, 7, 8, 9];
  const json: JSONType = await (await fetch("/voca.json")).json();
  const voca_list = json.data.filter((chunk) =>
    levels.includes(chunk.level) && chunk.day === day
  ).flatMap((chunk) => chunk.voca_list);
  const bufferPdf = await makePdf(shuffle(voca_list), type);
  await downloadBuffer(
    bufferPdf,
    `voca-${type.toLowerCase()}-day${day}-level(${levels.join(",")}).pdf`,
  );
}
