// ShopPage.jsx — /tienda
//
// Real e-commerce catalog: products fetched from Shopify via shopify-feed.js
// (window.Shop) and rendered as a responsive product grid with Add-to-cart
// buttons. The cart drawer (Cart.jsx, mounted at app level) handles the rest
// of the checkout flow.
//
// Before Shopify is connected, the page falls back to the placeholder
// PRODUCTS from Shop.jsx and disables the buy buttons (showing "Coming soon")
// so the layout is real and the SEO is right — it just can't take money yet.
// See shopify-feed.js header for the setup steps.

const shopPageStyles = {
  hero: {
    background: 'var(--brand-cream)',
    borderBottom: '1px solid var(--color-divider)',
    padding: '56px 0 48px',
  },
  inner: { maxWidth: 1200, margin: '0 auto', padding: '0 32px' },
  breadcrumb: {
    display: 'flex', alignItems: 'center', gap: 8,
    fontFamily: 'var(--font-sans)', fontSize: 12, fontWeight: 600,
    letterSpacing: '0.14em', textTransform: 'uppercase',
    color: 'var(--color-fg-muted)', marginBottom: 22,
  },
  breadcrumbLink: { color: 'inherit', textDecoration: 'none' },
  breadcrumbSep: { opacity: 0.4 },
  eyebrow: {
    fontFamily: 'var(--font-sans)', fontWeight: 700,
    fontSize: 12, letterSpacing: '0.22em', textTransform: 'uppercase',
    color: 'var(--brand-coral)', marginBottom: 12,
  },
  title: {
    fontFamily: 'var(--font-serif)', fontWeight: 700,
    fontSize: 64, lineHeight: 1.04, letterSpacing: '-0.02em',
    margin: 0, color: 'var(--color-fg-strong)', textWrap: 'balance',
  },
  deck: {
    fontFamily: 'var(--font-serif)', fontStyle: 'italic', fontWeight: 400,
    fontSize: 22, lineHeight: 1.45, maxWidth: '52ch', marginTop: 22,
    color: 'var(--color-fg)', textWrap: 'pretty',
  },
};

const catalogStyles = {
  section: { background: '#fff', padding: '64px 0 96px' },
  inner: { maxWidth: 1200, margin: '0 auto', padding: '0 32px' },
  notice: {
    background: 'var(--brand-cream)',
    border: '1px solid var(--color-divider)',
    borderRadius: 12, padding: '14px 18px', marginBottom: 22,
    fontFamily: 'var(--font-sans)', fontSize: 13, lineHeight: 1.5,
    color: 'var(--color-fg)',
  },
  emptyState: {
    padding: '64px 16px', textAlign: 'center',
    fontFamily: 'var(--font-serif)', fontStyle: 'italic',
    fontSize: 20, color: 'var(--color-fg-muted)',
  },
  grid: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 28 },
  card: {
    background: '#fff', borderRadius: 14,
    border: '1px solid var(--color-divider)',
    overflow: 'hidden', display: 'flex', flexDirection: 'column',
    transition: 'transform 240ms var(--ease-out), box-shadow 240ms var(--ease-out)',
  },
  art: {
    aspectRatio: '1 / 1',
    background: 'var(--brand-mint-wash)',
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    overflow: 'hidden', position: 'relative',
  },
  artImg: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
  artFallback: {
    fontFamily: 'var(--font-serif)', fontStyle: 'italic',
    color: 'var(--brand-leaf)', fontSize: 48,
  },
  body: {
    padding: '18px 20px 22px',
    display: 'flex', flexDirection: 'column', gap: 10, flex: 1,
  },
  name: {
    fontFamily: 'var(--font-serif)', fontWeight: 600,
    fontSize: 18, lineHeight: 1.25,
    color: 'var(--color-fg-strong)', margin: 0,
  },
  desc: {
    fontFamily: 'var(--font-sans)', fontSize: 13, lineHeight: 1.5,
    color: 'var(--color-fg-muted)', margin: 0,
    display: '-webkit-box', WebkitLineClamp: 2,
    WebkitBoxOrient: 'vertical', overflow: 'hidden',
  },
  bottom: {
    marginTop: 'auto',
    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
    gap: 10, paddingTop: 8,
  },
  price: {
    fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 18, color: '#151414',
  },
  addBtn: {
    display: 'inline-flex', alignItems: 'center', gap: 6,
    padding: '10px 16px', borderRadius: 9999,
    background: 'var(--brand-leaf)', color: '#fff', border: 'none',
    fontFamily: 'var(--font-sans)', fontSize: 13, fontWeight: 700,
    letterSpacing: '0.04em', cursor: 'pointer',
    transition: 'background 200ms var(--ease-out)',
  },
  addBtnDisabled: {
    background: '#E3E1DE', color: '#767574', cursor: 'not-allowed',
  },
};

