// Components for the Parallel Editions site.
// All component-scope styles are inline or scoped via classNames in the main CSS.

const { useState, useMemo, useEffect, useRef } = React;

// GoatCounter custom event. `event: true` files it under the Events tab.
function trackEvent(path, title) {
  if (typeof window === "undefined") return;
  window.goatcounter?.count?.({ path, title, event: true });
}

/* ───────── Typographic cover ─────────
   When we don't have a real cover image, render a typographic one.
   Each book has a small palette to differentiate them. The bottom band
   reflects the edition's direction (e.g. "ES-EN BILINGUAL"). */
function TypographicCover({ book, edition }) {
  const [bg, accent, text] = book.palette;
  const isLight = (hex) => {
    const r = parseInt(hex.slice(1,3),16), g = parseInt(hex.slice(3,5),16), b = parseInt(hex.slice(5,7),16);
    return (r*0.299 + g*0.587 + b*0.114) > 160;
  };
  const inkOnBg = isLight(bg) ? "#1a1a1a" : "#f5ebe0";
  const bandLabel = edition
    ? `${edition.target.toUpperCase()} + ${edition.support.toUpperCase()} · BILINGUAL`
    : "BILINGUAL";

  return (
    <div className="cover" style={{ background: bg, color: inkOnBg }}>
      <div className="cover-classic">
        <div className="cover-band-top" style={{ background: accent, color: isLight(accent) ? "#1a1a1a" : "#f5ebe0" }}>
          PARALLEL EDITIONS
        </div>
        <div className="cover-classic-body">
          <div className="cover-classic-title">{book.title}</div>
          <div className="cover-classic-rule" style={{ background: accent }}></div>
          <div className="cover-classic-author">{book.author}</div>
        </div>
        <div className="cover-band-bot" style={{ borderColor: accent }}>
          <span>{bandLabel}</span>
          <span>{book.year}</span>
        </div>
      </div>
    </div>
  );
}

/* ───────── Photo cover ───────── */
function PhotoCover({ src, title, author }) {
  // `src` is the .jpg path from data.js; covers-webp.sh emits a .webp sibling.
  // The browser picks WebP when it can and falls back to the JPEG otherwise.
  const webp = src.replace(/\.jpg$/, ".webp");
  return (
    <div className="cover cover-photo">
      <picture>
        <source srcSet={webp} type="image/webp" />
        <img
          src={src}
          alt={`${title}, ${author}`}
          loading="lazy"
          decoding="async"
          width="400"
          height="600"
        />
      </picture>
    </div>
  );
}

/* ───────── Samples manifest ─────────
   Fetches samples/manifest.json once on mount. Returns a Map of
   editionId ("book-target-support") → file path, or null while loading. */
function useSamplesManifest() {
  const [manifest, setManifest] = useState(null);
  useEffect(() => {
    fetch("samples/manifest.json")
      .then(r => (r.ok ? r.json() : {}))
      .then(json => {
        const m = new Map();
        for (const [key, entry] of Object.entries(json)) {
          m.set(key, entry.file);
        }
        setManifest(m);
      })
      .catch(() => setManifest(new Map()));
  }, []);
  return manifest;
}

/* ───────── Sample reader modal ─────────
   Renders a paginated epub.js reader for the sample associated with one
   edition. Closes on backdrop click, ESC, or the close button. */
