/* =========================================================
   STATE GLOBAL DEL FLUJO
========================================================= */
const state = {
  giftcardCount: 1,
  amounts: [],
  sender: { name: "", email: "" },   // solo email, sin whatsapp
  recipients: [],                    // [{ name, contact(email), message }]
  format: "square",
  selectedImages: []
};

/* Helpers */
const $ = s => document.querySelector(s);
const $$ = s => Array.from(document.querySelectorAll(s));

document.addEventListener("DOMContentLoaded", () => {
  goToStep(1);
  const d = document.querySelector('.format-card[data-format="square"]');
  if (d) d.classList.add("selected");
  fixIOSViewport();
});

/* =========================================================
   NAVEGACIÓN ENTRE PASOS
   (1: Cantidad, 2: Montos, 3: Remitente,
    4: Destinatarios, 5: Formato, 6: Imágenes,
    7: Previsualización, 8: Resumen/Pago)
========================================================= */
function goToStep(n) {
  $$(".step").forEach(el => el.classList.remove("step-active"));
  const stepEl = $("#step" + n);
  if (stepEl) stepEl.classList.add("step-active");
  updateStepper(n);

  if (n === 2) renderAmountCards();
  if (n === 3) renderSenderFields();
  if (n === 4) generateRecipientForms();
  if (n === 6) renderImageSelectors();
  if (n === 7) renderPreviewCards();
  if (n === 8) renderCheckout();
}

function updateStepper(current) {
  const dots = $$(".app-step-dot");
  dots.forEach(dot => {
    const step = parseInt(dot.dataset.step, 10);
    dot.classList.remove("active", "completed");
    if (step === current) dot.classList.add("active");
    else if (step < current) dot.classList.add("completed");
  });
}

function isValidEmail(email) {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return re.test(email.trim());
}

/* =========================================================
   PASO 1 — CANTIDAD
========================================================= */
function onNextFrom1() {
  const cnt = parseInt($("#giftcardCount").value || "0", 10);
  if (!cnt || cnt < 1) {
    Swal.fire({
      icon: "warning",
      title: "Cantidad inválida",
      text: "Ingresa al menos 1 giftcard."
    });
    return;
  }

  state.giftcardCount = cnt;
  state.amounts = new Array(cnt).fill(null);
  state.recipients = new Array(cnt)
    .fill(null)
    .map(() => ({ name: "", contact: "", message: "" }));
  state.selectedImages = new Array(cnt).fill(null);

  goToStep(2);
}

/* =========================================================
   PASO 2 — MONTOS
========================================================= */
async function loadAmountOptions() {
  const res = await fetch(`${BASE_URL}get-amount-options.php`);
  const text = await res.text();
  console.log("RAW:", text);
  try {
    const data = JSON.parse(text);
    console.log("PARSED:", data);
    return data.options || [];
  } catch (e) {
    console.error("JSON ERROR:", e);
    return [];
  }
}

