(function () {
  const canvas = document.getElementById("certificateCanvas");
  const ctx = canvas.getContext("2d");
  const state = document.getElementById("renderState");
  const qrScratch = document.getElementById("qrScratch");

  const W = 1024;
  const H = 759;
  let scaleX = 1;
  let scaleY = 1;

  const text = {
    loading: "\u52a0\u8f7d\u4e2d",
    rendering: "\u6e32\u67d3\u4e2d",
    ready: "\u5df2\u66f4\u65b0",
    imageFailed: "\u539f\u56fe\u52a0\u8f7d\u5931\u8d25",
    title: "\u7ed3\u4e1a\u8bc1\u4e66",
    proves: "\u7279\u6b64\u8bc1\u660e",
    inWord: "\u5728",
    year: "\u5e74",
    month: "\u6708",
    day: "\u65e5",
    toWord: "\u81f3",
    finished: "\u65e5\u5b8c\u6210\u4e86",
    issueDate: "\u53d1\u8bc1\u65e5\u671f\uff1a",
    certNo: "\u8bc1\u4e66\u7f16\u53f7\uff1a",
    fallbackStudent: "\u5b66\u5458",
    filePrefix: "\u7ed3\u4e1a\u8bc1\u4e66"
  };

  const defaults = {
    studentName: "\u5f20\u4e09",
    courseName: "\u667a\u6167\u8d4b\u80fd\u7597\u6108\u5e08",
    stageName: "\u521d\u7ea7\u8bfe\u7a0b\u7684\u4e13\u4e1a\u5b66\u4e60\u3002",
    certificateNo: "HY20260602DYC001",
    startYear: "2026",
    startMonth: "6",
    startDay: "1",
    endYear: "2026",
    endMonth: "6",
    endDay: "2",
    issueDate: "2026 \u5e74 6 \u6708 2 \u65e5",
    mentorLabel: "\u5bfc\u5e08\uff1a",
    mentorName: "\u5362\u6167",
    issuerLeft: "\u6df1\u5733\u5e02\u6167\u6108\u6587\u5316\u79d1\u6280\u6709\u9650\u516c\u53f8",
    issuerRight: "\u56fd\u9645\u751f\u547d\u667a\u6167\u542f\u8499\u7814\u7a76\u9662",
    verifyUrl: "https://example.com/cert/HY20260602DYC001"
  };

  const controls = {};
  Object.keys(defaults).forEach((key) => {
    controls[key] = document.getElementById(key);
  });
  controls.showOriginal = document.getElementById("showOriginal");
  controls.showDebug = document.getElementById("showDebug");

  const original = new Image();
  original.src = "./assets/original.png";
  original.onload = () => {
    syncCanvasSize(true);
    render();
  };
  original.onerror = () => {
    state.textContent = text.imageFailed;
    render();
  };

  let renderTimer = null;
  let qrText = "";

  bindEvents();
  render();

  function bindEvents() {
    Object.values(controls).forEach((input) => {
      if (!input) return;
      input.addEventListener("input", scheduleRender);
      input.addEventListener("change", scheduleRender);
    });

    document.getElementById("resetBtn").addEventListener("click", () => {
      Object.entries(defaults).forEach(([key, value]) => {
        controls[key].value = value;
      });
      controls.showOriginal.checked = true;
      controls.showDebug.checked = false;
      render();
    });

    document.getElementById("downloadBtn").addEventListener("click", () => {
      render();
      const link = document.createElement("a");
      const name = safeFileName(value("studentName") || text.fallbackStudent);
      link.download = `${text.filePrefix}_${name}.png`;
      link.href = canvas.toDataURL("image/png", 1);
      link.click();
    });
  }

  function scheduleRender() {
    window.clearTimeout(renderTimer);
    renderTimer = window.setTimeout(render, 80);
  }

  function value(key) {
    return (controls[key] && controls[key].value || "").trim();
  }

  function render() {
    try {
      state.textContent = text.rendering;

      const usingOriginal = controls.showOriginal.checked && original.complete && original.naturalWidth;
      syncCanvasSize(usingOriginal);
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.setTransform(scaleX, 0, 0, scaleY, 0, 0);

      if (usingOriginal) {
        ctx.drawImage(original, 0, 0, W, H);
        eraseEditableAreas();
      } else {
        drawFallbackBase();
      }

      drawStudentName();
      drawMainText();
      drawIssueDate();
      drawCertificateNo();

      if (usingOriginal) {
        drawOriginalMentorLayer();
      } else {
        drawMentor();
        drawFooter();
        drawQr();
      }

      if (controls.showDebug.checked) {
        drawDebugAreas();
      }

      state.textContent = text.ready;
    } catch (err) {
      console.error("Certificate render failed:", err);
      state.textContent = "渲染异常";
    }
  }

  function syncCanvasSize(usingOriginal) {
    const nextWidth = usingOriginal ? original.naturalWidth : W;
    const nextHeight = usingOriginal ? original.naturalHeight : H;
    if (canvas.width !== nextWidth) canvas.width = nextWidth;
    if (canvas.height !== nextHeight) canvas.height = nextHeight;
    scaleX = nextWidth / W;
    scaleY = nextHeight / H;
  }

  const areas = [
    { x: 150, y: 455, w: 760, h: 119, pad: 16 }
  ];

  function eraseEditableAreas() {
    areas.forEach((area) => eraseWithPaper(area));
  }

  function eraseWithPaper(area) {
    if (!original.complete || !original.naturalWidth) return;

    const pad = area.pad || 8;
    const sourceX = Math.round((area.sourceX || 72) * scaleX);
    const sourceY = Math.round((area.sourceY || Math.max(0, area.y - pad)) * scaleY);
    const sourceW = Math.round(88 * scaleX);
    const patch = document.createElement("canvas");
    patch.width = Math.round((area.w + pad * 2) * scaleX);
    patch.height = Math.round((area.h + pad * 2) * scaleY);
    const p = patch.getContext("2d");

    for (let x = 0; x < patch.width; x += sourceW) {
      const tileW = Math.min(sourceW, patch.width - x);
      p.drawImage(
        original,
        sourceX,
        sourceY,
        tileW,
        patch.height,
        x,
        0,
        tileW,
        patch.height
      );
    }

    p.globalCompositeOperation = "destination-in";
    p.globalCompositeOperation = "source-over";
    p.globalAlpha = area.blendAlpha || 0.38;
    p.fillStyle = "#fbf7ef";
    p.fillRect(0, 0, patch.width, patch.height);
    p.globalAlpha = 1;

    p.globalCompositeOperation = "destination-in";
    const g = p.createLinearGradient(0, 0, 0, patch.height);
    g.addColorStop(0, "rgba(0,0,0,0)");
    g.addColorStop(0.04, "rgba(0,0,0,1)");
    g.addColorStop(0.96, "rgba(0,0,0,1)");
    g.addColorStop(1, "rgba(0,0,0,0)");
    p.fillStyle = g;
    p.fillRect(0, 0, patch.width, patch.height);

    p.globalCompositeOperation = "destination-in";
    const gx = p.createLinearGradient(0, 0, patch.width, 0);
    gx.addColorStop(0, "rgba(0,0,0,0)");
    gx.addColorStop(0.035, "rgba(0,0,0,1)");
    gx.addColorStop(0.965, "rgba(0,0,0,1)");
    gx.addColorStop(1, "rgba(0,0,0,0)");
    p.fillStyle = gx;
    p.fillRect(0, 0, patch.width, patch.height);

    ctx.save();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.drawImage(patch, Math.round((area.x - pad) * scaleX), Math.round((area.y - pad) * scaleY));
    ctx.restore();
  }

  function drawFallbackBase() {
    ctx.fillStyle = "#fbf7ef";
    ctx.fillRect(0, 0, W, H);
    ctx.strokeStyle = "#25344f";
    ctx.lineWidth = 12;
    ctx.strokeRect(6, 6, W - 12, H - 12);
    ctx.strokeStyle = "#a98732";
    ctx.lineWidth = 2;
    ctx.strokeRect(42, 38, W - 84, H - 76);
    ctx.textAlign = "center";
    ctx.fillStyle = "#111";
    ctx.font = '700 52px "SimSun", serif';
    ctx.fillText(text.title, W / 2, 196);
  }

  function drawSubTitle() {
    ctx.textAlign = "center";
    ctx.fillStyle = "#49341f";
    ctx.font = '700 17px "SimSun", "Microsoft YaHei", serif';
    ctx.fillText(text.proves, W / 2, 329);
    ctx.fillStyle = "#555";
    ctx.font = '13px Georgia, serif';
    ctx.fillText("This Certifies That", W / 2, 351);
  }

  function drawStudentName() {
    const name = value("studentName");
    if (!name) return;
    ctx.textAlign = "center";
    ctx.fillStyle = "#111";
    ctx.font = '700 32px "KaiTi", "STKaiti", "SimSun", serif';
    ctx.fillText(name, W / 2, 414);
  }

  function drawMainText() {
    const line2 = value("stageName");

    ctx.fillStyle = "#111";
    ctx.textAlign = "left";
    let x = 185;
    const y = 494;
    x = drawInlineText(text.inWord, x, y, 24, 400, 18);
    x = drawInlineText(value("startYear"), x, y, 24, 400, 8);
    x = drawInlineText(text.year, x, y, 24, 400, 12);
    x = drawInlineText(value("startMonth"), x, y, 24, 400, 6);
    x = drawInlineText(text.month, x, y, 24, 400, 12);
    x = drawInlineText(value("startDay"), x, y, 24, 400, 4);
    x = drawInlineText(`${text.day}${text.toWord}`, x, y, 24, 400, 12);
    x = drawInlineText(value("endYear"), x, y, 24, 400, 8);
    x = drawInlineText(text.year, x, y, 24, 400, 12);
    x = drawInlineText(value("endMonth"), x, y, 24, 400, 6);
    x = drawInlineText(text.month, x, y, 24, 400, 12);
    x = drawInlineText(value("endDay"), x, y, 24, 400, 4);
    x = drawInlineText(`${text.day}\u5b8c\u6210\u4e86`, x, y, 24, 400, 8);
    drawInlineFlow([
      { text: `\u201c${value("courseName")}\u201d`, style: "course" },
      { text: line2, style: "normal", gapBefore: 14 },
    ], x, y);
  }

  function drawInlineFlow(segments, startX, startY) {
    let x = startX;
    let y = startY;
    const lineStart = 185;
    const lineHeight = 42;

    segments.forEach((segment) => {
      if (segment.gapBefore) x += segment.gapBefore;
      Array.from(segment.text || "").forEach((char) => {
        const style = inlineStyle(segment.style);
        ctx.font = style.font;
        const maxRight = y === startY ? 900 : 730;
        const width = ctx.measureText(char).width;
        if (x + width > maxRight && x > lineStart) {
          x = lineStart;
          y += lineHeight;
        }
        ctx.fillText(char, x, y);
        if (segment.style === "course") ctx.fillText(char, x + 0.45, y);
        x += width;
      });
    });
  }

  function inlineStyle(style) {
    if (style === "course") {
      return { font: '900 27px "KaiTi", "STKaiti", "Kaiti SC", "Noto Serif SC", serif' };
    }
    return { font: '400 24px "SimSun", "Noto Serif SC", serif' };
  }

  function drawInlineText(raw, x, y, size, weight, gap, style) {
    const textValue = raw || "";
    const useKaiti = style === true || style === "dateNumber";
    const family = useKaiti
      ? '"KaiTi", "STKaiti", "Kaiti SC", "Noto Serif SC", serif'
      : '"SimSun", "Noto Serif SC", serif';
    ctx.font = `${weight} ${size}px ${family}`;
    ctx.fillText(textValue, x, y);
    if (style === true || style === "dateNumber") {
      ctx.fillText(textValue, x + 0.45, y);
    }
    return x + ctx.measureText(textValue).width + gap;
  }

  function drawMentor() {
    const label = value("mentorLabel");
    const name = value("mentorName");
    if (!label && !name) return;

    ctx.fillStyle = "#111";
    ctx.textAlign = "left";
    ctx.font = '700 21px "SimSun", "Microsoft YaHei", serif';
    ctx.fillText(label, 740, 586);

    ctx.save();
    ctx.translate(812, 594);
    ctx.rotate(-0.05);
    ctx.fillStyle = "#1b1b1b";
    ctx.font = '700 34px "KaiTi", "STKaiti", "SimSun", serif';
    ctx.fillText(name, 0, 0);
    ctx.restore();
  }

  function drawOriginalMentorLayer() {
    if (!original.complete || !original.naturalWidth) return;
    ctx.drawImage(original, 720, 535, 230, 94, 720, 535, 230, 94);
  }

  function drawFooter() {
    ctx.fillStyle = "#111";
    ctx.font = '700 20px "SimSun", "Microsoft YaHei", serif';
    ctx.textAlign = "left";
    drawTextFit(value("issuerLeft"), 190, 643, 300, 20);

    ctx.textAlign = "right";
    drawTextFit(value("issuerRight"), 835, 643, 320, 20);

    ctx.textAlign = "center";
    drawIssueDate();
  }

  function drawIssueDate() {
    ctx.textAlign = "center";
    ctx.fillStyle = "#111";
    ctx.font = '700 13px "SimSun", serif';
    const parts = (value("issueDate").match(/\d+/g) || []);
    ctx.fillText(parts[0] || "", 500, 688);
    ctx.fillText(parts[1] || "", 535, 688);
    ctx.fillText(parts[2] || "", 566, 688);
  }

  function drawCertificateNo() {
    const no = value("certificateNo");
    if (!no) return;
    ctx.textAlign = "left";
    ctx.fillStyle = "#111827";
    ctx.font = '700 14px Arial, "Microsoft YaHei", sans-serif';
    ctx.fillText(`${text.certNo}${no}`, 105, 76);
  }

  function drawQr() {
    const x = W / 2 - 33;
    const y = 602;
    const box = 66;

    const qrNode = getQrNode(box);
    ctx.save();
    ctx.fillStyle = "#fff";
    ctx.fillRect(x - 5, y - 5, box + 10, box + 10);
    ctx.strokeStyle = "#d3c6a4";
    ctx.strokeRect(x - 5, y - 5, box + 10, box + 10);

    if (qrNode) {
      try {
        ctx.drawImage(qrNode, x, y, box, box);
      } catch (err) {
        console.warn("QR image is not ready yet, using fallback.", err);
        drawQrFallbackPixels(x, y);
        window.setTimeout(render, 120);
      }
    } else {
      drawQrFallbackPixels(x, y);
    }

    ctx.fillStyle = "#1f67ad";
    ctx.fillRect(x + 28, y + 28, 10, 10);
    ctx.restore();
  }

  function getQrNode(size) {
    if (!window.QRCode || !qrScratch) return null;

    const nextText = value("verifyUrl") || "https://example.com";
    if (nextText !== qrText || !qrScratch.firstChild) {
      qrText = nextText;
      qrScratch.innerHTML = "";
      new QRCode(qrScratch, {
        text: nextText,
        width: size,
        height: size,
        colorDark: "#111111",
        colorLight: "#ffffff",
        correctLevel: QRCode.CorrectLevel.H
      });
    }

    const canvasNode = qrScratch.querySelector("canvas");
    if (canvasNode) return canvasNode;

    const imgNode = qrScratch.querySelector("img");
    if (imgNode && imgNode.complete && imgNode.naturalWidth) return imgNode;
    return null;
  }

  function drawQrFallbackPixels(x, y) {
    ctx.fillStyle = "#111";
    const seed = hash(value("verifyUrl"));
    const cell = 3;
    for (let row = 0; row < 22; row++) {
      for (let col = 0; col < 22; col++) {
        const fixed = finder(col, row) || finder(col - 15, row) || finder(col, row - 15);
        const on = fixed || ((seed + row * 17 + col * 31 + row * col) % 7 < 3);
        if (on) ctx.fillRect(x + col * cell, y + row * cell, cell, cell);
      }
    }
  }

  function finder(col, row) {
    if (col < 0 || row < 0 || col > 6 || row > 6) return false;
    return col === 0 || row === 0 || col === 6 || row === 6 || (col >= 2 && col <= 4 && row >= 2 && row <= 4);
  }

  function hash(raw) {
    let n = 0;
    for (let i = 0; i < raw.length; i++) {
      n = (n * 31 + raw.charCodeAt(i)) >>> 0;
    }
    return n;
  }

  function drawTextFit(raw, x, y, maxWidth, size) {
    let fontSize = size;
    const textValue = raw || "";
    do {
      ctx.font = ctx.font.replace(/\d+px/, `${fontSize}px`);
      fontSize--;
    } while (ctx.measureText(textValue).width > maxWidth && fontSize > 12);
    ctx.fillText(textValue, x, y);
  }

  function drawDebugAreas() {
    ctx.save();
    ctx.strokeStyle = "#f04438";
    ctx.lineWidth = 2;
    ctx.setLineDash([6, 5]);
    areas.forEach((area) => ctx.strokeRect(area.x, area.y, area.w, area.h));
    ctx.restore();
  }

  function safeFileName(raw) {
    return raw.replace(/[\\/:*?"<>|]/g, "_");
  }
})();
