// ═══════════════════════════════════════════════════════════════
//  Aurelle Bride — components.jsx
//  Shared utilities, hooks, and UI components
//  Loaded first; assigns to window.* for cross-file access
// ═══════════════════════════════════════════════════════════════
window.Logo = function Logo({ size = 28, mark = false }) {
  return (
    <div style={{
      display:'flex',
      alignItems:'center',
      gap:10,
      fontFamily:'var(--serif)',
      fontSize:size,
      color:'var(--gold)'
    }}>
      <span>Aurelle</span>
      {!mark && <span style={{fontSize:size * 0.55}}>Bride</span>}
    </div>
  );
};

window.I = {
  arrow: () => <span>→</span>,
  sun: () => <span>☀</span>,
  moon: () => <span>☾</span>,
  plus: () => <span>＋</span>,
  close: () => <span>✕</span>,
  cog: () => <span>⚙</span>,
  upload: () => <span>⬆</span>,
};

window.Placeholder = function Placeholder({ label = '', tone = 'default' }) {
  return (
    <div style={{
      width:'100%',
      height:'100%',
      display:'flex',
      alignItems:'center',
      justifyContent:'center',
      background:'var(--bg-2)',
      color:'var(--ink-3)',
      fontSize:12
    }}>
      {label || 'No Image'}
    </div>
  );
};

window.useApp = function useApp() {
  return {
    theme: document.documentElement.dataset.theme || 'dark',
    toggleTheme: () => {
      const current = document.documentElement.dataset.theme || 'dark';
      document.documentElement.dataset.theme =
        current === 'dark' ? 'light' : 'dark';
    },
    navigate: (path) => {
      window.location.hash = '#/' + path;
    },
    showToast: (msg) => alert(msg),
    formatPrice: window.formatPKR
  };
};
const { useState, useEffect, useRef, useCallback, useMemo } = React;

// ── Config ────────────────────────────────────────────────────
window.cfg   = window.__SITE_CONFIG__ || {};
window.brand = window.cfg.brand || { brandName: 'Aurelle Bride', whatsapp: '+923358154775' };
window.seo   = window.cfg.seo   || {};

// ── API ───────────────────────────────────────────────────────
window.API_BASE = '/api';

window.apiFetch = async function(path, opts = {}) {
  const token = localStorage.getItem('ab_token');
  const headers = { 'Content-Type': 'application/json', ...(opts.headers || {}) };
  if (token) headers['Authorization'] = `Bearer ${token}`;
  // Auto-append .php if path has no extension and no query string starting segment
  const fullPath = path.replace(/^(\/(admin\/)?[a-z_]+)(\/|$|\?)/, (m, seg, adm, rest) => {
    return seg + '.php' + rest;
  });
  const res = await fetch(window.API_BASE + fullPath, { ...opts, headers });
  if (!res.ok) throw new Error(`API ${res.status}: ${await res.text()}`);
  return res.json();
};

// ── Formatting ────────────────────────────────────────────────
// formatPKR: admin-only, always shows raw PKR. Public prices use <PriceDisplay pkr={val} />.
window.formatPKR = function(val) {
  if (!val) return '—';
  return '₨ ' + Number(val).toLocaleString();
};
// Alias kept so any legacy code doesn't break during migration
window.formatPrice = window.formatPKR;

window.formatDate = function(d) {
  if (!d) return '—';
  return new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
};

window.timeAgo = function(d) {
  if (!d) return '';
  const diff = Date.now() - new Date(d).getTime();
  const mins = Math.floor(diff / 60000);
  if (mins < 60) return `${mins}m ago`;
  const hrs = Math.floor(mins / 60);
  if (hrs < 24) return `${hrs}h ago`;
  return Math.floor(hrs / 24) + 'd ago';
};

window.readingTime = function(html) {
  const words = (html || '').replace(/<[^>]+>/g, '').split(/\s+/).length;
  return Math.max(1, Math.round(words / 200));
};