async function renderAmountCards() {
  const cont = $("#amountContainer");
  cont.innerHTML = "";

  // Cargar montos desde la BD
  const options = await loadAmountOptions(); // ← trae icon, amount, title, desc
    window.lastAmountOptions = options; // ← NECESARIO PARA MOSTRAR TITULO EN QR

  

  for (let i = 0; i < state.giftcardCount; i++) {
    const block = document.createElement("div");
    block.className = "amount-block";
    block.innerHTML = `<h6 class="fw-semibold mb-2">Giftcard ${i + 1}</h6>`;

    const opts = document.createElement("div");
    opts.className = "amount-options";

    // === MONTO DESDE BD (DINÁMICO) ===
    options.forEach(opt => {
      const card = document.createElement("div");
      card.className = "amount-card amount-card-pro";
      card.dataset.idx = i;
      card.dataset.value = opt.amount;

      card.innerHTML = `
        <div class="amount-icon-wrap">
          <img src="${opt.icon}" class="amount-icon" />
        </div>
        <div class="amount-badge">$${opt.amount}</div>
        <div class="ac-title">${opt.title}</div>
        <div class="ac-desc">${opt.description}</div>
        <span class="checkmark">✔</span>
      `;

      if (state.amounts[i] === opt.amount) card.classList.add("selected");
      card.onclick = () => selectAmount(i, opt.amount, card);

      opts.appendChild(card);
    });

    // === MONTO LIBRE ===
    const free = document.createElement("div");
    free.className = "amount-card amount-card-pro";
    free.dataset.idx = i;
    free.dataset.value = "free";

    free.innerHTML = `
      <div class="amount-icon-wrap">
        <img src="icons/libre.png" class="amount-icon" />
      </div>
      <div class="amount-badge free-badge">Libre</div>
      <div class="ac-title">✏️ Monto libre</div>
      <div class="ac-desc">Ingresa el monto que desees.</div>
      <span class="checkmark">✔</span>
    `;

    if (state.amounts[i] && !options.some(p => p.amount === state.amounts[i])) {
      free.classList.add("selected");
    }

    free.onclick = () => {
      selectAmount(i, "free", free);
      freeInput.style.display = "block";
    };

    const freeInput = document.createElement("input");
    freeInput.className = "form-control mt-2 free-input";
    freeInput.id = "freeInput" + i;
    freeInput.placeholder = "Ingresa monto";

    if (state.amounts[i] && !options.some(p => p.amount === state.amounts[i])) {
      freeInput.value = state.amounts[i];
      freeInput.style.display = "block";
    } else {
      freeInput.style.display = "none";
    }

    freeInput.oninput = () => {
      const v = parseInt(freeInput.value || "0", 10);
      if (!isNaN(v) && v > 0) state.amounts[i] = v;
    };

    opts.appendChild(free);
    block.appendChild(opts);
    block.appendChild(freeInput);
    cont.appendChild(block);
  }
}

function selectAmount(idx, value, cardEl) {
  document
    .querySelectorAll(`.amount-card[data-idx="${idx}"]`)
    .forEach(c => c.classList.remove("selected"));

  cardEl.classList.add("selected");

  if (value === "free") {
    const input = $("#freeInput" + idx);
    input.style.display = "block";
    input.focus();
    state.amounts[idx] = parseInt(input.value || "0", 10) || null;
  } else {
    state.amounts[idx] = value;
    const input = $("#freeInput" + idx);
    if (input) {
      input.style.display = "none";
      input.value = "";
    }
  }
}

function onNextFrom2() {
  for (let i = 0; i < state.giftcardCount; i++) {
    if (!state.amounts[i] || state.amounts[i] <= 0) {
      Swal.fire({
        icon: "warning",
        title: "Falta monto",
        text: `Selecciona el monto para la giftcard ${i + 1}.`
      });
      return;
    }
  }
  goToStep(3);
}

/* =========================================================
   PASO 3 — REMITENTE (SOLO EMAIL)
========================================================= */
function renderSenderFields() {
  const c = $("#senderFields");
  c.innerHTML = "";

  const name = document.createElement("input");
  name.className = "form-control mb-2";
  name.id = "senderName";
  name.placeholder = "Nombre del remitente";
  name.value = state.sender.name;
  c.appendChild(name);

  const email = document.createElement("input");
  email.className = "form-control mb-2";
  email.type = "email";
  email.id = "senderEmail";
  email.placeholder = "Correo electrónico del remitente";
  email.value = state.sender.email;
  c.appendChild(email);
}

function onNextFrom3() {
  const name = $("#senderName").value.trim();
  const email = $("#senderEmail").value.trim();

  if (!name || !email) {
    Swal.fire({
      icon: "warning",
      title: "Datos incompletos",
      text: "Nombre y correo del remitente son obligatorios."
    });
    return;
  }

  if (!isValidEmail(email)) {
    Swal.fire({
      icon: "warning",
      title: "Correo inválido",
      text: "Ingresa un correo electrónico válido."
    });
    return;
  }

  state.sender = { name, email };
  goToStep(4);
}

