/* ============================================================
   invite-shared.jsx — Dữ liệu + khối dùng chung cho 3 phương án
   Xuất ra window ở cuối file để các file khác dùng được.
   ============================================================ */

// ---------- NỘI DUNG THIỆP (sửa tại đây) ----------
const WED = {
  groom: 'Phi Vũ',
  bride: 'Hồng Sương',
  groomFull: 'Nguyễn Phi Vũ',
  brideFull: 'Nguyễn Thị Hồng Sương',
  groomParents: ['Ông Nguyễn Hoàng Dũng', 'Bà Nguyễn Thị Dẫn'],
  brideParents: ['Ông Nguyễn Văn Thưởng', 'Bà Đặng Thị Hường'],
  groomAddress: 'Tổ 13, Ấp Bầu 2, X. Bình Mỹ, TP. HCM',
  brideAddress: 'Tổ 13, Ấp Bến 2, X. Trừ Văn Thố, TP. HCM',
  monogram: 'V & S',
  dateISO: '2026-07-05T10:30:00+07:00',
  dateText: 'Chủ Nhật · 05 . 07 . 2026',
  dateLong: 'Chủ Nhật, ngày 05 tháng 07 năm 2026 (nhằm 21/05 Bính Ngọ)',
  weekday: 'Chủ Nhật',
  day: '05', month: '07', year: '2026',
  ceremony: { label: 'Lễ Tân Hôn',  time: '10:30', note: 'Tại tư gia nhà trai' },
  party:    { label: 'Tiệc Mừng',   time: '11:30', note: 'Đón khách 11:30 · Nhập tiệc 12:00' },
  venue: 'Hoa Viên Vườn Mai',
  venueFull: 'Hoa Viên Vườn Mai',
  hall: '16A Đường 189, Ấp 3, X. Bình Mỹ',
  address: 'Huyện Củ Chi, TP. Hồ Chí Minh',
  mapsLink: 'https://www.google.com/maps/search/?api=1&query=Hoa+Vien+Vuon+Mai+16A+Duong+189+Ap+3+Binh+My+Cu+Chi',
  mapsEmbed: 'https://www.google.com/maps?q=Hoa%20Vien%20Vuon%20Mai%2016A%20Duong%20189%20Ap%203%20Binh%20My%20Cu%20Chi&output=embed',
  invite: 'Trân trọng kính mời quý vị cùng gia đình đến chung vui trong ngày trọng đại của chúng tôi. Sự hiện diện của quý vị là niềm vinh hạnh cho gia đình chúng tôi.',
  quote: 'Yêu nhau không phải là nhìn nhau, mà là cùng nhìn về một hướng.',
  quoteAuthor: 'Antoine de Saint-Exupéry',
  closing: 'Hẹn gặp quý vị trong ngày hạnh phúc của chúng tôi!',
  // Nhạc nền: dán URL trực tiếp (mp3/m4a/ogg). Để rỗng nếu không muốn.
  musicUrl: 'music/background.mp3',
};

// ============================================================
// Reveal-on-scroll — ENGINE TWEEN BẰNG JAVASCRIPT
// Ghi thẳng opacity + translateY từng khung hình (rAF) thay vì dựa
// vào CSS animation (vốn hay bị "đóng băng" trong iframe xem trước).
// Vừa mờ dần vừa trượt lên thật mượt trên mọi trình duyệt, kèm cơ
// chế an toàn: chắc chắn hiện rõ sau thời gian tối đa dù có lỗi gì.
// ============================================================
const RevealCtx = React.createContext(null);

const _reduceMotion = typeof window !== 'undefined' &&
  window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

const _RISE = 26;          // px trượt lên ban đầu
const _DUR = 820;          // thời lượng tween (ms)
const _SAFETY = 2200;      // dự phòng: hiện rõ sau ngần này dù chưa cuộn tới

