// Poster Generator screen — Tạo poster cá nhân hoá để share social
// Exports to window: PosterScreen
// Photo upload: 100% browser-only via FileReader + Canvas API. No server.

const POSTER_FORMATS = [
  { key: 'square',    label: 'FB Feed',      ratio: '1:1',   w: 1080, h: 1080, icon: 'ri-square-line' },
  { key: 'story',     label: 'Story / TikTok', ratio: '9:16', w: 1080, h: 1920, icon: 'ri-rectangle-line' },
  { key: 'zalo',      label: 'Zalo',         ratio: '4:5',   w: 1080, h: 1350, icon: 'ri-instagram-line' },
  { key: 'landscape', label: 'Ngang OG',     ratio: '16:9',  w: 1200, h: 675,  icon: 'ri-image-line' },
];

const POSTER_STYLES = [
  { key: 'modern',     label: 'Modern',     color: '#1E3888' },
  { key: 'energy',     label: 'Energy',     color: '#14C560' },
  { key: 'minimalist', label: 'Minimalist', color: '#5C5F5F' },
  { key: 'master',     label: 'Master',     color: '#DDC800' },
];

const MAX_PHOTO_BYTES = 5 * 1024 * 1024; // 5MB
const ALLOWED_PHOTO_TYPES = ['image/jpeg', 'image/png', 'image/webp'];

// ─────────────────────────────────────────────────────────────
// Helpers — convert SVG logo string to canvas-drawable Image
// ─────────────────────────────────────────────────────────────
function loadImage(src) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = src;
  });
}

function fileToDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

async function resizePhoto(dataUrl, maxDim) {
  const img = await loadImage(dataUrl);
  const max = maxDim || 800;
  let w = img.width, h = img.height;
  if (Math.max(w, h) > max) {
    const ratio = max / Math.max(w, h);
    w = Math.round(w * ratio);
    h = Math.round(h * ratio);
  }
  const c = document.createElement('canvas');
  c.width = w; c.height = h;
  const ctx = c.getContext('2d');
  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high';
  ctx.drawImage(img, 0, 0, w, h);
  return c.toDataURL('image/jpeg', 0.85);
}