// ── Seed / Fallback Data ──────────────────────────────────────
window.SEED_PRODUCTS = [
  { id: 8, name: 'Saffron Veil Lehenga', col: 'Lehenga', status: 'AVAILABLE', priceValue: 268000, tone: 'gold',
    desc: 'Saffron lehenga with hand-couched gold dabka and a sheer silk veil.',
    fabric_detail: 'Raw silk base with gold dabka threadwork. Sheer chiffon dupatta with scalloped border.',
    care: 'Dry clean only. Store flat, avoid direct sunlight.',
    delivery_weeks: 8, tags: 'bridal,lehenga,saffron,gold',
    image_url: 'https://aurellebride.com/uploads/177c6a178b39a0b6_Lehnga.jpg', date: '2026-04-10',
    gallery: [{ url: 'https://aurellebride.com/uploads/177c6a178b39a0b6_Lehnga.jpg', label: 'On model' },
              { url: 'https://aurellebride.com/uploads/8bfc7416e2aa0556_ChatGPT Image May 2, 2026, 01_42_12 PM.png', label: 'Embroidery detail' }] },
  { id: 9, name: 'Bridal Gown', col: 'Gown', status: '1 OF 1', priceValue: 298000, tone: 'default',
    desc: 'Black silk-satin couture gown for the modern reception bride.',
    fabric_detail: 'Heavyweight silk-satin with hand-set crystal embellishments on bodice.',
    care: 'Professional dry clean only.',
    delivery_weeks: 10, tags: 'bridal,gown,black,reception',
    image_url: 'https://aurellebride.com/uploads/7a08b8243982c91a_bridal gown.jpg', date: '2026-04-12',
    gallery: [{ url: 'https://aurellebride.com/uploads/7a08b8243982c91a_bridal gown.jpg', label: 'On model' }] },
  { id: 1777709176119, name: 'Bridal Mexi', col: 'Bridal Mexi', status: '1 OF 1', priceValue: 250000, tone: 'rich',
    desc: 'A richly embroidered mexi silhouette in deep blush.',
    delivery_weeks: 8, tags: 'bridal,mexi,blush',
    image_url: 'https://aurellebride.com/uploads/14d49814aaea6b7f_bridal mexi.jpg', date: '2026-05-02',
    gallery: [{ url: 'https://aurellebride.com/uploads/14d49814aaea6b7f_bridal mexi.jpg', label: 'On model' }] },
  { id: 1777711526393, name: 'Sharara Pink', col: 'Sharara', status: '1 OF 1', priceValue: 150000, tone: 'rich',
    desc: 'Blush-pink sharara with fine kiran border and organza kameez.',
    delivery_weeks: 7, tags: 'bridal,sharara,pink',
    image_url: 'https://aurellebride.com/uploads/2f586c5dcc02e4b7_Sharara.jpg', date: '2026-05-02',
    gallery: [{ url: 'https://aurellebride.com/uploads/2f586c5dcc02e4b7_Sharara.jpg', label: 'On model' }] },
  { id: 1777711611120, name: 'Anarkali', col: 'Anarkali', status: '1 OF 1', priceValue: 225000, tone: 'rich',
    desc: 'Floor-length anarkali with mirror-work yoke and flared skirt.',
    delivery_weeks: 9, tags: 'bridal,anarkali',
    image_url: 'https://aurellebride.com/uploads/9bc496a9d070d6fc_Anarkali.jpg', date: '2026-05-02',
    gallery: [{ url: 'https://aurellebride.com/uploads/9bc496a9d070d6fc_Anarkali.jpg', label: 'On model' }] },
  { id: 1777711644161, name: 'New Garara', col: 'Gharara', status: '1 OF 1', priceValue: 175000, tone: 'rich',
    desc: 'Ivory gharara with vintage gota patti work and raw silk kameez.',
    delivery_weeks: 8, tags: 'bridal,gharara,ivory',
    image_url: 'https://aurellebride.com/uploads/b17c151b9d328450_garara.jpg', date: '2026-05-02',
    gallery: [{ url: 'https://aurellebride.com/uploads/b17c151b9d328450_garara.jpg', label: 'On model' }] },
];