// easeOutCubic — chậm dần ở cuối, cảm giác mềm "đặt xuống"
const _easeOut = (t) => 1 - Math.pow(1 - t, 3);

// Một engine dùng chung: gom các phần tử chờ hiện, mỗi lần cuộn/khung
// hình kiểm tra phần tử nào lọt ngưỡng thì khởi động tween riêng cho nó.
const _revealRoots = new Map(); // rootEl -> { pending:Set }
function _ensureRoot(root) {
  if (_revealRoots.has(root)) return _revealRoots.get(root);
  const entry = { pending: new Set() };

  // Khởi động tween mờ-dần + trượt-lên cho 1 phần tử
  const animate = (rec) => {
    if (rec.started) return;
    rec.started = true;
    entry.pending.delete(rec.el);
    const el = rec.el;
    if (_reduceMotion) { el.style.opacity = '1'; el.style.transform = 'none'; return; }
    const begin = (ts) => {
      const start = ts + rec.delay;
      const frame = (now) => {
        const p = Math.min(1, Math.max(0, (now - start) / _DUR));
        const e = _easeOut(p);
        el.style.opacity = String(e);
        el.style.transform = 'translate3d(0,' + (_RISE * (1 - e)).toFixed(2) + 'px,0)';
        if (p < 1) requestAnimationFrame(frame);
        else { el.style.opacity = '1'; el.style.transform = 'none'; el.style.willChange = 'auto'; }
      };
      requestAnimationFrame(frame);
    };
    requestAnimationFrame(begin);
  };

  entry.check = () => {
    if (!entry.pending.size) return;
    const rb = root ? root.getBoundingClientRect() : { top: 0, bottom: window.innerHeight };
    const limit = rb.top + (rb.bottom - rb.top) * 0.92;
    entry.pending.forEach((el) => {
      if (el.getBoundingClientRect().top < limit) animate(el.__reveal);
    });
  };
  entry.animate = animate;

  (root || window).addEventListener('scroll', entry.check, { passive: true });
  window.addEventListener('resize', entry.check);
  _revealRoots.set(root, entry);
  return entry;
}

function useReveal(delay = 0) {
  const ref = React.useRef(null);
  const root = React.useContext(RevealCtx);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const entry = _ensureRoot(root);
    const rec = { el, delay, started: false };
    el.__reveal = rec;
    // trạng thái ban đầu: ẩn + lùi xuống (đặt qua JS để chắc chắn áp dụng)
    if (!_reduceMotion) { el.style.opacity = '0'; el.style.transform = 'translate3d(0,' + _RISE + 'px,0)'; el.style.willChange = 'opacity, transform'; }
    entry.pending.add(el);
    // kiểm tra vài khung hình đầu để hiện ngay phần đã nằm trong màn hình
    let n = 0;
    const tick = () => { entry.check(); if (++n < 8) requestAnimationFrame(tick); };
    requestAnimationFrame(tick);
    // an toàn: chắc chắn hiện rõ sau _SAFETY dù có chuyện gì
    const fb = setTimeout(() => entry.animate(rec), _SAFETY);
    return () => { entry.pending.delete(el); clearTimeout(fb); };
  }, [root, delay]);
  return ref;
}

function Reveal({ children, delay = 0, style, className = '' }) {
  const ref = useReveal(delay);
  return (
    <div ref={ref} className={'reveal ' + className} style={style}>
      {children}
    </div>
  );
}

// ---------- Photo placeholder (kéo ảnh thật vào thay sau) ----------
function Photo({ src, label = 'Ảnh cưới', style, className = '', round = 0 }) {
  return (
    <div className={'photo ' + className} style={{ borderRadius: round, ...style }}>
      {src ? <img src={src} alt={label} /> : (
        <div className="photo-mark">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M3 8.5A1.5 1.5 0 0 1 4.5 7h2L8 5h8l1.5 2h2A1.5 1.5 0 0 1 21 8.5v9A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5v-9Z"/>
            <circle cx="12" cy="12.5" r="3.2"/>
          </svg>
          <span>{label}</span>
        </div>
      )}
    </div>
  );
}