function SampleReaderModal({ book, edition, samplePath, onClose }) {
  const viewportRef = useRef(null);
  const renditionRef = useRef(null);
  const cardRef = useRef(null);
  const [loading, setLoading] = useState(true);
  const [progress, setProgress] = useState({ page: 0, total: 0 });
  const [atStart, setAtStart] = useState(true);
  const [atEnd, setAtEnd] = useState(false);

  const targetLang = LANG_BY_CODE[edition.target];
  const supportLang = LANG_BY_CODE[edition.support];
  const placeholder = isPlaceholder(edition);

  useEffect(() => {
    if (!viewportRef.current || !window.ePub) return;
    const book_ = window.ePub(samplePath);
    const rendition = book_.renderTo(viewportRef.current, {
      width: "100%",
      height: "100%",
      flow: "paginated",
      spread: "none",
      allowScriptedContent: false,
    });
    renditionRef.current = rendition;

    rendition.themes.default({
      "body": {
        "font-family": "'Newsreader', Georgia, serif !important",
        "color": "#17191d !important",
        "background": "#f4f2ec !important",
        "padding": "0 8px !important",
      },
      ".pair-container": {
        "padding": "0.4em 0",
        "border-bottom": "1px solid rgba(60, 60, 60, 0.14)",
      },
      ".target-text p": { "margin": "0 0 0.4em" },
      ".source-text p": {
        "margin": "0 0 0.6em",
        "color": "#383b42",
        "font-style": "italic",
        "opacity": "0.9",
      },
      "h1": {
        "font-family": "'Schibsted Grotesk', Helvetica, sans-serif",
        "font-weight": "600",
        "letter-spacing": "-0.02em",
      },
    });

    rendition.display().then(() => setLoading(false));

    book_.ready.then(() => book_.locations.generate(1600)).then(() => {
      const total = book_.locations.length();
      setProgress(p => ({ ...p, total }));
    });

    rendition.on("relocated", (loc) => {
      setAtStart(loc.atStart === true);
      setAtEnd(loc.atEnd === true);
      const pct = loc.start.percentage;
      const total = book_.locations.length();
      if (total) {
        setProgress({ page: Math.max(1, Math.round(pct * total)), total });
      }
    });

    return () => {
      try { rendition.destroy(); } catch {}
      try { book_.destroy(); } catch {}
    };
  }, [samplePath]);

  useEffect(() => {
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";

    // Remember what was focused so we can restore it on close, then move
    // focus into the dialog (the card itself — see tabIndex={-1} below).
    const prevFocus = document.activeElement;
    cardRef.current?.focus();

    // Focusable chrome controls inside the dialog. The epub.js content lives
    // in a cross-origin-ish iframe we deliberately don't reach into; trapping
    // the card's own controls is enough to keep keyboard focus contained.
    const focusablesIn = (root) => Array.from(
      root.querySelectorAll('button:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])')
    );

    const onKey = (e) => {
      if (e.key === "Escape") { onClose(); return; }
      if (e.key === "ArrowLeft") { renditionRef.current?.prev(); return; }
      if (e.key === "ArrowRight") { renditionRef.current?.next(); return; }
      if (e.key === "Tab" && cardRef.current) {
        const items = focusablesIn(cardRef.current);
        if (items.length === 0) { e.preventDefault(); return; }
        const first = items[0];
        const last = items[items.length - 1];
        const active = document.activeElement;
        // Wrap at the ends, and pull focus back in if it has escaped the card.
        if (e.shiftKey) {
          if (active === first || !cardRef.current.contains(active)) {
            e.preventDefault();
            last.focus();
          }
        } else if (active === last || !cardRef.current.contains(active)) {
          e.preventDefault();
          first.focus();
        }
      }
    };
    window.addEventListener("keydown", onKey);
    return () => {
      document.body.style.overflow = prevOverflow;
      window.removeEventListener("keydown", onKey);
      if (prevFocus && typeof prevFocus.focus === "function") prevFocus.focus();
    };
  }, [onClose]);

  const onBackdropClick = (e) => {
    if (e.target === e.currentTarget) onClose();
  };

  return (
    <div className="reader-overlay" onClick={onBackdropClick} role="dialog" aria-modal="true" aria-label={`${book.title} sample`}>
      <div className="reader-card" ref={cardRef} tabIndex={-1}>
        <header className="reader-head">
          <div>
            <h2 className="reader-head-title">{book.title}</h2>
            <span className="reader-head-meta">
              {edition.target === "en"
                ? `Sample · English edition for ${supportLang?.name} speakers`
                : `Sample · ${targetLang?.name} (${targetLang?.native}) with ${supportLang?.name} helper`}
            </span>
          </div>
          <button className="reader-close" onClick={onClose} aria-label="Close sample reader">×</button>
        </header>

        <div className="reader-body">
          <button
            className="reader-nav"
            onClick={() => renditionRef.current?.prev()}
            disabled={atStart}
            aria-label="Previous page"
          >‹</button>
          <div className="reader-viewport" ref={viewportRef}>
            {loading && <div className="reader-loading">Loading sample…</div>}
          </div>
          <button
            className="reader-nav"
            onClick={() => renditionRef.current?.next()}
            disabled={atEnd}
            aria-label="Next page"
          >›</button>
        </div>

        <footer className="reader-foot">
          <span className="reader-progress">
            {progress.total
              ? `${progress.page} / ${progress.total}`
              : "Sample · 1 chapter"}
          </span>
          {placeholder ? (
            <a href="#requests" className="reader-cta reader-cta--ghost" onClick={onClose}>
              Coming soon, request a notification
            </a>
          ) : (
            <a href={edition.url} target="_blank" rel="noopener" className="reader-cta">
              Get the full edition on Kindle →
            </a>
          )}
        </footer>
      </div>
    </div>
  );
}