function ShopCatalog() {
  const lang = React.useContext(LangContext);
  const isMobile = useIsMobile();
  const [products, setProducts] = React.useState(null);
  const [loading, setLoading]   = React.useState(true);
  const [configured, setConfigured] = React.useState(true); // optimistic — flips false if server says so

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      if (!window.Shop) { if (!cancelled) { setProducts(null); setLoading(false); setConfigured(false); } return; }
      const list = await window.Shop.fetchProducts();
      if (!cancelled) {
        setProducts(list);
        setLoading(false);
        setConfigured(!!(window.Shop.isConfigured && window.Shop.isConfigured()));
      }
    })();
    const onShop = (e) => {
      if (cancelled) return;
      setProducts(e.detail || null);
      setConfigured(!!(window.Shop && window.Shop.isConfigured && window.Shop.isConfigured()));
    };
    window.addEventListener('tnt-shop-updated', onShop);
    return () => { cancelled = true; window.removeEventListener('tnt-shop-updated', onShop); };
  }, []);

  // Fallback preview from the placeholder PRODUCTS in Shop.jsx, so the page
  // still has visual content before Shopify is wired.
  const fallback = (window.PRODUCTS || []).map((p, i) => {
    const name = (p.name && (p.name[lang] || p.name.en)) || '';
    const meta = (p.meta && (p.meta[lang] || p.meta.en)) || '';
    const amt  = Number(String(p.price || '').replace(/[^0-9.]/g, ''));
    return {
      id: 'fallback-' + i,
      handle: name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
      title: name,
      description: meta,
      price: p.price,
      priceAmount: isNaN(amt) ? null : amt,
      image: null,
      tint: p.tint,
      defaultVariantId: null,
      available: false,
    };
  });

  const items = (products && products.length) ? products : fallback;
  // auto-fit + minmax keeps cards at a sensible 240–300px when you only have
  // a few products (no stretched slabs); fills naturally as you add more.
  const cols = isMobile ? '1fr' : 'repeat(auto-fit, minmax(275px, 345px))';

  const T = lang === 'es' ? {
    notice: 'La tienda aún no está conectada — los productos abajo son una vista previa. Añade STRIPE_SECRET_KEY en Cloudflare Pages para activar la compra (ver stripe-feed.js).',
    add: 'Añadir',
    soon: 'Próximamente',
    loading: 'Cargando productos…',
    empty: 'Aún no hay productos publicados.',
  } : {
    notice: 'The store is not connected yet — products below are a preview. Add STRIPE_SECRET_KEY in Cloudflare Pages to enable purchases (see stripe-feed.js).',
    add: 'Add',
    soon: 'Coming soon',
    loading: 'Loading products…',
    empty: 'No products published yet.',
  };

  const addToCart = (product, e) => {
    if (e) { e.preventDefault(); e.stopPropagation(); }
    if (!product || !product.defaultVariantId) return;
    window.Shop.addLine(product.defaultVariantId, 1, {
      title: product.title,
      price: product.price,
      priceAmount: product.priceAmount,
      image: product.image,
      handle: product.handle,
    });
    window.dispatchEvent(new CustomEvent('app:open-cart'));
  };

  return (
    <section style={{ ...catalogStyles.section, ...(isMobile ? { padding: '40px 0 64px' } : {}) }}>
      <div style={{ ...catalogStyles.inner, ...(isMobile ? { padding: '0 20px' } : {}) }}>
        {!configured && <div style={catalogStyles.notice}>{T.notice}</div>}

        {loading && (!items || !items.length) && (
          <div style={catalogStyles.emptyState}>{T.loading}</div>
        )}

        {!loading && (!items || !items.length) && (
          <div style={catalogStyles.emptyState}>{T.empty}</div>
        )}

        {items && items.length > 0 && (
          <div style={{ ...catalogStyles.grid, gridTemplateColumns: cols, gap: isMobile ? 16 : 28, justifyContent: isMobile ? 'stretch' : 'center' }}>
            {items.map(p => {
              const buyable = !!(configured && p.defaultVariantId && p.available !== false);
              const detailHref = p.handle ? hrefFor({ view: 'product', productHandle: p.handle }) : null;
              const card = (
                <article style={catalogStyles.card}>
                  <div style={{ ...catalogStyles.art, background: p.tint || catalogStyles.art.background }}>
                    {p.image
                      ? <img src={p.image} alt={p.title} style={catalogStyles.artImg}/>
                      : <span style={catalogStyles.artFallback}>{(p.title || '·').slice(0, 1)}</span>}
                  </div>
                  <div style={catalogStyles.body}>
                    <h3 style={catalogStyles.name}>{p.title}</h3>
                    {p.description && <p style={catalogStyles.desc}>{p.description}</p>}
                    <div style={catalogStyles.bottom}>
                      <span style={catalogStyles.price}>{p.price}</span>
                      <button
                        style={{ ...catalogStyles.addBtn, ...(buyable ? {} : catalogStyles.addBtnDisabled) }}
                        disabled={!buyable}
                        onClick={(e) => addToCart(p, e)}
                        title={buyable ? T.add : T.soon}>
                        {buyable ? T.add : T.soon}
                      </button>
                    </div>
                  </div>
                </article>
              );
              return detailHref
                ? <a key={p.id} href={detailHref} style={{ display: 'block', textDecoration: 'none', color: 'inherit' }}>{card}</a>
                : <div key={p.id}>{card}</div>;
            })}
          </div>
        )}
      </div>
    </section>
  );
}

