// sv-screens.jsx — screens for the editorial-fullbleed portfolio.
/* global React, BlockFullscreen, BlockPair, PillButton, SafeImg */
const { useState: _uS, useEffect: _uE, useRef: _uR } = React;

/* Date « YYYY-MM-DD » → « 30 juillet 2026 » / « 30 July 2026 » (caps via CSS). */
function formatDate(iso, lang) {
  if (!iso) return '';
  try {
    const [y, m, d] = iso.split('-').map(Number);
    return new Intl.DateTimeFormat(lang === 'fr' ? 'fr-FR' : 'en-GB', {
      day: 'numeric', month: 'long', year: 'numeric',
    }).format(new Date(y, m - 1, d));
  } catch (e) {
    return iso;
  }
}

/* Séries visibles, dans l'ordre d'affichage. */
function visibleSeries() {
  const d = window.JP_DATA;
  return d && d.SERIES ? d.SERIES.filter((s) => !s.hidden) : [];
}

/* ------------------------------------------------------------------ */
/* Splash — first-visit entry.                                        */
/* ------------------------------------------------------------------ */
function Splash({ t, onEnter }) {
  const [leaving, setLeaving] = _uS(false);

  const enter = () => {
    if (leaving) return;
    setLeaving(true);
    setTimeout(onEnter, 800);
  };

  _uE(() => {
    const onWheel = (e) => { if (e.deltaY > 30) enter(); };
    const onKey = (e) => {
      if (['Enter', ' ', 'ArrowDown', 'Escape'].includes(e.key)) enter();
    };
    window.addEventListener('wheel', onWheel, { passive: true });
    window.addEventListener('keydown', onKey);
    return () => {
      window.removeEventListener('wheel', onWheel);
      window.removeEventListener('keydown', onKey);
    };
  }, [leaving]);

  return (
    <div
      className={`sv-splash ${leaving ? 'leaving' : ''}`}
      onClick={enter}
      role="button"
      aria-label={t.splash.enter}
    >
      <div className="sv-splash-word">Justin Personnaz</div>
      <div className="sv-splash-sub">{t.splash.hint}</div>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* Featured (home) — vertical feed of best-of blocks.                 */
/* ------------------------------------------------------------------ */
function Featured({ data, onOpenSerie }) {
  // Rythme éditorial : singles plein écran + diptyques (écran partagé).
  // Les 2 premiers blocs en eager pour que la home s'affiche immédiatement.
  return (
    <main className="featured-feed">
      {data.FEATURED.map((item, i) =>
        item.type === 'pair' ? (
          <BlockPair key={i} item={item} onOpen={onOpenSerie} eager={i < 2} />
        ) : (
          <BlockFullscreen key={i} item={item} onOpen={onOpenSerie} eager={i < 2} />
        )
      )}
    </main>
  );
}

/* ------------------------------------------------------------------ */
/* SeriesIndex — typographic list of series, grouped by category.     */
/* ------------------------------------------------------------------ */
function SeriesIndex({ data, onOpenSerie }) {
  return (
    <main className="index-view">
      {data.CATEGORIES.map((cat) => (
        <section key={cat.id} className="index-cat-block">
          <span className="pill pill-label index-cat-pill">{cat.label}</span>
          <ul className="index-list">
            {cat.series.map((sid) => {
              const s = data.byId[sid];
              return <SeriesIndexRow key={sid} serie={s} onOpen={onOpenSerie} />;
            })}
          </ul>
        </section>
      ))}
    </main>
  );
}

function SeriesIndexRow({ serie, onOpen }) {
  const [hovered, setHovered] = _uS(false);
  return (
    <li>
      <button
        type="button"
        className={`index-serie ${hovered ? 'hovered' : ''}`}
        onMouseEnter={() => setHovered(true)}
        onMouseLeave={() => setHovered(false)}
        onClick={() => onOpen(serie.id)}
      >
        {serie.label}
      </button>
    </li>
  );
}

/* ------------------------------------------------------------------ */
/* SerieView — the "project page". Light canvas. Gallery | Index.     */
/* ------------------------------------------------------------------ */
function SerieView({ serie, t, onBack, onOpenSerie }) {
  const [mode, setMode] = _uS('gallery'); // 'gallery' | 'index'

  // Série suivante dans l'ordre des séries visibles (boucle après la dernière).
  const all = visibleSeries();
  const idx = all.findIndex((s) => s.id === serie.id);
  const next = (all.length > 1 && idx >= 0) ? all[(idx + 1) % all.length] : null;

  return (
    <main className="serie-view">
      {/* Top bar : wordmark Bodoni (back) + close cross (back) */}
      <div className="serie-topbar">
        <button type="button" className="serie-wordmark" onClick={onBack} title={t.serie.back}>
          Justin Personnaz
        </button>
        <button
          type="button"
          className="serie-close"
          onClick={onBack}
          aria-label={t.serie.close}
        >
          <svg viewBox="0 0 22 22" fill="none" aria-hidden="true">
            <path d="M2 2 L20 20 M20 2 L2 20" stroke="currentColor" strokeWidth="1.3" />
          </svg>
        </button>
      </div>

      {mode === 'gallery' ? (
        <div className="serie-gallery">
          {serie.images.map((img, i) => (
            <div className="serie-photo" key={img.base}>
              <img
                src={img.src}
                srcSet={img.srcset}
                sizes="100vw"
                alt={serie.caption}
                loading={i < 2 ? 'eager' : 'lazy'}
                decoding="async"
              />
            </div>
          ))}
        </div>
      ) : (
        <div className="serie-contact">
          {serie.images.map((img, i) => (
            <div
              className="serie-thumb"
              key={img.base}
              onClick={() => setMode('gallery')}
            >
              <img
                src={img.src}
                srcSet={img.srcset}
                sizes="(min-width: 992px) 25vw, (min-width: 640px) 33vw, 50vw"
                alt={serie.caption}
                loading={i < 8 ? 'eager' : 'lazy'}
                decoding="async"
              />
            </div>
          ))}
        </div>
      )}

      {/* Fin de série : bascule vers la suivante */}
      {next && onOpenSerie && (
        <div className="serie-next">
          <span className="serie-next-label">{t.serie.next}</span>
          <button
            type="button"
            className="serie-next-link"
            onClick={() => onOpenSerie(next.id)}
          >
            {next.label}
          </button>
        </div>
      )}

      {/* Bottom-centre : series title pill */}
      <div className="serie-title-pill">
        <span className="pill">{serie.title}</span>
      </div>

      {/* Bottom-right : mode toggle */}
      <div className="serie-mode-toggle">
        <PillButton label selected={mode === 'gallery'} onClick={() => setMode('gallery')}>
          {t.serie.gallery}
        </PillButton>
        <PillButton label selected={mode === 'index'} onClick={() => setMode('index')}>
          {t.serie.index}
        </PillButton>
      </div>
    </main>
  );
}

/* ------------------------------------------------------------------ */
/* ContactForm — RGPD form (validation + consent), dark canvas.       */
/* ------------------------------------------------------------------ */
function ContactForm({ t, lang }) {
  const c = t.contact;
  const [form, setForm] = _uS({ name: '', email: '', project: c.options[0], message: '', deadline: '', rgpd: false });
  const [errors, setErrors] = _uS({});
  const [sent, setSent] = _uS(false);

  const update = (k) => (e) => {
    const v = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
    setForm((f) => ({ ...f, [k]: v }));
    setErrors((er) => ({ ...er, [k]: undefined }));
  };

  const onSubmit = (e) => {
    e.preventDefault();
    const er = {};
    if (!form.name.trim()) er.name = lang === 'fr' ? 'Champ requis.' : 'Required.';
    if (!/^\S+@\S+\.\S+$/.test(form.email)) er.email = lang === 'fr' ? 'Email invalide.' : 'Invalid email.';
    if (!form.message.trim()) er.message = lang === 'fr' ? 'Champ requis.' : 'Required.';
    if (!form.rgpd) er.rgpd = lang === 'fr' ? 'Consentement requis.' : 'Consent required.';
    if (Object.keys(er).length) { setErrors(er); return; }
    // Pas de backend : on ouvre le client mail avec le message prérempli.
    const subject = `${form.project} — ${form.name}`;
    const bodyLines = [
      form.message,
      '',
      `— ${form.name} · ${form.email}`,
      form.deadline ? (lang === 'fr' ? `Échéance : ${form.deadline}` : `Deadline: ${form.deadline}`) : '',
    ].filter(Boolean);
    const mailto = `mailto:${(window.JP_DATA && window.JP_DATA.LINKS.email) || ''}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(bodyLines.join('\n'))}`;
    window.location.href = mailto;
    setSent(true);
  };

  if (sent) {
    return (
      <div className="sv-sent">
        <span className="sv-form-heading">{lang === 'fr' ? 'Envoyé' : 'Sent'}</span>
        <p className="sv-sent-msg">{c.sent}</p>
        <PillButton
          label
          onClick={() => { setSent(false); setForm({ name: '', email: '', project: c.options[0], message: '', deadline: '', rgpd: false }); }}
        >
          {lang === 'fr' ? 'Nouveau message' : 'New message'}
        </PillButton>
      </div>
    );
  }

  return (
    <form className="sv-form" onSubmit={onSubmit} noValidate>
      <span className="sv-form-heading">{c.formHeading}</span>

      <div className="sv-form-row">
        <label className="sv-field">
          <span className="sv-field-label">{c.fields.name}</span>
          <input type="text" value={form.name} onChange={update('name')} className={errors.name ? 'has-error' : ''} autoComplete="name" />
          {errors.name && <span className="sv-error">{errors.name}</span>}
        </label>
        <label className="sv-field">
          <span className="sv-field-label">{c.fields.email}</span>
          <input type="email" value={form.email} onChange={update('email')} className={errors.email ? 'has-error' : ''} autoComplete="email" />
          {errors.email && <span className="sv-error">{errors.email}</span>}
        </label>
      </div>

      <div className="sv-form-row">
        <label className="sv-field">
          <span className="sv-field-label">{c.fields.project}</span>
          <select value={form.project} onChange={update('project')}>
            {c.options.map((o) => <option key={o} value={o}>{o}</option>)}
          </select>
        </label>
        <label className="sv-field">
          <span className="sv-field-label">{c.fields.deadline}</span>
          <input type="text" value={form.deadline} onChange={update('deadline')} placeholder={c.deadlinePlaceholder} />
        </label>
      </div>

      <label className="sv-field">
        <span className="sv-field-label">{c.fields.message}</span>
        <textarea rows="5" value={form.message} onChange={update('message')} className={errors.message ? 'has-error' : ''} />
        {errors.message && <span className="sv-error">{errors.message}</span>}
      </label>

      <label className="sv-rgpd">
        <input type="checkbox" checked={form.rgpd} onChange={update('rgpd')} />
        <span>{c.rgpd}</span>
      </label>
      {errors.rgpd && <span className="sv-error">{errors.rgpd}</span>}

      <div className="sv-form-submit">
        <PillButton label onClick={onSubmit}>{c.submit}</PillButton>
      </div>
    </form>
  );
}

/* ------------------------------------------------------------------ */
/* BlogList — journal index (dark canvas), typographic.               */
/* ------------------------------------------------------------------ */
function BlogList({ data, t, lang, onOpenPost }) {
  const posts = data.BLOG || [];
  return (
    <main className="blog-view">
      <span className="blog-heading">{t.blog.title}</span>
      {posts.length === 0 ? (
        <p className="blog-empty">{t.blog.empty}</p>
      ) : (
        <ul className="blog-list">
          {posts.map((p) => (
            <BlogRow key={p.slug} post={p} lang={lang} onOpen={onOpenPost} />
          ))}
        </ul>
      )}
    </main>
  );
}

function BlogRow({ post, lang, onOpen }) {
  const open = () => onOpen(post.slug);
  return (
    <li
      className="blog-item"
      onClick={open}
      role="link"
      tabIndex={0}
      onKeyDown={(e) => { if (e.key === 'Enter') open(); }}
    >
      <span className="blog-date">{formatDate(post.date, lang)}</span>
      <span className="blog-title">{post.title[lang]}</span>
      <p className="blog-excerpt">{post.excerpt[lang]}</p>
    </li>
  );
}

/* ------------------------------------------------------------------ */
/* BlogPost — single article, centred reading column. Dark canvas.    */
/* ------------------------------------------------------------------ */
function BlogPost({ post, t, lang, onBack }) {
  const body = (post.body && post.body[lang]) || [];
  return (
    <main className="post-view">
      <button type="button" className="post-back" onClick={onBack}>
        {'← '}{t.blog.back}
      </button>

      <div className="post-head">
        <span className="post-date">{formatDate(post.date, lang)}</span>
        <h1 className="post-title">{post.title[lang]}</h1>
      </div>

      {post.img && (
        <div className="post-image">
          <SafeImg img={post.img} alt={post.title[lang]} sizes="(min-width: 680px) 640px, 100vw" />
        </div>
      )}

      <div className="post-body">
        {body.map((p, i) => <p key={i}>{p}</p>)}
      </div>
    </main>
  );
}

/* ------------------------------------------------------------------ */
/* Info — bio + contact + form. Dark canvas.                          */
/* ------------------------------------------------------------------ */
function Info({ t, lang, links }) {
  const info = t.info;
  return (
    <main className="info-view">
      <section className="info-splash">
        <h1 className="info-name">Justin Personnaz</h1>
        <span className="info-role">{t.roleLine}</span>
      </section>

      <section className="info-bio">
        {info.bio.map((p, i) => <p key={i}>{p}</p>)}
      </section>

      <section className="info-contact">
        <a className="pill" href={`mailto:${links.email}`}>{links.email}</a>
        <div className="info-social">
          <a className="pill pill-label" href={links.instagram} target="_blank" rel="noopener noreferrer">{info.instagram}</a>
          <a className="pill pill-label" href={links.linkedin} target="_blank" rel="noopener noreferrer">{info.linkedin}</a>
        </div>
      </section>

      <section>
        <ContactForm t={t} lang={lang} />
      </section>
    </main>
  );
}

window.Splash = Splash;
window.Featured = Featured;
window.SeriesIndex = SeriesIndex;
window.SerieView = SerieView;
window.BlogList = BlogList;
window.BlogPost = BlogPost;
window.ContactForm = ContactForm;
window.Info = Info;