// ─────────────────────────────────────────────────────────────
// Canvas Poster Renderer
// ─────────────────────────────────────────────────────────────
async function renderPoster(opts) {
  const fmt = POSTER_FORMATS.find(f => f.key === opts.format) || POSTER_FORMATS[0];
  const N = window.SaladinNumerology || {};
  const C = window.SaladinContent || {};
  const W = fmt.w;
  const H = fmt.h;
  const canvas = document.createElement('canvas');
  canvas.width = W; canvas.height = H;
  const ctx = canvas.getContext('2d');

  const TW = 1122;
  const TH = 1402;
  const FONT_STACK = '"Be Vietnam Pro", "Noto Sans", -apple-system, sans-serif';
  const number = opts.lifePath != null ? opts.lifePath : 7;
  const safeName = (opts.userName || 'Bạn').trim();
  const nums = opts.numbers || {};
  const famousRaw = (C.NUMBER_CONTENT && C.NUMBER_CONTENT[number] && C.NUMBER_CONTENT[number].famous) || '';
  const famous = famousRaw.split(',').map((s) => s.trim()).filter(Boolean).slice(0, 2).join(' • ') || 'Charlie Chaplin • David Bowie';
  const FAMOUS_META = {
    'Charlie Chaplin': { field: 'Điện ảnh', success: 'Biểu tượng phim câm toàn cầu' },
    'David Bowie': { field: 'Âm nhạc', success: 'Huyền thoại sáng tạo đa phong cách' },
    'Walt Disney': { field: 'Giải trí', success: 'Sáng lập đế chế Disney' },
    'Lady Gaga': { field: 'Âm nhạc', success: 'Nghệ sĩ đa giải Grammy và Oscar' },
    'Steve Jobs': { field: 'Công nghệ', success: 'Đồng sáng lập Apple' },
    'Madonna': { field: 'Âm nhạc', success: 'Biểu tượng pop nhiều thập kỷ' },
    'Mark Zuckerberg': { field: 'Công nghệ', success: 'Đồng sáng lập Facebook' },
    'Barack Obama': { field: 'Chính trị', success: 'Tổng thống Hoa Kỳ thứ 44' },
    'Bill Gates': { field: 'Công nghệ', success: 'Đồng sáng lập Microsoft' },
    'Elon Musk': { field: 'Công nghệ', success: 'Lãnh đạo Tesla và SpaceX' },
    'Mẹ Teresa': { field: 'Xã hội', success: 'Giải Nobel Hòa bình 1979' },
    'Donald Trump': { field: 'Kinh doanh', success: 'Tổng thống Hoa Kỳ và doanh nhân' },
    'Oprah Winfrey': { field: 'Truyền thông', success: 'MC và nhà sản xuất hàng đầu' },
    'Albert Einstein': { field: 'Khoa học', success: 'Cha đẻ thuyết tương đối' },
  };

  // Base background image (new user-provided background)
  const bgImg = await loadImage('assets/poster-bg.png');
  const bgScale = Math.max(W / bgImg.width, H / bgImg.height);
  const bgW = bgImg.width * bgScale;
  const bgH = bgImg.height * bgScale;
  const bgX = (W - bgW) / 2;
  const bgY = (H - bgH) / 2;
  ctx.drawImage(bgImg, bgX, bgY, bgW, bgH);

  const scale = Math.min(W / TW, H / TH);
  const drawW = TW * scale;
  const drawH = TH * scale;
  const ox = (W - drawW) / 2;
  const oy = (H - drawH) / 2;

  const tx = (x) => ox + x * scale;
  const ty = (y) => oy + y * scale;
  const rr = (x, y, w, h, r) => {
    ctx.beginPath();
    ctx.roundRect(x, y, w, h, r);
  };
  const wrapLines = (text, maxWidth, font, maxLines = 3, withEllipsis = true) => {
    const raw = String(text || '').trim();
    if (!raw) return [];
    ctx.font = font;
    const words = raw.split(/\s+/);
    const lines = [];
    let line = '';
    for (let i = 0; i < words.length; i++) {
      const next = line ? `${line} ${words[i]}` : words[i];
      if (ctx.measureText(next).width <= maxWidth) {
        line = next;
      } else {
        if (line) lines.push(line);
        line = words[i];
      }
      if (lines.length === maxLines - 1 && i < words.length - 1) {
        const tail = [line, ...words.slice(i + 1)].join(' ');
        if (!withEllipsis) {
          lines.push(tail);
          return lines;
        }
        let cut = tail;
        while (cut.length > 1 && ctx.measureText(`${cut}…`).width > maxWidth) cut = cut.slice(0, -1);
        lines.push(`${cut}…`);
        return lines;
      }
    }
    if (line) lines.push(line);
    return lines.slice(0, maxLines);
  };

  const drawIconCard = (x, y, w, h, title, desc, _iconGlyph, cardBg = 'rgba(255,255,255,0.9)') => {
    rr(tx(x), ty(y), w * scale, h * scale, 22 * scale);
    ctx.fillStyle = cardBg;
    ctx.strokeStyle = 'rgba(18,110,39,0.12)';
    ctx.lineWidth = 1 * scale;
    ctx.fill(); ctx.stroke();
    const tNorm = String(title || '').toLowerCase();
    let titleColor = '#087331';
    let descColor = '#1c2d24';
    if (/(truyền đạt|giao tiếp|kết nối)/.test(tNorm)) { titleColor = '#0A4F86'; descColor = '#1E3E5B'; }
    else if (/(hài hước|lạc quan|vui)/.test(tNorm)) { titleColor = '#8A5A00'; descColor = '#5C430D'; }
    else if (/(nghệ thuật|sáng tạo|thẩm mỹ)/.test(tNorm)) { titleColor = '#4F3A8A'; descColor = '#40305F'; }
    else if (/(kỷ luật|thực tế|bền bỉ)/.test(tNorm)) { titleColor = '#0B6B3B'; descColor = '#1E4B35'; }
    else if (/(tự do|phiêu lưu|khám phá)/.test(tNorm)) { titleColor = '#006C66'; descColor = '#1B4A48'; }
    else if (/(nhạy bén|trực giác|phân tích)/.test(tNorm)) { titleColor = '#245596'; descColor = '#274465'; }
    ctx.fillStyle = titleColor;
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';
    const cardTitle = String(title || '').trim().toUpperCase();
    let titlePx = 18;
    let titleLines = [];
    while (titlePx >= 13) {
      const titleFont = `900 ${titlePx * scale}px ${FONT_STACK}`;
      titleLines = wrapLines(cardTitle, 244 * scale, titleFont, 2, false);
      if (titleLines.length <= 2) {
        ctx.font = titleFont;
        break;
      }
      titlePx -= 1;
    }
    titleLines.slice(0, 2).forEach((line, i) => {
      ctx.fillText(line, tx(x + 24), ty(y + 24 + i * 22));
    });
    ctx.fillStyle = descColor;
    const descFont = `500 ${14 * scale}px ${FONT_STACK}`;
    const lines = wrapLines(desc, 244 * scale, descFont, 2);
    ctx.font = descFont;
    lines.forEach((line, i) => ctx.fillText(line, tx(x + 24), ty(y + 76 + i * 24)));
  };

  // Core layout ornaments and cards from poster HTML
  let logoHorizontal = null;
  let iconTruyenDat = null; let iconHaiHuoc = null; let iconNgheThuat = null; let iconLacQuan = null; let iconNguoiSangTao = null;
  let qrCode = null; let mascotSi = null; let mascotSa = null;
  try { logoHorizontal = await loadImage('assets/saladin-xtra-logo.svg'); } catch (e) {}
  // Use font icons (glyphs) for better visual consistency and easier styling
  iconTruyenDat = '✦';
  iconHaiHuoc = '☺';
  iconNgheThuat = '♬';
  iconLacQuan = '☀';
  iconNguoiSangTao = '⚡';
  const qrParams = new URLSearchParams();
  qrParams.set('utm_source', 'direct');
  qrParams.set('utm_medium', 'social');
  qrParams.set('utm_campaign', 'share-result-2026');
  qrParams.set('id', Math.random().toString(36).slice(2, 10));
  qrParams.set('lp', String(number || 7));
  const baseOrigin = (typeof window !== 'undefined' && window.location && window.location.origin) ? window.location.origin : 'http://127.0.0.1:5173';
  const qrTarget = `${baseOrigin}/?${qrParams.toString()}`;
  try { qrCode = await loadImage(`https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=${encodeURIComponent(qrTarget)}`); } catch (e) {}
  try { mascotSi = await loadImage('assets/poster-kit/mascot-si.png'); } catch (e) {}
  try { mascotSa = await loadImage('assets/poster-kit/mascot-sa.png'); } catch (e) {}

  if (logoHorizontal) {
    const lw = 410 * scale;
    const lh = lw * (logoHorizontal.height / logoHorizontal.width);
    ctx.drawImage(logoHorizontal, tx(561) - lw / 2, ty(18), lw, lh);
  }
  rr(tx(228), ty(176), 666 * scale, 86 * scale, 44 * scale);
  ctx.strokeStyle = 'rgba(22,163,74,.68)';
  ctx.lineWidth = 1.5 * scale;
  ctx.fillStyle = 'rgba(255,255,255,.35)';
  ctx.fill(); ctx.stroke();
  ctx.fillStyle = '#087331';
  ctx.font = `900 ${46 * scale}px ${FONT_STACK}`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText('THẦN SỐ HỌC', tx(561), ty(232));

  const numberContent = (C.NUMBER_CONTENT && C.NUMBER_CONTENT[number]) || {};
  const traitSource = [numberContent.tagline, numberContent.short]
    .filter(Boolean)
    .join(',')
    .split(/[,.]/)
    .map((s) => s.trim())
    .filter(Boolean);
  const uniqueTraits = [...new Set(traitSource)].slice(0, 6);
  const traitCards = uniqueTraits.map((trait) => {
    const t = trait.toLowerCase();
    let icon = '✦';
    if (/(truyền đạt|giao tiếp|kết nối)/.test(t)) icon = '💬';
    else if (/(hài hước|lạc quan|vui)/.test(t)) icon = '☀';
    else if (/(nghệ thuật|sáng tạo|thẩm mỹ)/.test(t)) icon = '♬';
    else if (/(kỷ luật|thực tế|bền bỉ)/.test(t)) icon = '⚑';
    else if (/(tự do|phiêu lưu|khám phá)/.test(t)) icon = '➤';
    else if (/(nhạy bén|trực giác|phân tích)/.test(t)) icon = '✧';
    let cardBg = 'rgba(240,255,247,0.94)';
    if (/(truyền đạt|giao tiếp|kết nối)/.test(t)) cardBg = 'rgba(234,247,255,0.94)';
    else if (/(hài hước|lạc quan|vui)/.test(t)) cardBg = 'rgba(244,255,239,0.94)';
    else if (/(nghệ thuật|sáng tạo|thẩm mỹ)/.test(t)) cardBg = 'rgba(238,250,243,0.94)';
    else if (/(kỷ luật|thực tế|bền bỉ)/.test(t)) cardBg = 'rgba(237,251,245,0.94)';
    else if (/(tự do|phiêu lưu|khám phá)/.test(t)) cardBg = 'rgba(233,252,248,0.94)';
    else if (/(nhạy bén|trực giác|phân tích)/.test(t)) cardBg = 'rgba(236,248,255,0.94)';
    return {
      title: trait,
      desc: `Năng lượng ${trait.toLowerCase()} nổi bật trong hành trình phát triển của bạn.`,
      icon,
      cardBg,
    };
  });

  const cardLayoutsByCount = {
    1: [[32, 486]],
    2: [[32, 430], [798, 430]],
    3: [[32, 360], [32, 540], [798, 450]],
    4: [[32, 360], [32, 540], [798, 360], [798, 540]],
    5: [[32, 316], [32, 486], [32, 656], [798, 360], [798, 540]],
    6: [[32, 316], [32, 486], [32, 656], [798, 316], [798, 486], [798, 656]],
  };
  const cardLayout = cardLayoutsByCount[traitCards.length] || cardLayoutsByCount[4];
  traitCards.forEach((card, i) => {
    const pos = cardLayout[i];
    if (!pos) return;
    drawIconCard(pos[0], pos[1], 292, 154, card.title, card.desc, card.icon, card.cardBg);
  });

  // Dynamic avatar coin
  const ax = tx(561);
  const ay = ty(333);
  const ar = 44 * scale;
  if (opts.photoDataUrl) {
    try {
      const photo = await loadImage(opts.photoDataUrl);
      ctx.save();
      ctx.beginPath();
      ctx.arc(ax, ay, ar + 4 * scale, 0, Math.PI * 2);
      ctx.fillStyle = '#2FBF4A';
      ctx.fill();
      ctx.beginPath();
      ctx.arc(ax, ay, ar, 0, Math.PI * 2);
      ctx.clip();
      const ratio = photo.width / photo.height;
      let dw; let dh;
      if (ratio > 1) { dh = ar * 2; dw = dh * ratio; } else { dw = ar * 2; dh = dw / ratio; }
      ctx.drawImage(photo, ax - dw / 2, ay - dh / 2, dw, dh);
      ctx.restore();
    } catch (e) {}
  }

  const drawKnockout = (text, x, y, font, fill, stroke = 'rgba(246,251,247,0.96)', strokeWidth = 8 * scale) => {
    ctx.font = font;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.lineJoin = 'round';
    ctx.miterLimit = 2;
    ctx.strokeStyle = stroke;
    ctx.lineWidth = strokeWidth;
    ctx.strokeText(text, x, y);
    ctx.fillStyle = fill;
    ctx.fillText(text, x, y);
  };

  // Main number
  ctx.shadowColor = 'rgba(20,197,96,0.35)';
  ctx.shadowBlur = 14 * scale;
  drawKnockout(String(number), tx(561), ty(586), `900 ${387 * scale}px ${FONT_STACK}`, '#18A63E', 'rgba(246,251,247,0.95)', 12 * scale);
  ctx.shadowBlur = 0;

  // Name
  drawKnockout(safeName, tx(561), ty(782), `900 ${68 * scale}px ${FONT_STACK}`, '#0C2415', 'rgba(246,251,247,0.96)', 8 * scale);

  // Role pill text
  const numberName = (N.NUMBER_NAMES && N.NUMBER_NAMES[number]) || 'Người sáng tạo';
  const tagline = (N.NUMBER_TAGLINES && N.NUMBER_TAGLINES[number]) || 'Truyền đạt, hài hước, lạc quan, nghệ thuật.';
  const mainInsight = uniqueTraits[0] ? `${uniqueTraits[0]} là năng lượng nổi bật của bạn` : tagline;
  drawKnockout(`ĐƯỜNG ĐỜI  •  ${String(numberName).toUpperCase()}`, tx(561), ty(852), `800 ${20 * scale}px ${FONT_STACK}`, '#00692E', 'rgba(246,251,247,0.95)', 5 * scale);
  drawKnockout(mainInsight, tx(561), ty(892), `italic 350 ${22 * scale}px "Be Vietnam Pro", "Noto Sans", -apple-system, sans-serif`, '#0D6130', 'rgba(233,247,236,0.98)', 4 * scale);

  // Related numbers section (match HTML structure)
  const pickRelatedValue = (v, fallback = '-') => {
    if (v === null || v === undefined || v === '') return fallback;
    return String(v);
  };
  const related = [
    ['Đường đời', pickRelatedValue(nums.lifePath, String(number))],
    ['Linh hồn', pickRelatedValue(nums.soulUrge)],
    ['Nhân cách', pickRelatedValue(nums.personality)],
    ['Vận mệnh', pickRelatedValue(nums.destiny)],
    ['Năm cá nhân', pickRelatedValue(nums.personalYear)],
  ];
  const relatedNumberColor = (v) => {
    const n = Number(String(v).split('/')[0]);
    const map = {
      1: '#0A63C9', 2: '#00A884', 3: '#D38B00', 4: '#2C8E5A', 5: '#1E63B4',
      6: '#8E5BBE', 7: '#0F7B6C', 8: '#2563A6', 9: '#B56900',
      11: '#5E6AD2', 22: '#2A8D73', 33: '#8B5BAF',
    };
    return map[n] || '#0A8532';
  };
  const statsX = tx(102);
  const statsY = ty(941);
  const statsW = 918 * scale;
  const statsH = 130 * scale;
  const colW = statsW / 5;
  rr(statsX, statsY, statsW, statsH, 18 * scale);
  ctx.fillStyle = 'rgba(255,255,255,0.86)';
  ctx.strokeStyle = 'rgba(45, 165, 64, .30)';
  ctx.lineWidth = 1 * scale;
  ctx.fill();
  ctx.stroke();
  // section label pill
  rr(tx(430), ty(914), 262 * scale, 42 * scale, 22 * scale);
  const labelGrad = ctx.createLinearGradient(tx(430), ty(914), tx(692), ty(956));
  labelGrad.addColorStop(0, '#43c64d');
  labelGrad.addColorStop(1, '#079336');
  ctx.fillStyle = labelGrad;
  ctx.fill();
  ctx.fillStyle = '#FFFFFF';
  ctx.font = `800 ${20 * scale}px ${FONT_STACK}`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText('BỘ SỐ LIÊN QUAN', tx(561), ty(935));

  related.forEach((row, i) => {
    const cx = statsX + colW * i + colW / 2;
    if (i < 4) {
      ctx.strokeStyle = 'rgba(42, 159, 61, .20)';
      ctx.lineWidth = 1 * scale;
      ctx.beginPath();
      ctx.moveTo(statsX + colW * (i + 1), statsY + 22 * scale);
      ctx.lineTo(statsX + colW * (i + 1), statsY + statsH - 20 * scale);
      ctx.stroke();
    }
    ctx.fillStyle = '#213329';
    ctx.font = `600 ${13 * scale}px ${FONT_STACK}`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(row[0], cx, statsY + 44 * scale);
    drawKnockout(row[1], cx, statsY + 88 * scale, `900 ${44 * scale}px ${FONT_STACK}`, relatedNumberColor(row[1]), 'rgba(255,255,255,0.95)', 5 * scale);
  });

  // Famous profiles row
  const famousNames = famousRaw.split(',').map((s) => s.trim()).filter(Boolean).slice(0, 2);
  const displayFamous = famousNames.length ? famousNames : ['Charlie Chaplin', 'David Bowie'];
  ctx.fillStyle = '#0E5F2F';
  ctx.font = `800 ${20 * scale}px ${FONT_STACK}`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText('Người nổi tiếng cùng thần số', tx(561), ty(1112));

  const drawFamousProfile = (name, cx, cy) => {
    const meta = FAMOUS_META[name] || { field: 'Lĩnh vực', success: 'Dấu ấn nổi bật quốc tế' };
    const r = 28 * scale;
    const initials = name.split(' ').filter(Boolean).slice(-2).map((w) => w[0]).join('').toUpperCase();
    ctx.beginPath();
    ctx.arc(cx - 136 * scale, cy, r, 0, Math.PI * 2);
    ctx.fillStyle = '#D9F5E4';
    ctx.fill();
    ctx.fillStyle = '#0B7C31';
    ctx.font = `800 ${16 * scale}px ${FONT_STACK}`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(initials, cx - 136 * scale, cy + 1 * scale);

    ctx.textAlign = 'left';
    ctx.fillStyle = '#10351D';
    ctx.font = `700 ${16 * scale}px ${FONT_STACK}`;
    ctx.fillText(name, cx - 98 * scale, cy - 14 * scale);
    ctx.fillStyle = '#1F5A3C';
    ctx.font = `600 ${13 * scale}px ${FONT_STACK}`;
    ctx.fillText(meta.field, cx - 98 * scale, cy + 6 * scale);
    ctx.fillStyle = '#2E4E3A';
    ctx.font = `500 ${12 * scale}px ${FONT_STACK}`;
    const line = wrapLines(meta.success, 220 * scale, `500 ${12 * scale}px ${FONT_STACK}`, 1)[0] || meta.success;
    ctx.fillText(line, cx - 98 * scale, cy + 24 * scale);
  };
  drawFamousProfile(displayFamous[0], tx(400), ty(1152));
  drawFamousProfile(displayFamous[1], tx(722), ty(1152));

  // Bottom CTA block
  rr(tx(22), ty(1208), 1078 * scale, 180 * scale, 18 * scale);
  ctx.fillStyle = 'linear-gradient(90deg, rgba(255,255,255,.92), rgba(239,250,235,.88))';
  ctx.fillStyle = 'rgba(255,255,255,0.9)';
  ctx.strokeStyle = 'rgba(45, 165, 64, .28)';
  ctx.lineWidth = 1 * scale;
  ctx.fill(); ctx.stroke();
  rr(tx(22), ty(1208), 210 * scale, 180 * scale, 18 * scale);
  const g = ctx.createLinearGradient(tx(22), ty(1208), tx(232), ty(1388));
  g.addColorStop(0, '#21bd4a'); g.addColorStop(1, '#087633');
  ctx.fillStyle = g; ctx.fill();
  if (qrCode) ctx.drawImage(qrCode, tx(56), ty(1236), 132 * scale, 132 * scale);
  ctx.fillStyle = '#06451f';
  ctx.textAlign = 'left';
  ctx.font = `900 ${34 * scale}px ${FONT_STACK}`;
  ctx.fillText('KHÁM PHÁ THẦN SỐ HỌC CỦA BẠN', tx(255), ty(1268));
  ctx.fillStyle = '#1c2d24';
  ctx.font = `500 ${17 * scale}px ${FONT_STACK}`;
  ctx.fillText('Hiểu mình – Phát huy thế mạnh – Sống trọn tiềm năng', tx(255), ty(1306));
  ctx.fillStyle = '#087331';
  ctx.font = `900 ${30 * scale}px ${FONT_STACK}`;
  ctx.fillText('thansohoc.saladin.vn  ›', tx(255), ty(1352));
  if (mascotSa) ctx.drawImage(mascotSa, tx(895), ty(1220), 92 * scale, 168 * scale);
  if (mascotSi) ctx.drawImage(mascotSi, tx(978), ty(1220), 98 * scale, 168 * scale);

  return canvas;
}