// ---------- Botanical line sprig (trang trí tối giản, khẽ đung đưa) ----------
function Sprig({ size = 40, color = 'var(--sage)', flip = false, sway = true, className = '', style }) {
  return (
    <svg width={size} height={size} viewBox="0 0 60 60" fill="none"
      className={'sprig' + (sway ? ' sprig--sway' : '') + (className ? ' ' + className : '')}
      stroke={color} strokeWidth="1" strokeLinecap="round"
      style={{ ...(flip ? { ['--sprig-flip']: '-1' } : null), ...style }}>
      <path d="M30 56 C30 40 30 24 30 8" />
      <path d="M30 44 C22 42 16 36 15 28 C24 29 30 35 30 44 Z" />
      <path d="M30 44 C38 42 44 36 45 28 C36 29 30 35 30 44 Z" />
      <path d="M30 30 C24 28 20 23 19 16 C26 17 30 22 30 30 Z" />
      <path d="M30 30 C36 28 40 23 41 16 C34 17 30 22 30 30 Z" />
      <circle cx="30" cy="8" r="2.4" fill={color} stroke="none" />
    </svg>
  );
}

// ---------- Countdown ----------
function useCountdown(targetISO) {
  const target = React.useMemo(() => new Date(targetISO).getTime(), [targetISO]);
  const calc = () => {
    const diff = Math.max(0, target - Date.now());
    const d = Math.floor(diff / 86400000);
    const h = Math.floor((diff % 86400000) / 3600000);
    const m = Math.floor((diff % 3600000) / 60000);
    const s = Math.floor((diff % 60000) / 1000);
    return { d, h, m, s, done: diff === 0 };
  };
  const [t, setT] = React.useState(calc);
  React.useEffect(() => { const id = setInterval(() => setT(calc()), 1000); return () => clearInterval(id); }, [target]);
  return t;
}

function Countdown({ accent = 'var(--sage-deep)', sep = false }) {
  const { d, h, m, s } = useCountdown(WED.dateISO);
  const items = [[d, 'Ngày'], [h, 'Giờ'], [m, 'Phút'], [s, 'Giây']];
  const pad = (n) => String(n).padStart(2, '0');
  return (
    <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'flex-start', gap: 0 }}>
      {items.map(([v, lab], i) => (
        <React.Fragment key={lab}>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', minWidth: 56 }}>
            <span style={{ fontFamily: 'var(--serif)', fontSize: 42, fontWeight: 500, lineHeight: 1, color: accent, fontVariantNumeric: 'tabular-nums' }}>{pad(v)}</span>
            <span className="eyebrow" style={{ fontSize: 9.5, marginTop: 8, color: 'var(--ink-soft)' }}>{lab}</span>
          </div>
          {sep && i < items.length - 1 && (
            <span style={{ fontFamily: 'var(--serif)', fontSize: 30, color: 'var(--sage-soft)', lineHeight: '42px', padding: '0 4px' }}>·</span>
          )}
        </React.Fragment>
      ))}
    </div>
  );
}