window.SEED_REVIEWS = [
  { id: 1, name: 'Sana M.', location: 'Dubai, UAE', occasion: 'Wedding · Jan 2026', rating: 5,
    title: 'Worth every penny', body: 'I wore the Saffron Veil Lehenga for my walima and received compliments all night. The craftsmanship is extraordinary — you can feel the hand-work in every inch.', featured: 1, status: 'approved' },
  { id: 2, name: 'Ayesha K.', location: 'London, UK', occasion: 'Engagement · Dec 2025', rating: 5,
    title: 'A dream come true', body: 'Aurelle created my custom lehenga from scratch. The team was patient with my measurements and the final result was beyond what I imagined.', featured: 1, status: 'approved' },
  { id: 3, name: 'Zara R.', location: 'Karachi, PK', occasion: 'Barat · Feb 2026', rating: 5,
    title: 'Truly one of a kind', body: 'The detail on the embroidery is something I have never seen from any other atelier. Every stitch feels intentional. My mother cried when she saw me.', featured: 1, status: 'approved' },
];

// ══════════════════════════════════════════════════════════════
//  HOOKS
// ══════════════════════════════════════════════════════════════

window.useToast = function() {
  const [toast, setToast] = useState(null);
  const show = useCallback((msg, type = 'success') => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3400);
  }, []);
  return { toast, show };
};

window.useReveal = function(ref) {
  useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      entries => entries.forEach(e => e.isIntersecting && e.target.classList.add('in')),
      { threshold: 0.08 }
    );
    ref.current.querySelectorAll('.reveal').forEach(el => obs.observe(el));
    return () => obs.disconnect();
  }, [ref]);
};