/* ───────── Edition card ─────────
   One card = one purchasable edition (one direction). */
function EditionCard({ book, edition, samplesAvailable, onRequestSample }) {
  const targetLang = LANG_BY_CODE[edition.target];
  const supportLang = LANG_BY_CODE[edition.support];
  const eid = editionId(book, edition);
  const samplePath = samplesAvailable?.get(eid) || null;
  const placeholder = isPlaceholder(edition);

  return (
    <article className="book-card">
      <div className="book-card-cover-wrap">
        {edition.cover
          ? <PhotoCover src={edition.cover} title={book.title} author={book.author} />
          : <TypographicCover book={book} edition={edition} />}
      </div>
      <div className="book-card-body">
        <div className="edition-badge" title={`${targetLang?.name} prominent, ${supportLang?.name} helper`}>
          <span className="edition-badge-arrow">
            <span className="edition-badge-code">{edition.target.toUpperCase()}</span>
            <span className="edition-badge-sep">·</span>
            <span className="edition-badge-code edition-badge-code--muted">{edition.support.toUpperCase()}</span>
          </span>
          <span className="edition-badge-audience">Learn {targetLang?.name}</span>
        </div>
        <h3 className="book-card-title">{book.title}</h3>
        <div className="book-card-meta">
          <span className="book-card-author">{book.author}</span>
          <span className="book-card-dot">·</span>
          <span className="book-card-year">{book.year}</span>
        </div>
        <div className="book-card-actions">
          {placeholder ? (
            <a href="#requests" className="edition-cta edition-cta--ghost">
              Coming soon
            </a>
          ) : (
            <a
              href={edition.url}
              target="_blank"
              rel="noopener"
              className="edition-cta"
              title={`Buy on ${targetLang?.market}`}
              onClick={() => trackEvent(
                `kindle-click-${editionId(book, edition)}`,
                `Kindle: ${book.title} (${edition.target}→${edition.support})`
              )}
            >
              Get on Kindle →
            </a>
          )}
          {samplePath && (
            <button
              className="read-sample-btn"
              onClick={() => {
                trackEvent(
                  `sample-open-${editionId(book, edition)}`,
                  `Sample: ${book.title} (${edition.target}→${edition.support})`
                );
                onRequestSample(book, edition, samplePath);
              }}
              aria-label={`Read a sample of ${book.title} (${targetLang?.name} edition)`}
            >
              Read sample
            </button>
          )}
        </div>
      </div>
    </article>
  );
}

/* ───────── Language filter ─────────
   Two grouped rows of audience-prose pills. Each pill represents one
   (target, support) direction cohort. `value` is either null (all editions)
   or { target, support }; `onChange` receives the same shape. */