/* =========================================================
   PASO 4 — DESTINATARIOS (EMAIL OBLIGATORIO)
========================================================= */
function generateRecipientForms() {
  const container = $("#recipientContainer");
  container.innerHTML = "";

  for (let i = 0; i < state.giftcardCount; i++) {
    const rec = state.recipients[i] || { name: "", contact: "", message: "" };

    const div = document.createElement("div");
    div.className = "recipient-form";
    div.dataset.idx = i;

    div.innerHTML = `
      <label class="fw-semibold d-block mb-2">Giftcard ${i + 1}</label>
      <input class="form-control mb-2 recipient-name"
             placeholder="Nombre del destinatario"
             value="${escapeHtml(rec.name)}">
      <input class="form-control mb-2 recipient-contact"
             placeholder="Correo del destinatario"
             value="${escapeHtml(rec.contact)}">
      <textarea class="form-control mb-1 recipient-message"
          rows="2"
          maxlength="40"
          oninput="updateMsgCounter(this)"
          placeholder="Mensaje personal (máx 40 caracteres)">
${escapeHtml(rec.message)}
</textarea>

<div class="text-end small text-muted msg-counter">
  ${(rec.message || '').length}/40
</div>

    `;

    container.appendChild(div);
  }
}

function updateMsgCounter(el) {
  const counter = el.parentElement.querySelector(".msg-counter");
  if (counter) {
    counter.textContent = `${el.value.length}/40`;
  }
}


function onNextFrom4() {
  const forms = $$(".recipient-form");

  for (let i = 0; i < forms.length; i++) {
    const name = forms[i].querySelector(".recipient-name").value.trim();
    const contact = forms[i].querySelector(".recipient-contact").value.trim();
    const msg = forms[i].querySelector(".recipient-message").value.trim();

    if (!name || !contact) {
      Swal.fire({
        icon: "warning",
        title: "Datos incompletos",
        text: `Ingresa nombre y correo del destinatario ${i + 1}.`
      });
      return;
    }

    if (!isValidEmail(contact)) {
      Swal.fire({
        icon: "warning",
        title: "Correo inválido",
        text: `Ingresa un correo válido para el destinatario ${i + 1}.`
      });
      return;
    }

    state.recipients[i] = { name, contact, message: msg };
  }

  goToStep(5);
}

/* =========================================================
   PASO 5 — FORMATO
========================================================= */
function selectFormat(card) {
  $$(".format-card").forEach(c => c.classList.remove("selected"));
  card.classList.add("selected");

  // Guardar nuevo formato
  state.format = card.dataset.format;

  // Resetear imágenes porque el tamaño cambia
  state.selectedImages = new Array(state.giftcardCount).fill(null);

  // Redibujar tarjetas de selección de imagen
  renderImageSelectors();
}


function onNextFrom5() {
  if (!state.format) {
    Swal.fire({
      icon: "warning",
      title: "Sin formato",
      text: "Selecciona un formato de tarjeta."
    });
    return;
  }
  goToStep(6);
}

/* =========================================================
   PASO 6 — IMÁGENES (NUEVO SISTEMA DE SELECCIÓN)
========================================================= */
let modalInstance = null;
let modalCurrentIndex = 0;
let modalImagesList = [];

async function renderImageSelectors() {
  const holder = $("#imageButtonsHolder");
  holder.innerHTML = "";

  for (let i = 0; i < state.giftcardCount; i++) {
    const card = document.createElement("div");
    card.className = "image-select-card";
    card.dataset.idx = i;

    if (state.selectedImages[i]) {
      card.innerHTML = `
        <img src="${state.selectedImages[i]}" class="image-thumb" />
        <div class="image-label">G${i + 1}</div>
      `;
    } else {
      card.innerHTML = `
        <div class="image-thumb placeholder">
          <span>Clic para agregar</span>
        </div>
        <div class="image-label">G${i + 1}</div>
      `;
    }

    card.onclick = () => openImageModal(i);
    holder.appendChild(card);
  }
}

