// 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 style = POSTER_STYLES.find(s => s.key === opts.style) || POSTER_STYLES[0];
  const N = window.SaladinNumerology || {};

  const W = fmt.w;
  const H = fmt.h;
  const canvas = document.createElement('canvas');
  canvas.width = W; canvas.height = H;
  const ctx = canvas.getContext('2d');

  // 1. Background gradient
  const grad = ctx.createLinearGradient(0, 0, W, H);
  const dark = '#0A1530';
  const mid = '#1E3888';
  grad.addColorStop(0, mid);
  grad.addColorStop(0.6, '#152968');
  grad.addColorStop(1, dark);
  ctx.fillStyle = grad;
  ctx.fillRect(0, 0, W, H);

  // 2. Pythagorean 3x3 grid pattern (subtle white)
  const gridSize = Math.min(W, H) * 0.42;
  const gx = W * 0.5 - gridSize / 2;
  const gy = H * 0.5 - gridSize / 2;
  ctx.strokeStyle = 'rgba(255,255,255,0.045)';
  ctx.lineWidth = Math.max(1, W / 600);
  for (let i = 0; i <= 3; i++) {
    const t = i / 3;
    // vertical
    ctx.beginPath();
    ctx.moveTo(gx + gridSize * t, gy);
    ctx.lineTo(gx + gridSize * t, gy + gridSize);
    ctx.stroke();
    // horizontal
    ctx.beginPath();
    ctx.moveTo(gx, gy + gridSize * t);
    ctx.lineTo(gx + gridSize, gy + gridSize * t);
    ctx.stroke();
  }

  // 3. Top brand row
  const padX = Math.round(W * 0.06);
  const padY = Math.round(H * 0.05);
  const brandFontSize = Math.max(14, Math.round(W * 0.022));
  ctx.fillStyle = 'rgba(255,255,255,0.92)';
  ctx.font = `600 ${brandFontSize}px -apple-system, "Noto Sans", sans-serif`;
  ctx.textBaseline = 'top';
  ctx.textAlign = 'left';
  // Brand dot (green) + text
  const dotSize = brandFontSize * 0.7;
  ctx.fillStyle = '#14C560';
  ctx.beginPath();
  ctx.arc(padX + dotSize / 2, padY + brandFontSize / 2, dotSize / 2, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = 'rgba(255,255,255,0.92)';
  ctx.fillText('Saladin Thần số', padX + dotSize + 8, padY);

  // 4. BIG NUMBER center
  const number = opts.lifePath != null ? opts.lifePath : 7;
  const numberStr = String(number);
  const isMaster = N.NUMBER_NAMES && (number === 11 || number === 22 || number === 33);
  const numColor = isMaster ? '#FFD75A' : '#14C560';

  // Compute font size — different for each format
  let bigFontSize;
  if (fmt.key === 'square') bigFontSize = W * 0.42;
  else if (fmt.key === 'story') bigFontSize = W * 0.52;
  else if (fmt.key === 'zalo') bigFontSize = W * 0.46;
  else bigFontSize = H * 0.55; // landscape

  ctx.font = `900 ${Math.round(bigFontSize)}px -apple-system, "SF Pro Display", "Noto Sans", sans-serif`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';

  // Shadow for depth
  ctx.shadowColor = 'rgba(20,197,96,0.35)';
  ctx.shadowBlur = bigFontSize * 0.15;
  ctx.shadowOffsetY = bigFontSize * 0.02;

  // Position varies per format
  let numY;
  if (fmt.key === 'square')    numY = H * 0.46;
  else if (fmt.key === 'story') numY = H * 0.38;
  else if (fmt.key === 'zalo')  numY = H * 0.42;
  else numY = H * 0.5; // landscape

  ctx.fillStyle = numColor;
  ctx.fillText(numberStr, W / 2, numY);
  ctx.shadowBlur = 0;
  ctx.shadowOffsetY = 0;

  // 5. Avatar circle (if photo provided)
  if (opts.photoDataUrl) {
    try {
      const photoImg = await loadImage(opts.photoDataUrl);
      const avatarSize = Math.round(Math.min(W, H) * 0.16);
      let ax, ay;
      if (fmt.key === 'square') { ax = W / 2; ay = H * 0.18; }
      else if (fmt.key === 'story') { ax = W / 2; ay = H * 0.14; }
      else if (fmt.key === 'zalo')  { ax = W / 2; ay = H * 0.14; }
      else { ax = W * 0.18; ay = H * 0.5; }

      // White ring background
      ctx.save();
      ctx.beginPath();
      ctx.arc(ax, ay, avatarSize / 2 + 6, 0, Math.PI * 2);
      ctx.fillStyle = '#14C560';
      ctx.fill();

      ctx.beginPath();
      ctx.arc(ax, ay, avatarSize / 2, 0, Math.PI * 2);
      ctx.closePath();
      ctx.clip();
      // Draw photo cover-fit
      const ar = photoImg.width / photoImg.height;
      let dw, dh;
      if (ar > 1) { dh = avatarSize; dw = dh * ar; }
      else { dw = avatarSize; dh = dw / ar; }
      ctx.drawImage(photoImg, ax - dw / 2, ay - dh / 2, dw, dh);
      ctx.restore();
    } catch (e) {
      // photo fail — silently skip
      console.warn('[poster] photo render failed', e);
    }
  } else if (opts.userName) {
    // Initial avatar fallback
    const initial = opts.userName.trim().split(/\s+/).slice(-1)[0][0] || '?';
    const avatarSize = Math.round(Math.min(W, H) * 0.12);
    let ax, ay;
    if (fmt.key === 'square') { ax = W / 2; ay = H * 0.18; }
    else if (fmt.key === 'story') { ax = W / 2; ay = H * 0.14; }
    else if (fmt.key === 'zalo')  { ax = W / 2; ay = H * 0.14; }
    else { ax = W * 0.18; ay = H * 0.5; }

    ctx.fillStyle = '#14C560';
    ctx.beginPath();
    ctx.arc(ax, ay, avatarSize / 2, 0, Math.PI * 2);
    ctx.fill();

    ctx.fillStyle = '#fff';
    ctx.font = `700 ${Math.round(avatarSize * 0.5)}px -apple-system, "Noto Sans", sans-serif`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(initial.toUpperCase(), ax, ay);
  }

  // 6. Name (subtitle)
  if (opts.showName !== false && opts.userName) {
    const nameFontSize = Math.max(20, Math.round(W * 0.04));
    ctx.font = `600 ${nameFontSize}px -apple-system, "SF Pro Display", "Noto Sans", sans-serif`;
    ctx.fillStyle = 'rgba(255,255,255,0.96)';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'top';

    let ny;
    if (fmt.key === 'square') ny = H * 0.72;
    else if (fmt.key === 'story') ny = H * 0.68;
    else if (fmt.key === 'zalo') ny = H * 0.70;
    else ny = H * 0.20;

    if (fmt.key === 'landscape') {
      ctx.textAlign = 'left';
      ctx.fillText(opts.userName, W * 0.38, ny);
    } else {
      ctx.fillText(opts.userName, W / 2, ny);
    }
  }

  // 7. Label
  const numberName = (N.NUMBER_NAMES && N.NUMBER_NAMES[number]) || 'Người Tìm Tòi';
  const labelFontSize = Math.max(14, Math.round(W * 0.022));
  ctx.font = `500 ${labelFontSize}px -apple-system, "Noto Sans", sans-serif`;
  ctx.fillStyle = '#14C560';
  ctx.textBaseline = 'top';
  let ly;
  if (fmt.key === 'square')    ly = H * 0.78;
  else if (fmt.key === 'story') ly = H * 0.74;
  else if (fmt.key === 'zalo')  ly = H * 0.76;
  else ly = H * 0.32;

  const labelText = `SỐ ĐƯỜNG ĐỜI · ${numberName.toUpperCase()}`;
  if (fmt.key === 'landscape') {
    ctx.textAlign = 'left';
    ctx.fillText(labelText, W * 0.38, ly);
  } else {
    ctx.textAlign = 'center';
    ctx.fillText(labelText, W / 2, ly);
  }

  // 8. One-liner tagline (italic)
  const tagline = (N.NUMBER_TAGLINES && N.NUMBER_TAGLINES[number]) || 'Trí tuệ, phân tích, tìm tòi.';
  const tagFontSize = Math.max(14, Math.round(W * 0.024));
  ctx.font = `italic 400 ${tagFontSize}px -apple-system, "Noto Sans", sans-serif`;
  ctx.fillStyle = 'rgba(255,255,255,0.78)';
  let ty;
  if (fmt.key === 'square')    ty = H * 0.83;
  else if (fmt.key === 'story') ty = H * 0.79;
  else if (fmt.key === 'zalo')  ty = H * 0.81;
  else ty = H * 0.42;

  if (fmt.key === 'landscape') {
    ctx.textAlign = 'left';
    wrapText(ctx, tagline, W * 0.38, ty, W * 0.55, tagFontSize * 1.4);
  } else {
    ctx.textAlign = 'center';
    wrapText(ctx, tagline, W / 2, ty, W * 0.85, tagFontSize * 1.4);
  }

  // 9. CTA + watermark bottom
  const ctaFontSize = Math.max(12, Math.round(W * 0.018));
  ctx.font = `600 ${ctaFontSize}px -apple-system, "Noto Sans", sans-serif`;
  ctx.fillStyle = 'rgba(255,255,255,0.55)';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'bottom';
  if (fmt.key === 'landscape') {
    ctx.textAlign = 'right';
    ctx.fillText('saladin.vn/than-so', W - padX, H - padY);
  } else {
    ctx.fillText('Khám phá con số của bạn → saladin.vn/than-so', W / 2, H - padY);
  }

  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 [showName, setShowName] = React.useState(true);
  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,
          userName: showName ? u.name : '',
          photoDataUrl,
          showName, 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, showName, 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,
      userName: showName ? u.name : '',
      photoDataUrl,
      showName, 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ị tên" value={showName} onChange={setShowName} />
                <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 ? '60vh' : 720,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              borderRadius: 12, overflow: 'hidden',
              border: '1px solid var(--border-default)',
              background: '#000',
              boxShadow: 'var(--shadow-md)',
            }}>
              <canvas
                ref={previewRef}
                style={{
                  maxWidth: '100%', maxHeight: mobile ? '58vh' : 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 });