function LanguageFilter({ value, onChange, editions }) {
  const { learnersOfX, englishLearners } = useMemo(() => {
    const learnersOfX = {};   // target -> count, for XX-EN editions
    const englishLearners = {}; // support -> count, for EN-XX editions
    editions.forEach(({ edition }) => {
      if (edition.target !== "en" && edition.support === "en") {
        learnersOfX[edition.target] = (learnersOfX[edition.target] || 0) + 1;
      } else if (edition.target === "en" && edition.support !== "en") {
        englishLearners[edition.support] = (englishLearners[edition.support] || 0) + 1;
      }
    });
    return { learnersOfX, englishLearners };
  }, [editions]);

  const isActive = (target, support) =>
    value && value.target === target && value.support === support;

  const pill = (lang, target, support, count) => (
    <button
      key={`${target}-${support}`}
      className={`lang-filter-btn ${isActive(target, support) ? "is-active" : ""}`}
      onClick={() => {
        trackEvent(`lang-filter-${target}-${support}`, `Filter: ${target}→${support}`);
        onChange({ target, support });
      }}
    >
      <span className="lang-filter-name">{lang.name}</span>
      <span className="lang-filter-native">{lang.native}</span>
      <span className="lang-filter-count">{count}</span>
    </button>
  );

  return (
    <div className="lang-filter-groups">
      <div className="lang-filter-group">
        <div className="lang-filter-label">English speakers learning…</div>
        <div className="lang-filter">
          {LANGUAGES
            .filter(l => l.code !== "en" && learnersOfX[l.code])
            .map(l => pill(l, l.code, "en", learnersOfX[l.code]))}
        </div>
      </div>

      <div className="lang-filter-group">
        <div className="lang-filter-label">Learning English, native language…</div>
        <div className="lang-filter">
          {LANGUAGES
            .filter(l => l.code !== "en" && englishLearners[l.code])
            .map(l => pill(l, "en", l.code, englishLearners[l.code]))}
        </div>
      </div>

      <div className="lang-filter">
        <button
          className={`lang-filter-btn ${value === null ? "is-active" : ""}`}
          onClick={() => onChange(null)}
        >
          <span className="lang-filter-name">All editions</span>
          <span className="lang-filter-count">{editions.length}</span>
        </button>
      </div>
    </div>
  );
}

/* ───────── Pitch / feature strip ───────── */
function PitchStrip() {
  const items = [
    {
      n: "01",
      title: "Paragraph-level parallel text",
      body: "Each translated paragraph sits directly beside the original, so you never lose the thread between the two languages."
    },
    {
      n: "02",
      title: "End-of-chapter vocabulary notes",
      body: "Fifteen to twenty entries per chapter: idioms, period vocabulary, and culturally loaded expressions a dictionary alone won't explain."
    },
    {
      n: "03",
      title: "Kindle dictionary auto-switching",
      body: "Every paragraph carries a language tag, so long-pressing a word opens the dictionary for that language and not the other one."
    },
    {
      n: "04",
      title: "A guide to studying with the book",
      body: "Three ways to read it: target-language first for B2 and up, native-language first for B1 to B2, and shadow reading."
    },
  ];
  return (
    <section className="pitch">
      <div className="pitch-head">
        <h2 className="pitch-title">
          What's different about these editions
        </h2>
        <p className="pitch-lede">
          Every title is a complete bilingual edition, set paragraph by paragraph rather than in chapter blocks or sentence fragments. It is built for upper-intermediate and advanced readers (CEFR B2 to C1) who are ready to move from textbooks to real books.
        </p>
      </div>
      <ol className="pitch-grid">
        {items.map(it => (
          <li key={it.n} className="pitch-item">
            <div className="pitch-n">{it.n}</div>
            <div className="pitch-item-body">
              <h3 className="pitch-item-title">{it.title}</h3>
              <p className="pitch-item-text">{it.body}</p>
            </div>
          </li>
        ))}
      </ol>
    </section>
  );
}

/* ───────── Hero ───────── */
function Hero() {
  return (
    <section className="hero">
      <div className="hero-eyebrow">
        <span className="hero-eyebrow-rule"></span>
        <span>A library of bilingual classics</span>
      </div>
      <h1 className="hero-title">
        Read the classics<br/>
        <em className="hero-title-em">in two languages at once.</em>
      </h1>
      <p className="hero-sub">
        Paragraph-by-paragraph parallel editions of public-domain literature, for English speakers learning a language and for anyone learning English. Pick the direction that suits you, on Kindle.
      </p>
      <div className="hero-actions">
        <a href="#catalog" className="btn btn-primary">Browse the catalog</a>
        <a href="#pitch" className="btn btn-ghost">How they work →</a>
      </div>
      <div className="hero-langs">
        {LANGUAGES.map(l => (
          <span key={l.code}>
            <span className="hero-langs-native">{l.native}</span>
            <span className="hero-langs-code">{l.code.toUpperCase()}</span>
          </span>
        ))}
      </div>
    </section>
  );
}