function openImageModal(recipientIndex) {
  modalImagesList = [];
  modalCurrentIndex = 0;

  const folder = `${BASE_URL}images/${state.format}/`;
  const attempts = Array.from({ length: 12 }, (_, k) =>
    String(k + 1).padStart(3, "0") + ".jpg"
  );

  Promise.all(
    attempts.map(n =>
      checkImageExists(folder + n).then(ok => (ok ? folder + n : null))
    )
  ).then(list => {
    modalImagesList = list.filter(Boolean);
    if (!modalImagesList.length) {
      Swal.fire({
        icon: "info",
        title: "Sin imágenes",
        text: "No se encontraron imágenes en " + folder
      });
      return;
    }

    showModalImageAt(recipientIndex, 0);

    const modalEl = $("#imgModal");
    modalEl.addEventListener(
      "shown.bs.modal",
      () => {
        $("#confirmImgBtn").onclick = () => {
          state.selectedImages[recipientIndex] =
            modalImagesList[modalCurrentIndex];

          renderImageSelectors(); // ← REDIBUJAR TARJETAS

          modalInstance.hide();
        };
      },
      { once: true }
    );

    modalInstance = new bootstrap.Modal(modalEl, { backdrop: "static" });
    modalInstance.show();
  });
}

function showModalImageAt(recipientIndex, idx) {
  modalCurrentIndex = idx;
  $("#modalMainImg").src = modalImagesList[idx];

  const thumbs = $("#modalThumbs");
  thumbs.innerHTML = "";

  modalImagesList.forEach((url, i) => {
    const img = document.createElement("img");
    img.src = url;
    img.className = i === idx ? "active" : "";
    img.onclick = () => {
      modalCurrentIndex = i;
      $("#modalMainImg").src = modalImagesList[i];
      $$("#modalThumbs img").forEach((im, j) =>
        im.classList.toggle("active", j === i)
      );
    };
    thumbs.appendChild(img);
  });
}

document.addEventListener("click", e => {
  if (e.target.id === "prevImg") {
    if (!modalImagesList.length) return;
    modalCurrentIndex =
      (modalCurrentIndex - 1 + modalImagesList.length) %
      modalImagesList.length;
    $("#modalMainImg").src = modalImagesList[modalCurrentIndex];
    $$("#modalThumbs img").forEach((im, idx) =>
      im.classList.toggle("active", idx === modalCurrentIndex)
    );
  }
  if (e.target.id === "nextImg") {
    if (!modalImagesList.length) return;
    modalCurrentIndex =
      (modalCurrentIndex + 1) % modalImagesList.length;
    $("#modalMainImg").src = modalImagesList[modalCurrentIndex];
    $$("#modalThumbs img").forEach((im, idx) =>
      im.classList.toggle("active", idx === modalCurrentIndex)
    );
  }
});

/* BOTÓN DE CERRAR DEL MODAL DE IMÁGENES */
document.addEventListener("click", (e) => {
  if (e.target.id === "closeImgModalBtn") {
    if (modalInstance) modalInstance.hide();
  }
});

/* REINICIAR ESTADO DEL MODAL AL CERRAR */
document.getElementById("imgModal").addEventListener("hidden.bs.modal", () => {
  modalImagesList = [];
  modalCurrentIndex = 0;
  document.getElementById("modalMainImg").src = "";
  document.getElementById("modalThumbs").innerHTML = "";
  modalInstance = null;
});


function checkImageExists(url) {
  return new Promise(resolve => {
    const img = new Image();
    img.onload = () => resolve(true);
    img.onerror = () => resolve(false);
    img.src = url;
  });
}

async function onNextFrom6() {
  for (let i = 0; i < state.giftcardCount; i++) {
    if (!state.selectedImages[i]) {
      const res = await Swal.fire({
        icon: "question",
        title: "Faltan imágenes",
        text: `La giftcard ${i + 1} no tiene imagen seleccionada. ¿Quieres continuar de todos modos?`,
        showCancelButton: true,
        confirmButtonText: "Sí, continuar",
        cancelButtonText: "Elegir imágenes"
      });
      if (!res.isConfirmed) return;
      break;
    }
  }
  goToStep(7);
}

