import { createCanvas, loadImage, registerFont } from "canvas";
import sharp from "sharp";
import { readFileSync } from "node:fs";

registerFont(`${import.meta.dirname}/../assets/fonts/MPLUS.ttf`, {
  family: "M PLUS Rounded 1c",
  weight: "400",
});

function autoLineBreak(
  text: string,
  maxWidth: number,
  maxHeight: number,
  font: string,
) {
  const ctx = createCanvas(maxWidth, 100).getContext("2d")!;
  ctx.font = font;

  const lines: string[] = [];
  let currentLine = "";
  const lineHeight = parseInt(font);
  const maxLines = Math.floor(maxHeight / lineHeight);

  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) {
      if (lines.length + 1 === maxLines) {
        let trimmed = currentLine;
        while (ctx.measureText(trimmed + "...").width > maxWidth && trimmed.length > 0) {
          trimmed = trimmed.slice(0, -1);
        }
        lines.push(trimmed + "...");
        return lines.join("\n");
      } else {
        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) {
    if (lines.length < maxLines) {
      lines.push(currentLine);
    } else if (lines.length === maxLines) {
      let trimmed = currentLine;
      while (ctx.measureText(trimmed + "...").width > maxWidth && trimmed.length > 0) {
        trimmed = trimmed.slice(0, -1);
      }
      lines[maxLines - 1] = trimmed + "...";
    }
  }

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

function textReplace(text: string, maxWidth: number, font: string, maxHeight: number) {
  text = text.replaceAll("\n", "")
  text = text.replaceAll("\r", "");
  return autoLineBreak(text, maxWidth, maxHeight, font)
}

async function iconReplace(color: boolean, iconURL: string) {
  let result = "";
  const bufferReq = await fetch(iconURL, { method: "GET", cache: "no-store" });

  if (bufferReq.status < 200 || bufferReq.status > 299) {
    return readFileSync(`${import.meta.dirname}/../assets/PersonIcon.txt`, "utf-8");
  }

  const buffer = await bufferReq.arrayBuffer();

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

  return result;
}

export default async function MiQ(data: {
  type: "Buffer" | "Base64Data" | "Base64URL";
  color: boolean;
  text: string;
  iconURL: string;
  username: string;
  userid: string;
}) {
  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(data.color, data.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";
  const x = 910;
  const y = 520;
  ctx.fillText(`- ${data.username}`, x, y, canvas.width - iconSize);

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

  // 本文
  const maxWidth = canvas.width - iconSize;
  const maxHeight = 480 - 19;
  const lineHeight = 40;
  ctx.font = `${lineHeight}px "M PLUS Rounded 1c"`;
  ctx.fillStyle = "white";
  ctx.textAlign = "center";
  ctx.textBaseline = "top";

  const wrappedText = textReplace(data.text, maxWidth, ctx.font, maxHeight);
  const lines = wrappedText.split("\n");
  lines.forEach((line, index) => {
    ctx.fillText(line, x, 40 + index * lineHeight);
  });

  // フェード
  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 (data.type) {
    case "Buffer":
      return canvas.toBuffer();
    case "Base64Data":
      return canvas.toDataURL().replace("data:image/png;base64,", "");
    case "Base64URL":
      return canvas.toDataURL();
    default:
      return new Error("The type property is invalid.");
  }
}