/* ───────── Catalog ───────── */
function Catalog() {
  const [selection, setSelection] = useState(null); // { target, support } | null
  const [sort, setSort] = useState("language");
  const [reader, setReader] = useState(null); // { book, edition, samplePath } when open
  const samplesAvailable = useSamplesManifest();

  // Flatten books into editions — each is its own catalog card.
  const allEditions = useMemo(
    () => BOOKS.flatMap(b => b.editions.map(edition => ({ book: b, edition }))),
    []
  );

  const filtered = useMemo(() => {
    let list = selection
      ? allEditions.filter(({ edition }) =>
          edition.target === selection.target && edition.support === selection.support)
      : allEditions.slice();
    if (sort === "language") {
      const langRank = code => (code === "es" ? 0 : 1);
      list.sort((a, b) => {
        const ar = langRank(a.edition.target);
        const br = langRank(b.edition.target);
        if (ar !== br) return ar - br;
        const at = LANG_BY_CODE[a.edition.target].name;
        const bt = LANG_BY_CODE[b.edition.target].name;
        if (at !== bt) return at.localeCompare(bt);
        const as = LANG_BY_CODE[a.edition.support].name;
        const bs = LANG_BY_CODE[b.edition.support].name;
        if (as !== bs) return as.localeCompare(bs);
        return a.book.title.localeCompare(b.book.title);
      });
    }
    else if (sort === "title") list.sort((a, b) => a.book.title.localeCompare(b.book.title));
    else if (sort === "year") list.sort((a, b) => a.book.year - b.book.year);
    else if (sort === "author") list.sort((a, b) => a.book.author.localeCompare(b.book.author));
    return list;
  }, [allEditions, selection, sort]);

  return (
    <section id="catalog" className="catalog">
      <div className="catalog-head">
        <div className="catalog-head-left">
          <h2 className="catalog-title">{BOOKS.length} classics. {LANGUAGES.length} languages. {allEditions.length} editions.</h2>
        </div>
        <div className="catalog-head-right">
          <label className="catalog-sort">
            <span>Sort</span>
            <select value={sort} onChange={e => setSort(e.target.value)}>
              <option value="language">By language</option>
              <option value="title">A–Z by title</option>
              <option value="author">A–Z by author</option>
              <option value="year">By publication year</option>
            </select>
          </label>
        </div>
      </div>

      <LanguageFilter value={selection} onChange={setSelection} editions={allEditions} />

      <div className="catalog-resultline">
        {selection
          ? selection.target === "en"
            ? <>Showing <strong>{filtered.length}</strong> editions for <strong>{LANG_BY_CODE[selection.support].name}</strong> speakers learning English</>
            : <>Showing <strong>{filtered.length}</strong> editions for English speakers learning <strong>{LANG_BY_CODE[selection.target].name}</strong></>
          : <>Showing all <strong>{filtered.length}</strong> editions</>}
      </div>

      <div className="catalog-grid">
        {filtered.map(({ book, edition }) => (
          <EditionCard
            key={editionId(book, edition)}
            book={book}
            edition={edition}
            samplesAvailable={samplesAvailable}
            onRequestSample={(b, ed, samplePath) => setReader({ book: b, edition: ed, samplePath })}
          />
        ))}
      </div>

      {reader && (
        <SampleReaderModal
          book={reader.book}
          edition={reader.edition}
          samplePath={reader.samplePath}
          onClose={() => setReader(null)}
        />
      )}
    </section>
  );
}