/* =========================================================
   PASO 7 — PREVIEW (CON MARCA DE AGUA)
========================================================= */
async function renderPreviewCards() {
  saveRecipientData();

  const area = $("#previewArea");
  area.innerHTML = "";

  for (let i = 0; i < state.giftcardCount; i++) {
    const blob = await renderGiftcardToBlob({
      backgroundUrl: state.selectedImages[i] || makePlaceholderDataURL("GC"),
      to: state.recipients[i].name,
      from: state.sender.name,
      message: state.recipients[i].message,
      amount: state.amounts[i],
      format: state.format,
      code: "",
      watermark: true // PREVIEW con marca de agua
    });

    const url = URL.createObjectURL(blob);
    const img = document.createElement("img");
    img.src = url;
    img.className = "preview-img";

    const wrap = document.createElement("div");
    wrap.className = "preview-card-wrap";
    wrap.appendChild(img);

    area.appendChild(wrap);
  }
}

/* =========================================================
   PASO 8 — RESUMEN Y PAGO
========================================================= */
function renderCheckout() {
  saveRecipientData();

  const area = $("#checkoutArea");
  area.innerHTML = "";

  let total = 0;

  for (let i = 0; i < state.giftcardCount; i++) {
    const rec = state.recipients[i];

    // Convertir SIEMPRE a número
    const amount = parseFloat(state.amounts[i]) || 0;
    total += amount;

    const card = document.createElement("div");
    card.className = "card mb-3 p-3";

    card.innerHTML = `
      <div class="d-flex gap-3">
        <img src="${state.selectedImages[i] || makePlaceholderDataURL("GC")}"
             style="width:96px;height:64px;border-radius:12px;object-fit:cover;">
        <div class="flex-grow-1">
          <div class="fw-semibold">Giftcard ${i + 1}</div>
          <div class="small text-muted">Para: ${escapeHtml(rec.name)}</div>
          <div class="small text-muted">Correo: ${escapeHtml(rec.contact)}</div>
          <div class="small text-muted">Mensaje: <em>${escapeHtml(rec.message || "")}</em></div>
          <div class="fw-semibold mt-1">$${amount}</div>
        </div>
      </div>
    `;

    area.appendChild(card);
  }

  // Mostrar TOTAL corregido
  const totalDiv = document.createElement("div");
  totalDiv.className = "text-end fw-bold mt-2";
  totalDiv.textContent = `Total a pagar: $${total}`;
  area.appendChild(totalDiv);
}

function saveRecipientData() {
  const forms = $$(".recipient-form");
  forms.forEach((f, i) => {

    let msg = f.querySelector(".recipient-message").value.trim();
    if (msg.length > 40) msg = msg.substring(0, 40);

    state.recipients[i] = {
      name: f.querySelector(".recipient-name").value.trim(),
      contact: f.querySelector(".recipient-contact").value.trim(),
      message: msg
    };
  });
}


/* =========================================================
   GENERACIÓN FINAL (SIN MARCA DE AGUA)
========================================================= */

    async function saveCleanImagesBeforePayment(purchaseToken) {
  const imageNames = [];

  const tasks = state.recipients.map(async (_, i) => {
    const code = `GC${Date.now()}_${i}`;
    const filename = `giftcard_${Date.now()}_${i}.png`;
    imageNames[i] = filename;

    const blob = await renderGiftcardToBlob({
      backgroundUrl: state.selectedImages[i] || makePlaceholderDataURL("GC"),
      to: state.recipients[i].name,
      from: state.sender.name,
      message: state.recipients[i].message,
      amount: state.amounts[i],
      format: state.format,
      code,
      watermark: false // FINAL sin marca de agua
    });

    const base64data = await blobToBase64(blob);

    await Promise.all([
      fetch(`${BASE_URL}save-image.php`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ image: base64data, name: filename })
      }),
      fetch(`${BASE_URL}save-giftcards.php`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          recipient_name: state.recipients[i].name,
          recipient_contact: state.recipients[i].contact,
          message: state.recipients[i].message,
          sender_name: state.sender.name,
          sender_email: state.sender.email,
          amount: state.amounts[i],
          image_filename: filename,
          code,
        purchase_token: purchaseToken 
        })
      })
    ]);
  });

  await Promise.all(tasks);

  sessionStorage.setItem(
    "giftcardData",
    JSON.stringify({
      recipients: state.recipients,
      amounts: state.amounts,
      images: imageNames,
      sender: state.sender,
      days: new Array(state.giftcardCount).fill(30)
    })
  );
}