// Helper: wrap text to multiple lines
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
  const words = text.split(' ');
  let line = '';
  let yy = y;
  for (let i = 0; i < words.length; i++) {
    const test = line + words[i] + ' ';
    const metrics = ctx.measureText(test);
    if (metrics.width > maxWidth && i > 0) {
      ctx.fillText(line.trim(), x, yy);
      line = words[i] + ' ';
      yy += lineHeight;
    } else {
      line = test;
    }
  }
  ctx.fillText(line.trim(), x, yy);
}

// ─────────────────────────────────────────────────────────────
// React component — Poster Generator screen
// ─────────────────────────────────────────────────────────────
function PosterScreen({ mobile, user, onBack }) {
  const N = window.SaladinNumerology || {};
  const u = user || { name: 'Nguyễn Hoài An', dob: '1993-05-16' };
  const numbers = u.numbers || (N.calculateAll && N.calculateAll(u)) || { lifePath: 7 };

  const [format, setFormat] = React.useState('square');
  const [style, setStyle] = React.useState('modern');
  const [photoDataUrl, setPhotoDataUrl] = React.useState(null);
  const [photoError, setPhotoError] = React.useState(null);
  const [showDob, setShowDob] = React.useState(false);
  const [rendering, setRendering] = React.useState(false);

  const previewRef = React.useRef(null);
  const fileInputRef = React.useRef(null);
  const renderToken = React.useRef(0);

  // Re-render preview when settings change
  React.useEffect(() => {
    let cancelled = false;
    const token = ++renderToken.current;
    setRendering(true);
    const render = async () => {
      try {
        const canvas = await renderPoster({
          format, style,
          lifePath: numbers.lifePath,
          numbers,
          userName: u.name || '',
          userDob: u.dob || '',
          userGender: u.gender || '',
          photoDataUrl,
          showDob,
        });
        if (cancelled || token !== renderToken.current) return;
        const preview = previewRef.current;
        if (preview) {
          const ctx = preview.getContext('2d');
          preview.width = canvas.width;
          preview.height = canvas.height;
          ctx.drawImage(canvas, 0, 0);
        }
      } finally {
        if (!cancelled) setRendering(false);
      }
    };
    render();
    return () => { cancelled = true; };
  }, [format, style, photoDataUrl, showDob, numbers.lifePath, u.name]);

  // Photo upload handler
  const handlePhotoSelect = async (file) => {
    setPhotoError(null);
    if (!file) return;
    if (!ALLOWED_PHOTO_TYPES.includes(file.type)) {
      setPhotoError('Chỉ hỗ trợ JPG, PNG, WEBP');
      return;
    }
    if (file.size > MAX_PHOTO_BYTES) {
      setPhotoError('Ảnh quá lớn — tối đa 5MB');
      return;
    }
    try {
      const dataUrl = await fileToDataUrl(file);
      const resized = await resizePhoto(dataUrl, 800);
      setPhotoDataUrl(resized);
      if (N.trackEvent) N.trackEvent('photo_uploaded', { size_kb: Math.round(file.size / 1024) });
    } catch (e) {
      setPhotoError('Không đọc được ảnh — thử ảnh khác');
    }
  };

  // Download poster
  const handleDownload = async () => {
    const canvas = await renderPoster({
      format, style,
      lifePath: numbers.lifePath,
      numbers,
      userName: u.name || '',
      userDob: u.dob || '',
      userGender: u.gender || '',
      photoDataUrl,
      showDob,
    });
    canvas.toBlob((blob) => {
      if (!blob) return;
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `saladin-than-so-LifePath${numbers.lifePath}-${Date.now()}.png`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      if (N.trackEvent) N.trackEvent('poster_downloaded', {
        format, life_path: numbers.lifePath, has_photo: !!photoDataUrl,
      });
    }, 'image/png', 0.95);
  };

  const fmt = POSTER_FORMATS.find(f => f.key === format);

  return (
    <div style={{ background: 'var(--bg)', minHeight: '100%' }}>
      <Container style={{ paddingTop: mobile ? 16 : 32, paddingBottom: mobile ? 80 : 64 }}>
        {/* Back */}
        <button
          type="button" onClick={onBack}
          style={{
            background: 'transparent', border: 'none', cursor: 'pointer',
            display: 'inline-flex', alignItems: 'center', gap: 6,
            fontSize: 13, fontWeight: 500, color: 'var(--text-support)',
            padding: 0, marginBottom: mobile ? 16 : 24,
          }}
        >
          <i className="ri-arrow-left-line" style={{ fontSize: 16 }}></i>
          Quay lại kết quả
        </button>

        <div style={{ marginBottom: mobile ? 20 : 28 }}>
          <h1 style={{
            fontFamily: 'var(--font-display)', fontWeight: 700,
            fontSize: mobile ? 24 : 32, lineHeight: 1.15,
            margin: 0, marginBottom: 6,
            color: 'var(--text-default)', letterSpacing: '-0.02em',
          }}>
            Tạo poster cá nhân hoá
          </h1>
          <p style={{ fontSize: mobile ? 14 : 15, color: 'var(--text-support)', margin: 0 }}>
            Khoe Life Path {numbers.lifePath} của bạn lên social trong 30 giây.
          </p>
        </div>

        <div style={{
          display: 'grid',
          gridTemplateColumns: mobile ? '1fr' : '380px 1fr',
          gap: mobile ? 16 : 32,
          alignItems: 'flex-start',
        }}>
          {/* LEFT — Control panel */}
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 18,
            background: 'var(--bg-variant)', padding: mobile ? 16 : 20,
            borderRadius: 12,
            border: '1px solid var(--border-default)',
          }}>
            {/* Photo upload */}
            <ControlSection title="1. Tải ảnh đại diện" optional>
              <div
                onClick={() => fileInputRef.current && fileInputRef.current.click()}
                onDragOver={(e) => { e.preventDefault(); }}
                onDrop={(e) => {
                  e.preventDefault();
                  const file = e.dataTransfer.files[0];
                  if (file) handlePhotoSelect(file);
                }}
                style={{
                  border: `2px dashed ${photoDataUrl ? '#14C560' : 'var(--border-default)'}`,
                  borderRadius: 10, padding: 16,
                  cursor: 'pointer', textAlign: 'center',
                  transition: 'border-color 150ms',
                  background: 'var(--bg)',
                  display: 'flex', alignItems: 'center', gap: 12,
                }}
              >
                {photoDataUrl ? (
                  <>
                    <img src={photoDataUrl} alt="" style={{
                      width: 64, height: 64, borderRadius: '50%',
                      objectFit: 'cover', border: '3px solid #14C560',
                    }}/>
                    <div style={{ flex: 1, textAlign: 'left' }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-default)' }}>Đã chọn ảnh</div>
                      <div style={{ display: 'flex', gap: 12, marginTop: 4 }}>
                        <button type="button" onClick={(e) => { e.stopPropagation(); fileInputRef.current && fileInputRef.current.click(); }} style={btnLinkStyle}>Đổi ảnh</button>
                        <button type="button" onClick={(e) => { e.stopPropagation(); setPhotoDataUrl(null); }} style={btnLinkStyle}>Xoá</button>
                      </div>
                    </div>
                  </>
                ) : (
                  <>
                    <div style={{
                      width: 48, height: 48, borderRadius: '50%',
                      background: 'var(--primary-95)',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                    }}>
                      <i className="ri-upload-cloud-2-line" style={{ fontSize: 22, color: '#005323' }}></i>
                    </div>
                    <div style={{ flex: 1, textAlign: 'left' }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-default)' }}>Kéo thả hoặc click chọn ảnh</div>
                      <div style={{ fontSize: 11, color: 'var(--text-support)', marginTop: 2 }}>JPG / PNG / WEBP · tối đa 5MB</div>
                    </div>
                  </>
                )}
              </div>
              <input
                ref={fileInputRef}
                type="file" accept="image/*" style={{ display: 'none' }}
                onChange={(e) => handlePhotoSelect(e.target.files[0])}
              />
              {photoError && (
                <div style={{ fontSize: 12, color: '#BA1A1A', marginTop: 6, display: 'flex', alignItems: 'center', gap: 4 }}>
                  <i className="ri-error-warning-fill" style={{ fontSize: 14 }}></i> {photoError}
                </div>
              )}
              <div style={{
                fontSize: 11, color: 'var(--text-support)', marginTop: 6,
                display: 'flex', alignItems: 'center', gap: 4,
              }}>
                <i className="ri-shield-check-line" style={{ fontSize: 12, color: '#14C560' }}></i>
                Browser-only — Saladin không lưu ảnh của bạn
              </div>
            </ControlSection>

            {/* Format selector */}
            <ControlSection title="2. Chọn định dạng">
              <div style={{
                display: 'flex', flexWrap: 'wrap', gap: 6,
              }}>
                {POSTER_FORMATS.map(f => (
                  <button
                    key={f.key}
                    type="button"
                    onClick={() => { setFormat(f.key); if (N.trackEvent) N.trackEvent('poster_format_selected', { format: f.key }); }}
                    style={{
                      padding: '8px 14px', borderRadius: 50,
                      border: `1px solid ${format === f.key ? '#14C560' : 'var(--border-default)'}`,
                      background: format === f.key ? 'var(--primary-95)' : 'var(--bg)',
                      color: format === f.key ? '#005323' : 'var(--text-default)',
                      fontSize: 12, fontWeight: 600, cursor: 'pointer',
                      fontFamily: 'inherit',
                      display: 'inline-flex', alignItems: 'center', gap: 6,
                      transition: 'all 150ms',
                    }}
                  >
                    <i className={f.icon} style={{ fontSize: 14 }}></i>
                    {f.label}
                  </button>
                ))}
              </div>
              <div style={{ fontSize: 11, color: 'var(--text-support)', marginTop: 6 }}>
                {fmt && `${fmt.ratio} · ${fmt.w}×${fmt.h}px`}
              </div>
            </ControlSection>

            {/* Style toggles */}
            <ControlSection title="3. Tinh chỉnh">
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                <Toggle label="Hiển thị ngày sinh" value={showDob} onChange={setShowDob} />
              </div>
            </ControlSection>

            {/* Actions */}
            <div style={{
              display: 'flex', gap: 8, marginTop: 4,
              flexDirection: mobile ? 'column' : 'row',
            }}>
              <button
                type="button"
                onClick={handleDownload}
                disabled={rendering}
                style={{
                  flex: 1,
                  background: '#14C560', color: '#fff',
                  padding: '12px 18px', borderRadius: 4, border: 'none',
                  fontFamily: 'inherit', fontSize: 14, fontWeight: 600, cursor: 'pointer',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
                  boxShadow: '0 4px 12px -2px rgba(20,197,96,0.35)',
                  opacity: rendering ? 0.7 : 1,
                }}
              >
                <i className="ri-download-2-line" style={{ fontSize: 16 }}></i>
                Tải poster
              </button>
            </div>
          </div>

          {/* RIGHT — Preview */}
          <div style={{
            display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
            position: 'relative',
            minHeight: mobile ? 320 : 540,
          }}>
            <div style={{
              maxWidth: '100%',
              width: fmt && fmt.w >= fmt.h ? '100%' : 'auto',
              maxHeight: mobile ? '62vh' : 720,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              borderRadius: 12, overflow: 'hidden',
              border: '1px solid var(--border-default)',
              background: 'var(--bg-variant)',
              boxShadow: 'var(--shadow-md)',
            }}>
              <canvas
                ref={previewRef}
                style={{
                  maxWidth: '100%', maxHeight: mobile ? '60vh' : 700,
                  display: 'block', objectFit: 'contain',
                }}
              />
            </div>
            {rendering && (
              <div style={{
                position: 'absolute', top: 12, right: 12,
                display: 'flex', alignItems: 'center', gap: 6,
                padding: '6px 12px', background: 'var(--bg)',
                border: '1px solid var(--border-default)',
                borderRadius: 50, fontSize: 12, color: 'var(--text-support)',
                boxShadow: 'var(--shadow-sm)',
              }}>
                <i className="ri-loader-4-line" style={{ animation: 'spin 800ms linear infinite', fontSize: 14 }}></i>
                Đang vẽ…
              </div>
            )}
          </div>
        </div>
      </Container>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Sub-components