/* ───────── Reader requests / feedback form ───────── */
function RequestsForm({ endpoint }) {
  const [status, setStatus] = useState("idle"); // idle | submitting | submitted | error

  async function onSubmit(e) {
    e.preventDefault();
    const form = e.currentTarget;
    setStatus("submitting");
    try {
      const res = await fetch(endpoint, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(form),
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      setStatus("submitted");
      form.reset();
    } catch (err) {
      setStatus("error");
    }
  }

  return (
    <section id="requests" className="requests">
      <div className="requests-head">
        <h2 className="requests-title">Tell us what to publish next.</h2>
        <p className="requests-lede">
          Want a classic we haven't done yet? A language pair or direction we don't offer? A feature you wish the books had, or a problem with one you bought? Send a note and it comes straight to us.
        </p>
      </div>

      {status === "submitted" ? (
        <div className="requests-thanks" role="status">
          <div className="requests-thanks-mark">✓</div>
          <h3>Thanks, we read every one.</h3>
          <p>If you left an email, we'll reply when we have something useful to say.</p>
        </div>
      ) : (
        <form className="requests-form" onSubmit={onSubmit} noValidate={false}>
          <input type="hidden" name="_subject" value="Reader request — parallel-editions.web.app" />
          {/* Honeypot — hidden from humans, irresistible to bots. */}
          <input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" className="field-honeypot" aria-hidden="true" />

          <div className="field-row">
            <label className="field">
              <span className="field-label">Your name <span className="field-opt">(optional)</span></span>
              <input type="text" name="name" autoComplete="name" maxLength={100} />
            </label>
            <label className="field">
              <span className="field-label">Email <span className="field-opt">(optional, if you want a reply)</span></span>
              <input type="email" name="_replyto" autoComplete="email" maxLength={200} />
            </label>
          </div>

          <label className="field">
            <span className="field-label">What's this about?</span>
            <select name="kind" required defaultValue="">
              <option value="" disabled>Choose one…</option>
              <option value="book">Book request: a classic we haven't published</option>
              <option value="translation">Language pair or direction we don't yet offer</option>
              <option value="feature">Feature for the books</option>
              <option value="problem">Problem with a book</option>
              <option value="other">Something else</option>
            </select>
          </label>

          <label className="field">
            <span className="field-label">Your message</span>
            <textarea
              name="message"
              required
              minLength={20}
              maxLength={4000}
              rows={6}
              placeholder="Tell us which book, which direction, or what's on your mind. Specifics help."
            />
          </label>

          <div className="requests-actions">
            <button type="submit" className="requests-submit" disabled={status === "submitting"}>
              {status === "submitting" ? "Sending…" : "Send your request"}
            </button>
            {status === "error" && (
              <span className="requests-error" role="alert">
                Something went wrong. Try again, or email <a href="mailto:stanislav.stoyanov@gmail.com">stanislav.stoyanov@gmail.com</a>.
              </span>
            )}
          </div>
        </form>
      )}
    </section>
  );
}

/* ───────── Masthead + footer ───────── */
function Masthead() {
  return (
    <header className="masthead" data-screen-label="Site masthead">
      <a href="#" className="masthead-brand">
        <span className="masthead-mark">P<span className="masthead-mark-amp">·</span>E</span>
        <span className="masthead-name">Parallel Editions</span>
      </a>
      <div className="masthead-actions">
        <nav className="masthead-nav">
          <a href="#requests">Requests</a>
        </nav>
        <a href="#catalog" className="masthead-cta">Browse →</a>
      </div>
    </header>
  );
}

function Footer() {
  return (
    <footer className="footer">
      <div className="footer-top">
        <div className="footer-brand">
          <div className="footer-mark">P·E</div>
          <div className="footer-tag">Parallel Editions publishes public-domain classics side by side, in either direction, across multiple languages.</div>
        </div>
        <div className="footer-cols">
          <div className="footer-col">
            <div className="footer-col-h">Browse</div>
            <a href="#catalog">All editions</a>
            <a href="#pitch">The format</a>
          </div>
          <div className="footer-col">
            <div className="footer-col-h">Marketplaces</div>
            <span>amazon.com · .co.uk · .de</span>
            <span>amazon.fr · .es · .it</span>
            <span>amazon.co.jp · .ca · .com.au</span>
          </div>
        </div>
      </div>
      <div className="footer-bot">
        <span>© {new Date().getFullYear()} Parallel Editions</span>
      </div>
    </footer>
  );
}

Object.assign(window, {
  TypographicCover, PhotoCover, EditionCard, LanguageFilter,
  PitchStrip, Hero, Catalog, RequestsForm, Masthead, Footer,
  SampleReaderModal,
});