// Genera token único por compra
function generatePurchaseToken() {
  const token = "PT_" + Date.now() + "_" + Math.floor(Math.random() * 999999);
  sessionStorage.setItem("purchaseToken", token);
  return token;
}


async function proceedToPayment() {
  showSpinner();

  try {
    // 1) Generar token para TODA la compra
    const purchaseToken = generatePurchaseToken();

    // 2) Guardar imágenes y giftcards ENVIANDO purchase_token
    await saveCleanImagesBeforePayment(purchaseToken);

    // 3) Preparar items del checkout
    const items = state.amounts.map((amount, i) => ({
      name: `Giftcard ${i + 1} para ${state.recipients[i]?.name}`,
      amount: parseInt(amount, 10)
    }));

    const email = state.sender.email;
    if (!email || !email.includes("@")) {
      hideSpinner();
      await Swal.fire({
        icon: "warning",
        title: "Correo inválido",
        text: "El correo del remitente no es válido."
      });
      return;
    }

    // 4) Crear sesión de pago en PHP
    const res = await fetch(`${BASE_URL}create-checkout-session.php`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        items,
        customer_email: email,
        purchase_token: purchaseToken  // ← ¡LA CLAVE!
      })
    });

    const data = await res.json();

    if (data.url) {
      // 5) Enviar a Stripe
      window.location.href = data.url;
    } else {
      hideSpinner();
      Swal.fire({
        icon: "error",
        title: "Error en pago",
        text: "No se pudo crear la sesión de pago."
      });
    }

  } catch (err) {
    console.error("ERROR en proceedToPayment:", err);
    hideSpinner();
    Swal.fire({
      icon: "error",
      title: "Error",
      text: "Hubo un problema al preparar el pago."
    });
  }
}