// Build the hero deck dynamically from the live Stripe catalog so the copy
// always reflects what's actually for sale. Reads each product's optional
// `metadata.category` and lists the unique values in a natural sentence.
// Falls back to a generic-but-true line when no categories are set, and to
// the static i18n string when there are no products yet.
function formatCategoryList(items, lang) {
  if (!items.length) return '';
  if (items.length === 1) return items[0];
  if (items.length === 2) return items.join(lang === 'es' ? ' y ' : ' and ');
  const head = items.slice(0, -1).join(', ');
  return head + (lang === 'es' ? ' y ' : ', and ') + items[items.length - 1];
}
function buildDynamicDeck(products, lang) {
  if (!products || !products.length) return null;
  const cats = [...new Set(
    products
      .map(p => (p.metadata && p.metadata.category ? String(p.metadata.category).trim() : ''))
      .filter(Boolean)
  )].sort((a, b) => a.localeCompare(b, lang));
  if (cats.length) {
    const joined = formatCategoryList(cats, lang);
    return lang === 'es'
      ? `Productos seleccionados a mano: ${joined.toLowerCase()}. Cada uno probado al menos un mes antes de tenerlo en stock.`
      : `Hand-picked products: ${joined.toLowerCase()}. Each one tested for at least a month before stocking it.`;
  }
  return lang === 'es'
    ? 'Productos seleccionados a mano, de pequeños lotes. Cada uno probado al menos un mes antes de tenerlo en stock.'
    : 'Hand-picked products from small-batch makers. Each one tested for at least a month before stocking it.';
}

function ShopPage() {
  const tr = useT();
  const lang = React.useContext(LangContext);
  const isMobile = useIsMobile();
  const crumb = tr('nav.shop');

  // Subscribe to the live catalog so the deck stays in sync with whatever
  // you've published in Stripe. fetchProducts is cached at the engine level
  // so calling it here in addition to ShopCatalog is free.
  const [products, setProducts] = React.useState(null);
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      if (!window.Shop || !window.Shop.fetchProducts) return;
      const list = await window.Shop.fetchProducts();
      if (!cancelled) setProducts(list || []);
    })();
    const onShop = (e) => { if (!cancelled) setProducts(e.detail || []); };
    window.addEventListener('tnt-shop-updated', onShop);
    return () => { cancelled = true; window.removeEventListener('tnt-shop-updated', onShop); };
  }, []);
  const dynamicDeck = buildDynamicDeck(products, lang);
  const deck = dynamicDeck || tr('shop.sub');

  React.useEffect(() => {
    const canonical = SEO.url(buildPath({ view: 'shop' }));
    updateSeo({
      title: tr('shop.title'),
      description: deck,
      canonical,
      ogType: 'website',
      ogImage: SEO.url('/og-image.jpg'),
      lang,
      jsonLd: {
        '@context': 'https://schema.org',
        '@type': 'CollectionPage',
        name: crumb + ' — Top Natural Tips',
        description: deck,
        url: canonical,
        isPartOf: { '@type': 'WebSite', name: 'Top Natural Tips', url: SEO.ORIGIN + '/' },
        publisher: SEO.org(),
      },
    });
  }, [lang, deck]);

  return (
    <main>
      <header style={{ ...shopPageStyles.hero, ...(isMobile ? { padding: '40px 0 32px' } : {}) }}>
        <div style={{ ...shopPageStyles.inner, ...(isMobile ? { padding: '0 20px' } : {}) }}>
          <nav style={shopPageStyles.breadcrumb} aria-label="Breadcrumb">
            <a href={hrefFor({ view: 'home' })} style={shopPageStyles.breadcrumbLink}>{tr('section.back')}</a>
            <span style={shopPageStyles.breadcrumbSep}>/</span>
            <span>{crumb}</span>
          </nav>
          <div style={shopPageStyles.eyebrow}>{tr('shop.eyebrow')}</div>
          <h1 style={{ ...shopPageStyles.title, ...(isMobile ? { fontSize: 34 } : {}) }}>{tr('shop.title')}</h1>
          <p style={{ ...shopPageStyles.deck, ...(isMobile ? { fontSize: 18 } : {}) }}>{deck}</p>
        </div>
      </header>

      <ShopCatalog/>
    </main>
  );
}

window.ShopCatalog = ShopCatalog;
window.ShopPage = ShopPage;
