(() => {
  "use strict";

  const scriptUrl = document.currentScript?.src || location.href;
  const backgroundUrl = new URL("assets/compliance-carton-bg.jpg", scriptUrl).href;
  const templateUrl = new URL("assets/compliance-label-template.svg", scriptUrl).href;
  const TEMPLATE_SIZE = 1000;
  const PAPER_RENDER_SCALE = 1.4;
  const DEFAULT_CONTENT_FONT_SIZE = 26;
  const MIN_CONTENT_FONT_SIZE = 22;
  const MAX_CONTENT_FONT_SIZE = 34;
  const assetPromises = new Map();

  const DEFAULT_WARNINGS = [
    "⚠ CHOKING HAZARD · Keep away from babies and children",
    "⚠ RISQUE D'ÉTOUFFEMENT · Tenir hors de portée des bébés et des enfants",
    "⚠ ERSTICKUNGSGEFAHR · Fern von Babys und Kindern aufbewahren",
    "⚠ RISCHIO DI SOFFOCAMENTO · Tenere lontano da bambini e neonati",
    "⚠ RIESGO DE ASFIXIA · Mantener alejado de bebés y niños",
    "⚠ VERSTIKKINGSGEVAAR · Buiten bereik van baby's en kinderen houden",
    "⚠ RYZYKO UDUSZENIA · Trzymać z dala od niemowląt i dzieci"
  ].join("\n");

  // Coordinates use the 1000 × 1000 viewBox from the supplied compliance SVG.
  // The representative values deliberately share one x coordinate so Name,
  // both Address lines and E-mail remain vertically aligned.
  const CONTENT_FIELDS = Object.freeze({
    manufacturerName: { x: 326, y: 28, width: 648, height: 44, maxLines: 1, baseline: 61 },
    manufacturerAddress: { x: 326, y: 78, width: 648, height: 96, maxLines: 2, baseline: 139, multiBaselines: [119, 151] },
    manufacturerEmail: { x: 326, y: 180, width: 648, height: 50, maxLines: 1, baseline: 217 },
    batchNumber: { x: 231, y: 237, width: 300, height: 48, maxLines: 1, baseline: 274 },
    representativeName: { x: 370, y: 295, width: 430, height: 43, maxLines: 1, baseline: 330 },
    representativeAddress: { x: 370, y: 334, width: 430, height: 73, maxLines: 2, baseline: 364, multiBaselines: [364, 398] },
    representativeEmail: { x: 370, y: 405, width: 430, height: 39, maxLines: 1, baseline: 431 }
  });

  const defaults = () => ({
    enabled: false,
    autoAttach: true,
    manufacturerName: "",
    manufacturerAddress: "",
    manufacturerEmail: "",
    batchNumber: "S2026",
    representativeName: "",
    representativeAddress: "",
    representativeEmail: "",
    contentFontSize: DEFAULT_CONTENT_FONT_SIZE,
    warnings: DEFAULT_WARNINGS,
    // Retained for settings compatibility. The supplied SVG now fixes these
    // marks and their content, so the renderer no longer reads the old flags.
    originText: "MADE IN CHINA",
    showCe: true,
    showEcRep: true,
    showRecycling: true,
    showOrigin: true,
    masterUrl: "",
    masterObjectKey: "",
    updatedAt: "",
    version: 0
  });

  function normalize(raw) {
    const profile = { ...defaults(), ...((raw && typeof raw === "object") ? raw : {}) };
    const requestedSize = Number(profile.contentFontSize);
    profile.contentFontSize = Math.max(
      MIN_CONTENT_FONT_SIZE,
      Math.min(MAX_CONTENT_FONT_SIZE, Number.isFinite(requestedSize) ? Math.round(requestedSize) : DEFAULT_CONTENT_FONT_SIZE)
    );
    return profile;
  }

  function loadAsset(url, errorMessage) {
    if (!assetPromises.has(url)) {
      assetPromises.set(url, new Promise((resolve, reject) => {
        const image = new Image();
        image.onload = () => resolve(image);
        image.onerror = () => reject(new Error(errorMessage));
        image.src = url;
      }));
    }
    return assetPromises.get(url);
  }

  const loadBackground = () => loadAsset(backgroundUrl, "纸箱背景加载失败");
  const loadTemplate = () => loadAsset(templateUrl, "合规标签模板加载失败");

  function splitLongToken(ctx, token, maxWidth) {
    const chunks = [];
    let chunk = "";
    for (const character of [...token]) {
      const candidate = `${chunk}${character}`;
      if (chunk && ctx.measureText(candidate).width > maxWidth) {
        chunks.push(chunk);
        chunk = character;
      } else {
        chunk = candidate;
      }
    }
    if (chunk) chunks.push(chunk);
    return chunks.length ? chunks : [""];
  }

  function wrapParagraph(ctx, paragraph, maxWidth) {
    const source = String(paragraph || "").trim();
    if (!source || ctx.measureText(source).width <= maxWidth) return [source];

    const words = source.split(/\s+/).filter(Boolean);
    if (words.length <= 1) return splitLongToken(ctx, source, maxWidth);

    const lines = [];
    let line = "";
    for (const word of words) {
      const candidate = line ? `${line} ${word}` : word;
      if (line && ctx.measureText(candidate).width > maxWidth) {
        lines.push(line);
        line = "";
      }
      if (!line && ctx.measureText(word).width > maxWidth) {
        const chunks = splitLongToken(ctx, word, maxWidth);
        lines.push(...chunks.slice(0, -1));
        line = chunks.at(-1) || "";
      } else {
        line = line ? `${line} ${word}` : word;
      }
    }
    if (line) lines.push(line);
    return lines;
  }

  function wrapText(ctx, value, maxWidth, maxLines) {
    const source = String(value || "").trim();
    if (!source) return [];
    const lines = [];
    for (const paragraph of source.split(/\r?\n/)) {
      lines.push(...wrapParagraph(ctx, paragraph, maxWidth));
    }
    return lines.slice(0, Math.max(1, maxLines));
  }

  function fieldLines(ctx, value, box, fontSize) {
    ctx.font = `400 ${fontSize}px Arial, sans-serif`;
    return wrapText(ctx, value, Math.max(20, box.width - 8), box.maxLines);
  }

  function fieldBaselines(lines, box) {
    if (lines.length > 1 && Array.isArray(box.multiBaselines)) {
      return box.multiBaselines.slice(0, lines.length);
    }
    return [box.baseline];
  }

  function drawFieldValue(ctx, value, box, fontSize) {
    const lines = fieldLines(ctx, value, box, fontSize);
    if (!lines.length) return lines;
    const baselines = fieldBaselines(lines, box);

    ctx.save();
    ctx.beginPath();
    ctx.rect(box.x, box.y, box.width, box.height);
    ctx.clip();
    ctx.fillStyle = "#080808";
    ctx.font = `400 ${fontSize}px Arial, sans-serif`;
    ctx.textAlign = "left";
    ctx.textBaseline = "alphabetic";
    lines.forEach((line, index) => ctx.fillText(line, box.x + 4, baselines[index]));
    ctx.restore();
    return lines;
  }

  function canonicalWarnings(value) {
    return String(value || "").split(/\r?\n/).map(line => line.trim()).filter(Boolean).join("\n");
  }

  function drawCustomWarnings(ctx, profile) {
    if (canonicalWarnings(profile.warnings) === canonicalWarnings(DEFAULT_WARNINGS)) return;
    const warnings = canonicalWarnings(profile.warnings).split("\n").filter(Boolean);
    ctx.fillStyle = "#fff";
    ctx.fillRect(15, 462, 970, 350);
    ctx.fillStyle = "#090909";
    ctx.font = "400 26px Arial, sans-serif";
    ctx.textAlign = "left";
    ctx.textBaseline = "top";
    let y = 468;
    for (const warning of warnings) {
      const value = warning.startsWith("⚠") ? warning : `⚠ ${warning}`;
      for (const line of wrapText(ctx, value, 960, 2)) {
        if (y > 792) return;
        ctx.fillText(line, 18, y);
        y += 31;
      }
      y += 5;
    }
  }

  function inspectLayout(rawProfile) {
    const profile = normalize(rawProfile);
    const probe = document.createElement("canvas");
    const ctx = probe.getContext("2d");
    const fields = {};
    Object.entries(CONTENT_FIELDS).forEach(([key, box]) => {
      fields[key] = {
        box: { ...box },
        lines: fieldLines(ctx, profile[key], box, profile.contentFontSize)
      };
      fields[key].baselines = fieldBaselines(fields[key].lines, box);
    });
    return { contentFontSize: profile.contentFontSize, fields };
  }

  function renderPaper(profile, template) {
    const paper = document.createElement("canvas");
    paper.width = Math.round(TEMPLATE_SIZE * PAPER_RENDER_SCALE);
    paper.height = Math.round(TEMPLATE_SIZE * PAPER_RENDER_SCALE);
    const ctx = paper.getContext("2d");
    ctx.scale(PAPER_RENDER_SCALE, PAPER_RENDER_SCALE);
    ctx.drawImage(template, 0, 0, TEMPLATE_SIZE, TEMPLATE_SIZE);
    drawCustomWarnings(ctx, profile);
    Object.entries(CONTENT_FIELDS).forEach(([key, box]) => {
      drawFieldValue(ctx, profile[key], box, profile.contentFontSize);
    });
    return paper;
  }

  async function draw(canvas, rawProfile) {
    const profile = normalize(rawProfile);
    const [background, template] = await Promise.all([loadBackground(), loadTemplate()]);
    const frame = document.createElement("canvas");
    frame.width = 2000;
    frame.height = 2000;
    const ctx = frame.getContext("2d", { alpha: false });
    const scale = Math.max(frame.width / background.width, frame.height / background.height);
    const drawWidth = background.width * scale;
    const drawHeight = background.height * scale;
    ctx.drawImage(background, (frame.width - drawWidth) / 2, (frame.height - drawHeight) / 2, drawWidth, drawHeight);

    const paper = renderPaper(profile, template);
    const paperSize = 1148;
    const paperX = 426;
    const paperY = 376;
    const paperRadius = 10;

    // A one-pixel contact line and a four-pixel lower paper edge make the
    // label read as physically attached without creating a floating shadow.
    ctx.save();
    ctx.beginPath();
    ctx.roundRect(paperX, paperY, paperSize, paperSize, paperRadius);
    ctx.clip();
    ctx.drawImage(paper, paperX, paperY, paperSize, paperSize);
    ctx.restore();
    ctx.strokeStyle = "rgba(53,33,18,.22)";
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.roundRect(paperX + 0.5, paperY + 0.5, paperSize - 1, paperSize - 1, paperRadius);
    ctx.stroke();
    const edge = ctx.createLinearGradient(0, paperY + paperSize, 0, paperY + paperSize + 4);
    edge.addColorStop(0, "#e5dfd4");
    edge.addColorStop(0.55, "#c4b8a7");
    edge.addColorStop(1, "#8e7b64");
    ctx.fillStyle = edge;
    ctx.fillRect(paperX + 1, paperY + paperSize, paperSize - 2, 4);
    ctx.fillStyle = "rgba(50,30,15,.22)";
    ctx.fillRect(paperX + 2, paperY + paperSize + 4, paperSize - 4, 1);

    // Build the complete image off-screen, then replace the visible preview in
    // one paint. This prevents a save/re-render from briefly showing a blank or
    // carton-only canvas while the template asset is still resolving.
    canvas.width = frame.width;
    canvas.height = frame.height;
    canvas.getContext("2d", { alpha: false }).drawImage(frame, 0, 0);
    return canvas;
  }

  function toJpegBlob(canvas, quality = 0.94) {
    return new Promise((resolve, reject) => canvas.toBlob(
      blob => blob ? resolve(blob) : reject(new Error("JPG 图片生成失败")),
      "image/jpeg",
      quality
    ));
  }

  window.YilianComplianceLabel = {
    defaults,
    normalize,
    draw,
    toJpegBlob,
    inspectLayout,
    fieldBoxes: CONTENT_FIELDS,
    backgroundUrl,
    templateUrl,
    contentFontBounds: {
      minimum: MIN_CONTENT_FONT_SIZE,
      maximum: MAX_CONTENT_FONT_SIZE,
      defaultValue: DEFAULT_CONTENT_FONT_SIZE
    }
  };
})();