/* =========================================================
   CANVAS — RENDERIZADO
========================================================= */
function renderGiftcardToBlob({
  backgroundUrl,
  to,
  from,
  message,
  amount,
  format,
  code,
  watermark
}) {
  return new Promise((resolve, reject) => {
    let w = 1000,
      h = 1000;
    if (format === "landscape") {
      w = 1400;
      h = 787;
    }
    if (format === "portrait") {
      w = 787;
      h = 1400;
    }

    const canvas = document.createElement("canvas");
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext("2d");

    const bg = new Image();
    bg.crossOrigin = "anonymous";
    bg.onload = async () => {
      try {
        const scale = Math.max(w / bg.width, h / bg.height);
        ctx.drawImage(
          bg,
          (w - bg.width * scale) / 2,
          (h - bg.height * scale) / 2,
          bg.width * scale,
          bg.height * scale
        );
      } catch {
        ctx.fillStyle = "#ccc";
        ctx.fillRect(0, 0, w, h);
      }

      /* Gradiente oscuro en la parte baja para legibilidad */
      const grad = ctx.createLinearGradient(0, h * 0.6, 0, h);
      grad.addColorStop(0, "rgba(0,0,0,0)");
      grad.addColorStop(1, "rgba(0,0,0,0.60)");
      ctx.fillStyle = grad;
      ctx.fillRect(0, h * 0.6, w, h * 0.4);

      /* =========================================
         MONTO — Playfair Display con glow premium
         ========================================= */
      ctx.save();
      ctx.shadowColor = "rgba(0,0,0,0.35)";
      ctx.shadowBlur = 18;
      ctx.shadowOffsetY = 4;

      ctx.fillStyle = "#ffffff";
      ctx.textAlign = "left";
      ctx.font = `${Math.floor(w * 0.085)}px "Playfair Display"`;
      ctx.fillText(`$${amount}`, 48, h - Math.floor(h * 0.27));

      ctx.restore();

      /* =========================================
         PARA / DE — Inter con glow suave
         ========================================= */
      ctx.save();
      ctx.shadowColor = "rgba(0,0,0,0.25)";
      ctx.shadowBlur = 10;
      ctx.shadowOffsetY = 3;

      ctx.fillStyle = "rgba(255,255,255,0.92)";
      ctx.textAlign = "left";
      ctx.font = `${Math.floor(w * 0.038)}px "Inter"`;
      ctx.fillText(`Para: ${to}`, 48, h - Math.floor(h * 0.20));
      ctx.fillText(`De: ${from}`, 48, h - Math.floor(h * 0.15));

      ctx.restore();

      /* =========================================
         MENSAJE PERSONAL — Cormorant Garamond italic
         ========================================= */
      ctx.save();
      ctx.shadowColor = "rgba(0,0,0,0.20)";
      ctx.shadowBlur = 8;
      ctx.shadowOffsetY = 2;

      ctx.fillStyle = "rgba(255,255,255,0.80)";
      ctx.textAlign = "left";
      ctx.font = `${Math.floor(w * 0.035)}px "Cormorant Garamond"`;

      wrapText(
        ctx,
        message || "",
        48,
        h - Math.floor(h * 0.09),
        w - 120,
        Math.floor(h * 0.05)
      );

      ctx.restore();
      
    /* =========================================
   SERVICIO / MONTO ARRIBA DEL QR (AJUSTADO POR FORMATO)
   ========================================= */
ctx.save();
ctx.font = `bold ${Math.floor(w * 0.032)}px "Inter"`;
ctx.fillStyle = "white";
ctx.textAlign = "right";
ctx.shadowColor = "rgba(0,0,0,0.45)";
ctx.shadowBlur = 8;

// Si existe título desde la BD
let serviceTitle = "";

try {
  const idx = state.amounts.indexOf(amount);
  if (idx >= 0 && window.lastAmountOptions) {
    const opt = window.lastAmountOptions.find(o => o.amount == amount);
    if (opt) serviceTitle = opt.title;
  }
} catch (e) {
  serviceTitle = "";
}

// Texto final
const serviceText = serviceTitle || "Giftcard";

// === Y del texto depende del FORMATO para que nunca se encime con el QR ===
let serviceY;

if (format === "landscape") {
  // Formato horizontal: subirlo más
  serviceY = h - Math.floor(h * 0.42);
} else if (format === "portrait") {
  // Formato vertical
  serviceY = h - Math.floor(h * 0.15);
} else {
  // square (cuadrado)
  serviceY = h - Math.floor(h * 0.25);
}

// Dibujar el texto alineado a la derecha
ctx.fillText(
  serviceText,
  w - 48,   // margen derecho
  serviceY  // altura ajustada según formato
);

ctx.restore();



              /* =========================================
           QR
           ========================================= */
        const qrSize = Math.floor(w * 0.18);
        const qrText = code
          ? `${BASE_URL}validar.php?code=${encodeURIComponent(code)}`
          : `${BASE_URL}preview`;
        
        await drawQRToCanvas(
          ctx,
          qrText,
          w - qrSize - 48,
          h - qrSize - 48,
          qrSize
        );


      /* =========================================
         Marca de agua SOLO en previsualización
         ========================================= */
      if (watermark) {
        ctx.save();
        ctx.globalAlpha = 0.08;
        ctx.fillStyle = "#ffffff";
        ctx.font = `bold ${Math.floor(w * 0.06)}px "Inter"`;

        ctx.translate(w / 2, h / 2);
        ctx.rotate(-Math.PI / 4);

        for (let y = -h; y < h; y += h / 3) {
          for (let x = -w; x < w; x += w / 2) {
            ctx.fillText("PREVIEW", x, y);
          }
        }
        ctx.restore();
      }

      /* =========================================
         Datos extra SOLO en la tarjeta FINAL (sin marca de agua)
         ========================================= */
      if (!watermark) {
        const telNegocio = "55 1234 5678";
        const correoNegocio = "contacto@tu-negocio.com";

        const fecha = new Date().toLocaleDateString("es-MX", {
          day: "numeric",
          month: "long",
          year: "numeric"
        });

        ctx.fillStyle = "#ffffff";
        ctx.textAlign = "left";
        ctx.font = `bold 14px "Inter"`;

        const margenX = 48;
        let y = Math.floor(h * 0.06);

        ctx.fillText(`Válida por 30 días desde ${fecha}`, margenX, y);
        y += 24;

        ctx.font = `normal 14px "Inter"`;
        ctx.fillText(`Tel: ${telNegocio}`, margenX, y);
        y += 24;

        ctx.fillText(`Email: ${correoNegocio}`, margenX, y);
      }

      /* =========================================
         Overlay "Gloss" premium tipo barniz
         ========================================= */
      ctx.save();
      const gloss = ctx.createLinearGradient(0, 0, 0, h);
      gloss.addColorStop(0, "rgba(255,255,255,0.18)");
      gloss.addColorStop(0.25, "rgba(255,255,255,0.06)");
      gloss.addColorStop(0.5, "rgba(255,255,255,0.0)");
      gloss.addColorStop(1, "rgba(0,0,0,0.0)");
      ctx.fillStyle = gloss;
      ctx.fillRect(0, 0, w, h);
      ctx.restore();

      canvas.toBlob(blob => resolve(blob));
    };

    bg.onerror = () => reject("No se pudo cargar la imagen de fondo");
    bg.src = backgroundUrl;
  });
}

