"use client";

/* Renders one OMR sheet to a canvas at a chosen DPI — used BOTH by the print
   view (high-DPI render → img → browser print) and by the in-app "sample
   sheet" self-demo, where we fill some bubbles and feed the image straight
   back into the scanner. Geometry comes from lib/omr-sheet.ts. */

import QRCode from "qrcode";
import { OMR_PAGE, OMR_FIDUCIAL, OMR_QR, OMR_GRID, omrBubbleCenter, omrFiducialCenters, omrQrPayload } from "@/lib/omr-sheet";

export interface RenderSheetOpts {
  institutionName: string;
  examName: string;
  subject: string;
  setLabel: string | null;
  roll: string;
  questions: number;
  /** embedded in the QR so the scanner knows the paper */
  scheduleId?: number;
  /** demo mode: pre-filled answers ("AB-D…") to feed the scanner */
  filled?: string;
}

const MM = 96 / 25.4; // px per mm at 96dpi (canvas units)
const TEAL = "#24695c";
const INK = "#111111";
const MUTED = "#5a6a66";
const LINE = "#22302c";

export async function renderOmrSheet(opts: RenderSheetOpts, dpi = 200): Promise<HTMLCanvasElement> {
  const s = dpi / 96; // scale relative to the 96dpi mm grid
  const W = Math.round(OMR_PAGE.w * MM * s);
  const H = Math.round(OMR_PAGE.h * MM * s);
  const canvas = document.createElement("canvas");
  canvas.width = W;
  canvas.height = H;
  const ctx = canvas.getContext("2d")!;
  const mm = (v: number) => v * MM * s;

  // paper
  ctx.fillStyle = "#ffffff";
  ctx.fillRect(0, 0, W, H);
  ctx.fillStyle = "#111";

  // fiducials
  const f = OMR_FIDUCIAL;
  const fm = f.margin;
  ctx.fillRect(mm(fm), mm(fm), mm(f.size), mm(f.size));
  ctx.fillRect(mm(OMR_PAGE.w - fm - f.size), mm(fm), mm(f.size), mm(f.size));
  ctx.fillRect(mm(fm), mm(OMR_PAGE.h - fm - f.size), mm(f.size), mm(f.size));
  ctx.fillRect(mm(OMR_PAGE.w - fm - f.size), mm(OMR_PAGE.h - fm - f.size), mm(f.size), mm(f.size));

  // header text
  ctx.textBaseline = "top";
  /* ---------- header ---------- */
  ctx.textAlign = "center";
  ctx.textBaseline = "top";
  ctx.fillStyle = INK;
  ctx.font = `800 ${mm(5.4)}px Poppins, Arial`;
  ctx.fillText(opts.institutionName.slice(0, 46).toUpperCase(), mm(OMR_PAGE.w / 2 - 10), mm(13));
  ctx.font = `600 ${mm(3.8)}px Poppins, Arial`;
  ctx.fillStyle = "#333333";
  ctx.fillText(`${opts.examName}  —  ${opts.subject}`, mm(OMR_PAGE.w / 2 - 10), mm(20));
  // teal accent rule
  ctx.fillStyle = TEAL;
  ctx.fillRect(mm(22), mm(25.4), mm(OMR_PAGE.w - 44 - 34), mm(0.9));
  ctx.fillStyle = MUTED;
  ctx.font = `700 ${mm(2.8)}px Rubik, Arial`;
  ctx.fillText("OPTICAL MARK RECOGNITION (OMR) ANSWER SHEET", mm(OMR_PAGE.w / 2 - 10), mm(27.6));

  /* ---------- student info box ---------- */
  const boxX = 14;
  const boxY = 34;
  const boxW = OMR_PAGE.w - 28 - 34;
  const boxH = 17;
  ctx.strokeStyle = LINE;
  ctx.lineWidth = Math.max(1, mm(0.5));
  ctx.strokeRect(mm(boxX), mm(boxY), mm(boxW), mm(boxH));
  ctx.textBaseline = "middle";
  ctx.textAlign = "left";
  ctx.fillStyle = MUTED;
  ctx.font = `700 ${mm(3)}px Rubik, Arial`;
  ctx.fillText("ROLL", mm(boxX + 4), mm(boxY + 5.5));
  ctx.fillStyle = INK;
  ctx.font = `800 ${mm(7.5)}px Rubik, Consolas, monospace`;
  ctx.fillText(opts.roll, mm(boxX + 4), mm(boxY + 12));
  ctx.strokeStyle = "#c7d4d0";
  ctx.lineWidth = Math.max(1, mm(0.35));
  ctx.beginPath();
  ctx.moveTo(mm(boxX + 42), mm(boxY + 2.5));
  ctx.lineTo(mm(boxX + 42), mm(boxY + boxH - 2.5));
  ctx.stroke();
  ctx.fillStyle = MUTED;
  ctx.font = `700 ${mm(3)}px Rubik, Arial`;
  ctx.fillText("SET", mm(boxX + 47), mm(boxY + 5.5));
  ctx.fillStyle = INK;
  ctx.font = `800 ${mm(7.5)}px Rubik, Arial`;
  ctx.fillText(opts.setLabel ?? "—", mm(boxX + 47), mm(boxY + 12));
  ctx.beginPath();
  ctx.moveTo(mm(boxX + 62), mm(boxY + 2.5));
  ctx.lineTo(mm(boxX + 62), mm(boxY + boxH - 2.5));
  ctx.stroke();
  ctx.fillStyle = MUTED;
  ctx.font = `700 ${mm(3)}px Rubik, Arial`;
  ctx.fillText("STUDENT NAME", mm(boxX + 67), mm(boxY + 5.5));
  ctx.strokeStyle = "#9aa8a4";
  ctx.setLineDash([mm(1.2), mm(1)]);
  ctx.beginPath();
  ctx.moveTo(mm(boxX + 67), mm(boxY + 13.5));
  ctx.lineTo(mm(boxX + boxW - 4), mm(boxY + 13.5));
  ctx.stroke();
  ctx.setLineDash([]);

  /* ---------- instructions ---------- */
  ctx.fillStyle = MUTED;
  ctx.font = `500 ${mm(2.7)}px Rubik, Arial`;
  ctx.textAlign = "center";
  ctx.fillText(
    "Use a dark blue/blue-black pen. Fill ONE bubble per question completely — do not tick or cross.",
    mm(OMR_PAGE.w / 2 - 10),
    mm(56)
  );
  ctx.fillText(
    "Keep all four corner squares visible. Do not fold, staple or write outside the boxes.",
    mm(OMR_PAGE.w / 2 - 10),
    mm(60)
  );

  // QR — framed, with caption
  const qrData = await QRCode.toDataURL(omrQrPayload(opts.scheduleId ?? 0, opts.roll, opts.setLabel), {
    margin: 0,
    width: Math.round(mm(OMR_QR.size)),
    errorCorrectionLevel: "M",
  });
  const qrImg = new Image();
  await new Promise<void>((res, rej) => {
    qrImg.onload = () => res();
    qrImg.onerror = () => rej(new Error("qr render"));
    qrImg.src = qrData;
  });
  const qx = OMR_QR.x;
  const qy = OMR_QR.y;
  ctx.strokeStyle = TEAL;
  ctx.lineWidth = Math.max(1, mm(0.6));
  ctx.strokeRect(mm(qx - 3), mm(qy - 3), mm(OMR_QR.size + 6), mm(OMR_QR.size + 6));
  ctx.fillStyle = "#fff";
  ctx.fillRect(mm(qx), mm(qy), mm(OMR_QR.size), mm(OMR_QR.size));
  ctx.drawImage(qrImg, mm(qx), mm(qy), mm(OMR_QR.size), mm(OMR_QR.size));
  ctx.fillStyle = MUTED;
  ctx.font = `600 ${mm(2.6)}px Rubik, Arial`;
  ctx.textAlign = "center";
  ctx.textBaseline = "top";
  ctx.fillText("AUTO-SCAN CODE", mm(qx + OMR_QR.size / 2), mm(qy + OMR_QR.size + 4));

  /* ---------- answer grid ---------- */
  const OPT = ["A", "B", "C", "D"];
  const gridTop = OMR_GRID.y - OMR_GRID.rowPitchY / 2 - 2;
  const gridBottom = OMR_GRID.y + (opts.questions - 1) * OMR_GRID.rowPitchY + OMR_GRID.rowPitchY / 2 + 2;
  const gridLeft = OMR_GRID.x - OMR_GRID.optionPitchX / 2 - 2;
  const gridRight = OMR_GRID.x + 3 * OMR_GRID.optionPitchX + OMR_GRID.optionPitchX / 2 + 2;

  // soft header band with option letters
  ctx.fillStyle = "rgba(36,105,92,0.08)";
  ctx.fillRect(mm(gridLeft), mm(gridTop), mm(gridRight - gridLeft), mm(6.4));
  ctx.fillStyle = TEAL;
  ctx.font = `800 ${mm(3.4)}px Rubik, Arial`;
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  for (let opt = 0; opt < 4; opt++) {
    ctx.fillText(OPT[opt], mm(OMR_GRID.x + opt * OMR_GRID.optionPitchX), mm(gridTop + 3.4));
  }
  ctx.font = `800 ${mm(2.6)}px Rubik, Arial`;
  ctx.fillText("Q", mm(OMR_GRID.x - 9), mm(gridTop + 3.4));

  // outer border of the answer area
  ctx.strokeStyle = LINE;
  ctx.lineWidth = Math.max(1, mm(0.5));
  ctx.strokeRect(mm(gridLeft), mm(gridTop), mm(gridRight - gridLeft), mm(gridBottom - gridTop));

  // separator every 5 questions (light gray — stays above the scan threshold)
  ctx.strokeStyle = "#cfd9d5";
  ctx.lineWidth = Math.max(1, mm(0.3));
  for (let q = 5; q < opts.questions; q += 5) {
    const y = OMR_GRID.y + (q - 1) * OMR_GRID.rowPitchY + OMR_GRID.rowPitchY / 2;
    ctx.beginPath();
    ctx.moveTo(mm(gridLeft + 1), mm(y));
    ctx.lineTo(mm(gridRight - 1), mm(y));
    ctx.stroke();
  }

  // bubbles + question numbers
  for (let q = 0; q < opts.questions; q++) {
    const rowY = omrBubbleCenter(q, 0).y;
    ctx.font = `700 ${mm(3)}px Rubik, Arial`;
    ctx.fillStyle = "#333333";
    ctx.textAlign = "right";
    ctx.fillText(String(q + 1), mm(OMR_GRID.x - 9), mm(rowY));
    ctx.textAlign = "left";

    for (let opt = 0; opt < 4; opt++) {
      const c = omrBubbleCenter(q, opt);
      ctx.beginPath();
      ctx.arc(mm(c.x), mm(c.y), mm(OMR_GRID.bubbleR), 0, Math.PI * 2);
      ctx.strokeStyle = "#222222";
      ctx.lineWidth = Math.max(1, mm(0.35));
      ctx.stroke();
      const fill = opts.filled?.[q];
      if (fill && fill !== "-" && OPT.indexOf(fill) === opt) {
        ctx.beginPath();
        ctx.arc(mm(c.x), mm(c.y), mm(OMR_GRID.bubbleR * 0.72), 0, Math.PI * 2);
        ctx.fillStyle = INK;
        ctx.fill();
      }
    }
  }

  /* ---------- footer ---------- */
  const footY = Math.min(OMR_PAGE.h - 16, gridBottom + 8);
  ctx.strokeStyle = "#9aa8a4";
  ctx.lineWidth = Math.max(1, mm(0.35));
  ctx.beginPath();
  ctx.moveTo(mm(18), mm(footY + 4));
  ctx.lineTo(mm(70), mm(footY + 4));
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(mm(115), mm(footY + 4));
  ctx.lineTo(mm(OMR_PAGE.w - 38), mm(footY + 4));
  ctx.stroke();
  ctx.fillStyle = MUTED;
  ctx.font = `600 ${mm(2.6)}px Rubik, Arial`;
  ctx.textAlign = "left";
  ctx.fillText("Student's Signature", mm(18), mm(footY + 6.6));
  ctx.fillText("Examiner's Signature", mm(115), mm(footY + 6.6));
  ctx.textAlign = "right";
  ctx.font = `500 ${mm(2.4)}px Rubik, Arial`;
  ctx.fillText(`IncrelogEMS OMR · ${opts.examName.slice(0, 26)}`, mm(OMR_PAGE.w - 30), mm(footY + 6.6));

  return canvas;
}

export { omrFiducialCenters };