window.useProducts = function() {
  const [products, setProducts] = useState(window.SEED_PRODUCTS);
  const [loading, setLoading] = useState(false);
  useEffect(() => {
    setLoading(true);
    fetch('/api/products.php')
      .then(r => r.json())
      .then(d => { if (Array.isArray(d) && d.length) setProducts(d); })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);
  return { products, setProducts, loading };
};

// ══════════════════════════════════════════════════════════════
//  UI PRIMITIVES
// ══════════════════════════════════════════════════════════════

window.Spinner = function Spinner({ size = 20 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      style={{ animation: 'spin 0.8s linear infinite' }}>
      <style>{`@keyframes spin{to{transform:rotate(360deg)}}`}</style>
      <circle cx="12" cy="12" r="10" stroke="var(--line-2)" strokeWidth="2"/>
      <path d="M12 2a10 10 0 0 1 10 10" stroke="var(--gold)" strokeWidth="2" strokeLinecap="round"/>
    </svg>
  );
};

window.Toast = function Toast({ toast }) {
  if (!toast) return null;
  return <div className={`toast show ${toast.type}`}>{toast.msg}</div>;
};

window.Modal = function Modal({ open, onClose, title, children, maxW = '620px' }) {
  useEffect(() => {
    document.body.style.overflow = open ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [open]);
  if (!open) return null;
  return (
    <div className="modal-bg" onClick={e => e.target === e.currentTarget && onClose()}>
      <div className="modal-card" style={{ maxWidth: maxW }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
          <h3 style={{ fontFamily: 'var(--serif)', fontSize: 26, fontWeight: 400 }}>{title}</h3>
          <button className="icon-btn" onClick={onClose} aria-label="Close" style={{ fontSize: 18 }}>✕</button>
        </div>
        {children}
      </div>
    </div>
  );
};

window.Field = function Field({ label, required, error, hint, children, style }) {
  return (
    <div className="field" style={style}>
      {label && (
        <label>
          {label}
          {required && <span className="req"> *</span>}
          {hint && <span style={{ color: 'var(--ink-3)', fontWeight: 400, textTransform: 'none', letterSpacing: 0 }}> — {hint}</span>}
        </label>
      )}
      {children}
      {error && <span className="err">⚠ {error}</span>}
    </div>
  );
};

window.StatusPill = function StatusPill({ status }) {
  const s = (status || '').toLowerCase().replace(/\s+/g, '-');
  return <span className={`status-pill status-${s}`}>{status}</span>;
};

window.StarRating = function StarRating({ value = 5, onChange, size = 20 }) {
  const [hover, setHover] = useState(0);
  return (
    <div style={{ display: 'flex', gap: 4 }}>
      {[1, 2, 3, 4, 5].map(n => (
        <span key={n}
          onClick={() => onChange && onChange(n)}
          onMouseEnter={() => onChange && setHover(n)}
          onMouseLeave={() => onChange && setHover(0)}
          style={{
            fontSize: size, color: n <= (hover || value) ? 'var(--gold)' : 'var(--line-2)',
            cursor: onChange ? 'pointer' : 'default', transition: 'color .15s',
          }}>★</span>
      ))}
    </div>
  );
};

window.VideoEmbed = function VideoEmbed({ provider, videoId, videoUrl, thumbnail, title = '' }) {
  const src = {
    vimeo:   `https://player.vimeo.com/video/${videoId}?dnt=1&byline=0&portrait=0&title=0`,
    youtube: `https://www.youtube-nocookie.com/embed/${videoId}?rel=0`,
    bunny:   videoUrl,
    self:    videoUrl,
  }[provider] || videoUrl;

  if (provider === 'bunny' || provider === 'self') {
    return (
      <div className="video-wrap">
        <video controls preload="metadata" playsInline poster={thumbnail}>
          <source src={src} type="video/mp4" />
          Your browser does not support video.
        </video>
      </div>
    );
  }
  return (
    <div className="video-wrap">
      <iframe src={src} allow="autoplay; fullscreen; picture-in-picture"
        allowFullScreen loading="lazy" title={title} />
    </div>
  );
};

// ══════════════════════════════════════════════════════════════
//  MEASUREMENTS FORM
// ══════════════════════════════════════════════════════════════
window.MeasurementsForm = function MeasurementsForm({ value, onChange }) {
  const fields = [
    { key: 'bust', label: 'Bust', hint: 'fullest point' },
    { key: 'waist', label: 'Waist', hint: 'natural waist' },
    { key: 'hips', label: 'Hips', hint: 'fullest point' },
    { key: 'height', label: 'Height', hint: 'without shoes' },
    { key: 'shoulder', label: 'Shoulder', hint: 'seam to seam' },
    { key: 'sleeve_length', label: 'Sleeve Length', hint: 'shoulder to wrist' },
    { key: 'shirt_length', label: 'Shirt / Kameez Length', hint: '' },
    { key: 'trouser_length', label: 'Trouser / Pyjama Length', hint: '' },
  ];
  const u = value.unit || 'cm';
  const set = (k, v) => onChange({ ...value, [k]: v });

  return (
    <div>
      <div style={{ marginBottom: 20 }}>
        <p style={{ color: 'var(--ink-3)', fontSize: 13, marginBottom: 14 }}>
          All garments are made to your exact measurements — no off-the-rack sizing.
        </p>
        <div style={{ display: 'flex', gap: 16 }}>
          {['cm', 'in'].map(opt => (
            <label key={opt} style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13 }}>
              <input type="radio" name="munit" value={opt}
                checked={u === opt} onChange={() => set('unit', opt)} />
              <strong>{opt === 'cm' ? 'Centimetres (cm)' : 'Inches (in)'}</strong>
            </label>
          ))}
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        {fields.map(f => (
          <Field key={f.key} label={`${f.label} (${u})`} hint={f.hint}>
            <input type="number" step="0.5" min="0" placeholder="—"
              className="input"
              value={value[f.key] || ''}
              onChange={e => set(f.key, e.target.value)} />
          </Field>
        ))}
      </div>

      <Field label="Fit Preference" style={{ marginTop: 16 }}>
        <select className="select" value={value.measurement_note || 'regular'}
          onChange={e => set('measurement_note', e.target.value)}>
          <option value="fitted">Fitted — tailored close to body</option>
          <option value="regular">Regular — standard ease</option>
          <option value="loose">Comfortable / Loose</option>
        </select>
      </Field>

      <div style={{ marginTop: 16, padding: 14, borderRadius: 10, background: 'var(--gold-soft)', border: '1px solid color-mix(in oklab, var(--gold) 20%, transparent)' }}>
        <p style={{ fontSize: 12, color: 'var(--gold)', fontFamily: 'var(--mono)', letterSpacing: '.1em', textTransform: 'uppercase' }}>
          💡 Don't have a tailor nearby?
        </p>
        <p style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 6 }}>
          WhatsApp us a photo of yourself in fitted clothing with a measuring tape — our team will guide you through it.
        </p>
      </div>
    </div>
  );
};