// ---------- Gallery (lưới ảnh + lightbox) ----------
function Gallery({ layout = 'mosaic', count = 6 }) {
  const [open, setOpen] = React.useState(null);
  const cells = Array.from({ length: count }, (_, i) => i);
  const tile = (i, extra) => (
    <div key={i} onClick={() => setOpen(i)} style={{ cursor: 'pointer', ...extra }}>
      <Photo label={'Khoảnh khắc ' + (i + 1)} style={{ width: '100%', height: '100%' }} round={2} />
    </div>
  );

  let grid;
  if (layout === 'mosaic') {
    grid = (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gridAutoRows: '108px', gap: 8 }}>
        {tile(0, { gridColumn: '1 / 2', gridRow: '1 / 3' })}
        {tile(1, {})}
        {tile(2, {})}
        {tile(3, { gridColumn: '1 / 3', gridRow: '3 / 4', height: 132 })}
        {tile(4, {})}
        {tile(5, {})}
      </div>
    );
  } else if (layout === 'strip') {
    grid = (
      <div className="no-scrollbar" style={{ display: 'flex', gap: 10, overflowX: 'auto', paddingBottom: 4, scrollSnapType: 'x mandatory' }}>
        {cells.map((i) => (
          <div key={i} onClick={() => setOpen(i)} style={{ flex: '0 0 64%', scrollSnapAlign: 'center', cursor: 'pointer' }}>
            <Photo label={'Khoảnh khắc ' + (i + 1)} style={{ width: '100%', height: 240 }} round={2} />
          </div>
        ))}
      </div>
    );
  } else {
    grid = (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 6 }}>
        {cells.map((i) => tile(i, { aspectRatio: '3 / 4' }))}
      </div>
    );
  }

  return (
    <>
      {grid}
      {open !== null && (
        <div onClick={() => setOpen(null)}
          style={{ position: 'absolute', inset: 0, zIndex: 40, background: 'rgba(40,42,34,.82)',
            display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, backdropFilter: 'blur(4px)' }}>
          <Photo label={'Khoảnh khắc ' + (open + 1)} style={{ width: '100%', height: 360, boxShadow: '0 20px 60px rgba(0,0,0,.4)' }} round={4} />
        </div>
      )}
    </>
  );
}

// ---------- Map ----------
function MapBlock({ height = 200, round = 4, frameColor = 'var(--line)' }) {
  return (
    <div>
      <div style={{ position: 'relative', height, borderRadius: round, overflow: 'hidden', border: '1px solid ' + frameColor, background: 'var(--sage-mist)' }}>
        <iframe title="Bản đồ địa điểm" src={WED.mapsEmbed}
          style={{ width: '100%', height: '100%', border: 0, filter: 'grayscale(.35) saturate(.85) contrast(.95)' }}
          loading="lazy" referrerPolicy="no-referrer-when-downgrade"></iframe>
      </div>
    </div>
  );
}

function MapButton({ block }) {
  return (
    <a href={WED.mapsLink} target="_blank" rel="noreferrer"
      style={{ display: block ? 'flex' : 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        fontFamily: 'var(--sans)', fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase',
        color: 'var(--sage-deep)', textDecoration: 'none', border: '1px solid var(--line)',
        padding: '12px 22px', borderRadius: 999, background: 'transparent', transition: 'all .25s',
        width: block ? '100%' : 'auto' }}
      onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--sage-deep)'; e.currentTarget.style.color = '#fff'; e.currentTarget.style.borderColor = 'var(--sage-deep)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--sage-deep)'; e.currentTarget.style.borderColor = 'var(--line)'; }}>
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
        <path d="M21 10c0 7-9 12-9 12s-9-5-9-12a9 9 0 0 1 18 0Z"/><circle cx="12" cy="10" r="3"/>
      </svg>
      Xem chỉ đường
    </a>
  );
}