/* QR helper */
async function drawQRToCanvas(ctx, text, x, y, size) {
  const qrCanvas = document.createElement("canvas");
  qrCanvas.width = size;
  qrCanvas.height = size;

  await QRCode.toCanvas(qrCanvas, text, {
    width: size,
    margin: 1,
    color: { dark: "#000000", light: "#ffffff" }
  });

  ctx.drawImage(qrCanvas, x, y, size, size);
}

/* Utils */
function escapeHtml(str) {
  return (str || "")
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;");
}

function makePlaceholderDataURL(label) {
  const c = document.createElement("canvas");
  c.width = 400;
  c.height = 300;
  const ctx = c.getContext("2d");
  ctx.fillStyle = "#e5e7eb";
  ctx.fillRect(0, 0, 400, 300);
  ctx.fillStyle = "#4b5563";
  ctx.font = "bold 32px system-ui";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillText(label, 200, 150);
  return c.toDataURL();
}

function blobToBase64(blob) {
  return new Promise(resolve => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });
}

function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
  const words = text.split(" ");
  let line = "";
  let yy = y;

  for (let n = 0; n < words.length; n++) {
    const test = line + words[n] + " ";
    const w = ctx.measureText(test).width;

    if (w > maxWidth && n > 0) {
      ctx.fillText(line, x, yy);
      line = words[n] + " ";
      yy += lineHeight;
    } else {
      line = test;
    }
  }

  ctx.fillText(line, x, yy);
}

/* =========================================================
   SPINNER
========================================================= */
function showSpinner() {
  const el = $("#loadingOverlay");
  if (el) el.classList.add("active");
}

function hideSpinner() {
  const el = $("#loadingOverlay");
  if (el) el.classList.remove("active");
}

/* =========================================================
   SAFARI — FIX DEL 100vh REAL
========================================================= */
function fixIOSViewport() {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty("--vh", `${vh}px`);
}
window.addEventListener("resize", fixIOSViewport);
window.addEventListener("orientationchange", fixIOSViewport);