// ─────────────────────────────────────────────────────────────
function ControlSection({ title, optional, children }) {
  return (
    <div>
      <div style={{
        display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 8,
      }}>
        <h3 style={{
          fontSize: 12, fontWeight: 700,
          margin: 0, color: 'var(--text-default)',
          textTransform: 'uppercase', letterSpacing: '0.06em',
        }}>{title}</h3>
        {optional && <span style={{ fontSize: 11, color: 'var(--text-support)' }}>(tuỳ chọn)</span>}
      </div>
      {children}
    </div>
  );
}

function Toggle({ label, value, onChange }) {
  return (
    <label style={{
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      cursor: 'pointer', userSelect: 'none',
    }}>
      <span style={{ fontSize: 13, color: 'var(--text-default)' }}>{label}</span>
      <button
        type="button"
        onClick={() => onChange(!value)}
        aria-pressed={value}
        style={{
          width: 36, height: 20, borderRadius: 50,
          border: 'none', cursor: 'pointer',
          background: value ? '#14C560' : 'var(--border-strong, #cfd3d8)',
          padding: 0, position: 'relative',
          transition: 'background 150ms',
        }}
      >
        <span style={{
          position: 'absolute', top: 2,
          left: value ? 18 : 2,
          width: 16, height: 16, borderRadius: '50%',
          background: '#fff', transition: 'left 150ms',
          boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
        }}/>
      </button>
    </label>
  );
}

const btnLinkStyle = {
  background: 'transparent', border: 'none', padding: 0,
  fontSize: 12, color: '#14C560', cursor: 'pointer',
  fontFamily: 'inherit', fontWeight: 600,
};

Object.assign(window, { PosterScreen, renderPoster, POSTER_FORMATS });
