import { createCanvas, loadImage, registerFont } from "canvas";
import sharp from "sharp";
import { MiQOptions } from "./miq";

// フォント読み込み
registerFont("miq/fonts/MPLUS.ttf", {
  family: "M PLUS Rounded 1c",
  weight: "400",
});

function maxLengthCut(
  text: string,
  maxLength: number,
) {
  if (text.length > maxLength) {
    text = text.substring(0, maxLength)
    + "...";
  }

  return text;
}

function autoLineBreak(
  text: string,
  maxWidth: number,
  font: string = '48px "M PLUS Rounded 1c"'
): string {
  const ctx = createCanvas(maxWidth, 100).getContext("2d");
  ctx.font = font;

  const lines: string[] = [];
  let currentLine = "";

  for (let i = 0; i < text.length; i++) {
    const char = text[i];
    const testLine = currentLine + char;
    const width = ctx.measureText(testLine).width;

    if (width > maxWidth) {
      lines.push(currentLine);
      currentLine = char;

      while (ctx.measureText(currentLine).width > maxWidth && currentLine.length > 1) {
        const cutPoint = currentLine.length - 1;
        lines.push(currentLine.slice(0, cutPoint));
        currentLine = currentLine.slice(cutPoint);
      }
    } else {
      currentLine = testLine;
    }
  }

  if (currentLine) lines.push(currentLine);

  return lines.join("\n");
}

function textReplace(
  text: string,
  maxWidth: number,
  font?: string
) {
  // 改行削除
  text = text.replaceAll("\n", "");
  text = text.replaceAll("\r", "");
  // 自動改行
  text = autoLineBreak(text, maxWidth, font);
  // 100文字以上を省略
  text = maxLengthCut(text, 100);

  return text;
}

async function iconReplace(
  color: boolean,
  iconURL: string
) {
  let result = "";

  const buffer = await(await fetch(iconURL, {
    method: "GET",
    cache: "no-store",
  })).arrayBuffer();

  if (color) {
    const img = await sharp(Buffer.from(buffer))
    .png()
    .toBuffer();

    result = `data:image/png;base64,${img.toString("base64")}`;
  } else {
    const img = await sharp(Buffer.from(buffer))
    .png()
    .grayscale()
    .toBuffer();

    result = `data:image/png;base64,${img.toString("base64")}`;
  }

  return result;
}

/**
 * A function to generate
 * Make it a quote on Node.js.
*/
export default async function MiQ({
  type,
  color,
  text,
  iconURL,
  userName,
  userID,
}: MiQOptions) {
  // 初期化
  const canvas = createCanvas(1200, 630);
  const ctx = canvas.getContext("2d");

  // 背景描画
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // アイコン描画
  const iconImg = await loadImage(await iconReplace(
    color,
    iconURL,
  ));
  const iconSize = canvas.height;
  ctx.drawImage(iconImg, 0, 0, iconSize, iconSize);

  // ユーザー名描画
  ctx.font = '38px "M PLUS Rounded 1c"';
  ctx.fillStyle = "white";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  let x = 910;
  let y = 480;
  ctx.fillText(`- ${userName}`, x, y, canvas.width-iconSize);

  // ユーザーID描画
  ctx.font = '28px "M PLUS Rounded 1c"';
  ctx.fillStyle = "#b4b4b4";
  ctx.fillText(`@${userID}`, x, y+50, canvas.width-iconSize);

  // 本文描画
  const maxWidth = canvas.width - iconSize;
  ctx.font = '48px "M PLUS Rounded 1c"';
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillStyle = "white";
  text = textReplace(text, maxWidth, ctx.font);
  ctx.fillText(text, x, 80);

  // フェード描画
  const fadeColor = "black";
  const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
  gradient.addColorStop(0, "rgba(0, 0, 0, 0)");
  gradient.addColorStop(0.5, fadeColor);
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, iconSize, canvas.height);

  // 返答
  switch (type) {
    case "Buffer":
      return canvas.toBuffer();
    case "Base64Data":
      return canvas.toDataURL()
      .replace("data:image/png;base64,", "");
    case "Base64URL":
      return canvas.toDataURL();
    default:
      return "Error: The type property is invalid.";
  }
}