// ══════════════════════════════════════════════════════════════
//  PRODUCT CARD
// ══════════════════════════════════════════════════════════════
window.ProductCard = function ProductCard({ product, onClick, index = 0 }) {
  const statusColor = { 'AVAILABLE': 'badge', 'SOLD': 'badge-ink', '1 OF 1': 'badge', 'ARCHIVED': 'badge-ink' };
  const cls = statusColor[product.status] || 'badge-ink';
  return (
    <article className="product"
      style={{ animation: `cardIn 0.5s ease-out ${index * 80}ms both` }}
      onClick={() => onClick && onClick(product)}
      role="button" tabIndex={0}
      onKeyDown={e => e.key === 'Enter' && onClick && onClick(product)}
      aria-label={`View ${product.name}`}>
      <style>{`@keyframes cardIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}`}</style>
      <div className="media">
        {product.image_url
          ? <img src={product.image_url} alt={product.name} loading="lazy" width="400" height="533" />
          : <div className="ph-img"><div className="ph-cross" /><div className="ph-label">No image</div></div>}
        <div className="status">
          <span className={cls}>{product.status}</span>
        </div>
      </div>
      <div className="meta">
        <div className="row1">
          <h3>{product.name}</h3>
        </div>
        <span className="col">{product.col}</span>
        {product.desc && <p style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 4 }}>{product.desc}</p>}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 10 }}>
          <PriceDisplay pkr={product.priceValue}
            style={{ fontFamily: 'var(--serif)', color: 'var(--gold)', fontSize: 18, fontStyle: 'italic' }}
            showPkrSub={false}
          />
          {product.delivery_weeks && (
            <span style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--mono)' }}>
              ~{product.delivery_weeks}w delivery
            </span>
          )}
        </div>
      </div>
    </article>
  );
};

// ══════════════════════════════════════════════════════════════
//  REVIEW CARD
// ══════════════════════════════════════════════════════════════
window.ReviewCard = function ReviewCard({ review }) {
  const initials = (review.name || '?').split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
  return (
    <div className="review-card">
      <StarRating value={review.rating} size={16} />
      {review.title && <p style={{ fontWeight: 600, fontSize: 15, margin: '10px 0 6px' }}>{review.title}</p>}
      <p className="review-body">"{review.body}"</p>
      <div className="review-meta">
        <div className="avatar">{initials}</div>
        <div className="info">
          <div className="name">{review.name}</div>
          <div className="loc">{[review.occasion, review.location].filter(Boolean).join(' · ')}</div>
        </div>
      </div>
    </div>
  );
};