// ---------- Music player (nút nhạc nền nổi) ----------
function MusicPlayer({ tone = 'sage', fixed = false, signal = 0 }) {
  const [playing, setPlaying] = React.useState(false);
  const [armed, setArmed] = React.useState(false);
  const audioRef = React.useRef(null);

  const toggle = () => {
    const a = audioRef.current;
    setArmed(true);
    if (!a) { setPlaying((p) => !p); return; }
    if (a.paused) { a.play().then(() => setPlaying(true)).catch(() => setPlaying(true)); }
    else { a.pause(); setPlaying(false); }
  };

  // Bắt đầu phát khi mở thiệp (chỉ khi đã gắn file nhạc)
  React.useEffect(() => {
    if (signal > 0) { const a = audioRef.current; if (a && a.src) { a.play().catch(() => {}); setPlaying(true); } }
  }, [signal]);

  const bg = tone === 'dark' ? 'rgba(40,42,34,.9)' : 'var(--paper)';
  const fg = tone === 'dark' ? '#fff' : 'var(--sage-deep)';

  return (
    <div style={{ position: fixed ? 'fixed' : 'absolute', right: fixed ? 'clamp(16px,3vw,28px)' : 16, bottom: fixed ? 'clamp(16px,3vw,28px)' : 18, zIndex: 120 }}>
      {/* URL nhạc lấy từ WED.musicUrl trong cùng file này */}
      <audio ref={audioRef} src={WED.musicUrl} loop preload="auto"></audio>
      <button onClick={toggle} aria-label="Bật / tắt nhạc nền"
        style={{ width: fixed ? 'clamp(46px,5vw,56px)' : 46, height: fixed ? 'clamp(46px,5vw,56px)' : 46, borderRadius: '50%', border: '1px solid var(--line)',
          background: bg, color: fg, boxShadow: 'var(--shadow-soft)', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          animation: playing ? 'spin 5s linear infinite' : 'none' }}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
          {playing ? (
            <><path d="M9 18V5l11-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="17" cy="16" r="3"/></>
          ) : (
            <><path d="M9 18V5l11-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="17" cy="16" r="3"/><line x1="3" y1="3" x2="21" y2="21" stroke="currentColor" strokeWidth="1.5"/></>
          )}
        </svg>
      </button>
      {armed && playing && audioRef.current && !audioRef.current.src && (
        <div style={{ position: 'absolute', right: 56, bottom: 6, whiteSpace: 'nowrap',
          background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 8, padding: '6px 10px',
          fontFamily: 'var(--sans)', fontSize: 10, color: 'var(--ink-soft)', boxShadow: 'var(--shadow-soft)' }}>
          Thêm file nhạc (.mp3) của bạn
        </div>
      )}
    </div>
  );
}

// ---------- Cánh hoa rơi (petals) — lớp phủ toàn màn hình, dịu nhẹ ----------
function Petals({ count = 16 }) {
  const petals = React.useMemo(() => {
    if (_reduceMotion) return [];
    const colors = ['#E7C9C2', '#D8C7A8', '#BCC4AE', '#EAD7CE', '#C9D2BC'];
    return Array.from({ length: count }, (_, i) => {
      return {
        left: (i / count) * 100 + (i % 3) * 4,
        size: 8 + (i % 5) * 2.4,
        delay: -(i * 1.7) % 18,
        dur: 13 + (i % 6) * 2.6,
        drift: (i % 2 ? 1 : -1) * (24 + (i % 4) * 14),
        rot: (i % 2 ? 1 : -1) * (160 + (i % 3) * 90),
        color: colors[i % colors.length],
        opacity: 0.5 + (i % 4) * 0.12,
      };
    });
  }, [count]);

  if (!petals.length) return null;
  return (
    <div className="petals" aria-hidden="true">
      {petals.map((p, i) => (
        <span key={i} className="petal" style={{
          left: p.left + '%',
          width: p.size, height: p.size * 1.4,
          background: p.color,
          opacity: p.opacity,
          animationDuration: p.dur + 's',
          animationDelay: p.delay + 's',
          ['--drift']: p.drift + 'px',
          ['--rot']: p.rot + 'deg',
        }} />
      ))}
    </div>
  );
}

// ---------- Section wrapper helpers ----------
function Section({ children, style, pad = '64px 32px' }) {
  return <section style={{ padding: pad, ...style }}>{children}</section>;
}

Object.assign(window, {
  WED, RevealCtx, useReveal, Reveal, Photo, Sprig,
  useCountdown, Countdown, Gallery, MapBlock, MapButton, MusicPlayer, Section, Petals,
  __reduceMotion: _reduceMotion,
});
