const {
  useState: useListState,
  useEffect: useListEffect,
  useMemo: useListMemo,
  useRef: useListRef,
  useLayoutEffect: useListLayout,
} = React;

/* ---- Commerce: a vinyl affiliate touchpoint per entry ---- */
const VINYL_PRICES = [22.99, 24.99, 25.99, 26.99, 28.99, 29.99, 31.99, 33.99];
function vinylFor(a) {
  const url = (window.BBR_buyUrl)
    ? window.BBR_buyUrl(a.artist + " " + a.title, a.slug)
    : "https://www.amazon.co.uk/s?k=" + encodeURIComponent(a.artist + " " + a.title) + "+vinyl&i=popular&tag=theleadin1-21";
  return { url };
}

function VinylIcon() {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
      <circle cx="12" cy="12" r="9.2" />
      <circle cx="12" cy="12" r="2.3" />
    </svg>
  );
}

function VinylBuy({ album, inline }) {
  const { url } = vinylFor(album);
  // On touch devices the row's own onClick (internal album navigation) can
  // win over a child link, sending the user to the album page instead of the
  // retailer. Open the retailer explicitly and stop the event fully.
  const open = (e) => {
    e.preventDefault();
    e.stopPropagation();
    if (window.BBR_trackClick) window.BBR_trackClick(album.slug, "list");
    window.open(url, "_blank", "noopener");
  };
  return (
    <a
      className={"vinyl-buy" + (inline ? " inline" : "")}
      href={url}
      target="_blank"
      rel="noopener sponsored"
      onClick={open}
    >
      <b><VinylIcon /> Vinyl</b>
      <span className="vb-price">Buy →</span>
    </a>
  );
}

/* ---- Expandable blurb: two lines, then "more ▾" ---- */
function Blurb({ text }) {
  const [open, setOpen] = useListState(false);
  const [clipped, setClipped] = useListState(true); // start clamped, then measure
  const ref = useListRef(null);
  useListLayout(() => {
    const el = ref.current;
    if (el) setClipped(el.scrollHeight > el.clientHeight + 4);
  }, [text]);
  return (
    <div className="blurb-wrap">
      <p ref={ref} className={"blurb" + (open ? " open" : "") + (clipped && !open ? " clipped" : "")}>
        {text}
      </p>
      {(clipped || open) && (
        <button className="blurb-toggle" onClick={() => setOpen((o) => !o)} aria-expanded={open}>
          {open ? "less ▴" : "more ▾"}
        </button>
      )}
    </div>
  );
}

/* ---- Editorial breathers between rank tiers ---- */
const INTERJECTIONS = {
  10: "The top ten are consensus. From here, every placement is an argument.",
  25: "The quarter-century mark. Everything above here changed the language.",
  50: "The back half is where the list gets interesting.",
  75: "The final twenty-five. None of these need to be here. All of them earned it.",
};

function ListPage({ albums, onAlbum, onRequireAuth }) {
  const [decade, setDecade] = useListState("all");
  const [genre, setGenre] = useListState("all");
  const [query, setQuery] = useListState("");
  const [activeRange, setActiveRange] = useListState(0);
  const [add, setAdd] = useListState(null); // { album, intent }, drives the add-record modal
  const [storeNonce, forceStore] = useListState(0);
  const [commentCounts, setCommentCounts] = useListState({}); // { slug: count }

  // One query: pull every comment's slug, tally per album client-side. Avoids
  // 100 separate count queries for the list.
  useListEffect(() => {
    let active = true;
    if (window.BBR_supabase) {
      window.BBR_supabase
        .from("comments")
        .select("slug")
        .then(({ data }) => {
          if (!active || !data) return;
          const counts = {};
          for (const row of data) counts[row.slug] = (counts[row.slug] || 0) + 1;
          setCommentCounts(counts);
        });
    }
    return () => { active = false; };
  }, []);

  // Shared collection / wishlist store
  const store = window.BBR_store;
  useListEffect(() => (store ? store.subscribe(() => forceStore((n) => n + 1)) : undefined), []);
  const openAdd = (album, intent) => {
    if (onRequireAuth && !onRequireAuth(intent === "wishlist" ? "wishlist" : "collection")) return;
    setAdd({ album, intent });
  };
  const handleAdd = (rec) => {
    if (!store) return;
    if (add && add.intent === "wishlist") store.addToWishlist(rec);
    else store.addToCollection(rec);
    setAdd(null);
  };
  const toggleOwned = (a) => {
    if (!store) return;
    if (store.ownedSlug(a.slug)) store.removeCollectionBySlug(a.slug);
    else openAdd(a, "collection");
  };
  const toggleWished = (a) => {
    if (!store) return;
    if (store.wishedSlug(a.slug)) store.removeWishlistBySlug(a.slug);
    else openAdd(a, "wishlist");
  };

  const decades = useListMemo(() => ["all", ...window.DECADES], []);
  const genres = useListMemo(() => ["all", ...window.GENRES], []);
  const ranges = useListMemo(() => {
    const out = [];
    for (let s = 1; s <= albums.length; s += 10) out.push([s, Math.min(s + 9, albums.length)]);
    return out;
  }, [albums.length]);

  const q = query.trim().toLowerCase();
  const filtered = albums.filter((a) => {
    if (decade !== "all" && a.decade !== decade) return false;
    if (genre !== "all" && a.genre !== genre) return false;
    if (q) {
      const hay = (a.title + " " + a.artist + " " + a.genre + " " + a.year).toLowerCase();
      if (!hay.includes(q)) return false;
    }
    return true;
  });

  const unfiltered = decade === "all" && genre === "all" && !q;
  // How many of the ranked hundred are in the user's collection.
  const ownedCount = useListMemo(
    () => (store ? albums.filter((a) => store.ownedSlug(a.slug)).length : 0),
    [albums, storeNonce]
  );

  const toolbarOffset = () => {
    const tb = document.querySelector(".toolbar");
    return (tb ? tb.offsetHeight : 80) + 24;
  };
  const scrollToRank = (rank) => {
    const el = document.querySelector(`.entry[data-rank="${rank}"]`);
    if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - toolbarOffset(), behavior: "smooth" });
  };
  const jumpToRange = (start, end) => {
    const entries = [...document.querySelectorAll(".entry[data-rank]")];
    const inRange = entries.find((el) => { const r = +el.dataset.rank; return r >= start && r <= end; });
    const fallback = entries.find((el) => +el.dataset.rank >= start) || entries[entries.length - 1];
    const target = inRange || fallback;
    if (target) window.scrollTo({ top: target.getBoundingClientRect().top + window.scrollY - toolbarOffset(), behavior: "smooth" });
  };

  /* scroll-spy: highlight the range pill for the rank near the top of the viewport */
  useListEffect(() => {
    let raf = 0;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        raf = 0;
        const mark = toolbarOffset() + 8;
        const entries = [...document.querySelectorAll(".entry[data-rank]")];
        let current = entries[0];
        for (const el of entries) {
          if (el.getBoundingClientRect().top <= mark) current = el; else break;
        }
        if (current) setActiveRange(Math.floor((+current.dataset.rank - 1) / 10) * 10 + 1);
      });
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, [filtered.length]);

  const reset = () => { setDecade("all"); setGenre("all"); setQuery(""); };
  const shuffle = () => {
    if (!filtered.length) return;
    const pick = filtered[Math.floor(Math.random() * filtered.length)];
    scrollToRank(pick.rank);
  };

  return (
    <div className="countdown-page">
      <header className="page-head">
        <h1>The <em>Hundred</em><br />greatest<br />records.</h1>
        <p className="lede">A countdown built from critical consensus and a lot of second listens. Start at one hundred. Work your way down, every entry indexed, annotated, and defended.</p>
      </header>

      <div className="toolbar">
        <div className="tb-row tb-row--main">
          <div className="search-field">
            <svg className="search-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" />
            </svg>
            <input
              type="text"
              className="search-input"
              placeholder="Search the hundred…"
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              aria-label="Search albums by title, artist, genre or year"
            />
            {query && (
              <button className="search-clear" onClick={() => setQuery("")} aria-label="Clear search">×</button>
            )}
          </div>

          <div className="lp-filters">
            <div className="mc-select">
              <select value={decade} onChange={(e) => setDecade(e.target.value)} aria-label="Filter by decade">
                <option value="all">All decades</option>
                {window.DECADES.map((d) => <option key={d} value={d}>{d}</option>)}
              </select>
            </div>
            <div className="mc-select">
              <select value={genre} onChange={(e) => setGenre(e.target.value)} aria-label="Filter by genre">
                <option value="all">All genres</option>
                {window.GENRES.map((g) => <option key={g} value={g}>{g}</option>)}
              </select>
            </div>
          </div>

          <div className="right">
            <span className="count-label"><b>{filtered.length}</b> / {albums.length}</span>
            <button className="toolbar-btn ghost" onClick={shuffle}>⇄ Shuffle</button>
            {!unfiltered && <button className="toolbar-btn" onClick={reset}>Reset</button>}
          </div>
        </div>

        <div className="tb-row tb-row--jump">
          <label>Jump</label>
          <div className="range-pills">
            {ranges.map(([s, e]) => (
              <button
                key={s}
                className={"chip range-pill" + (unfiltered && activeRange === s ? " active" : "")}
                onClick={() => jumpToRange(s, e)}
              >{s}–{e}</button>
            ))}
          </div>
        </div>
      </div>

      <main className="list">
        <div className="fork">
          <div className="fork-head">
            <div className="kick">Your turn</div>
            <h3>Think we got it <em>wrong?</em></h3>
            <p>Two ways to weigh in — talk it out, or put an album forward.</p>
          </div>
          <div className="fork-cards">
            <button className="fork-card" onClick={() => { const el = document.getElementById("list-discussion"); if (el) el.scrollIntoView({ behavior: "smooth" }); }}>
              <span className="fork-ico" aria-hidden="true">
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M21 11.5a8.38 8.38 0 0 1-8.5 8.5 8.5 8.5 0 0 1-3.9-.9L3 21l1.9-5.6a8.5 8.5 0 0 1-.9-3.9A8.38 8.38 0 0 1 12.5 3 8.38 8.38 0 0 1 21 11.5z"/></svg>
              </span>
              <span className="fork-card-t">Comment on our list</span>
              <span className="fork-card-d">Argue the order, defend a snub.</span>
              <span className="fork-go">Join the discussion →</span>
            </button>
            <button className="fork-card" onClick={() => { const el = document.getElementById("nominations"); if (el) el.scrollIntoView({ behavior: "smooth" }); }}>
              <span className="fork-ico" aria-hidden="true">
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M12 5v14M5 12h14"/></svg>
              </span>
              <span className="fork-card-t">Nominate an album we missed</span>
              <span className="fork-card-d">Put one forward, vote the contenders up.</span>
              <span className="fork-go">See the ballot →</span>
            </button>
          </div>
        </div>
        {filtered.length === 0 && (
          <div style={{ padding: "80px 0", textAlign: "center", color: "var(--ink-mute)", fontFamily: "var(--serif)", fontStyle: "italic", fontSize: 22 }}>
            Nothing matches “{query || "that filter"}”. Try reset.
          </div>
        )}
        {filtered.map((a) => {
          const blurb = window.BLURBS[a.rank] || `${a.artist}'s ${a.year} statement, cut at ${a.studio.split(",")[0]} with ${a.producer}. A ${a.decade} landmark that still bends the room it plays in.`;
          const side = window.SIDENOTES[a.rank];
          const hasEssay = !!(window.ESSAYS_FULL && window.ESSAYS_FULL[a.slug]);
          const owned = store && store.ownedSlug(a.slug);
          const wished = store && store.wishedSlug(a.slug);
          const interjection = unfiltered ? INTERJECTIONS[a.rank] : null;
          return (
            <React.Fragment key={a.rank}>
              <article data-rank={a.rank} className={"entry" + (a.rank <= 3 ? " top3" : "")}>
                <h2 className="rank-num">{a.rank}</h2>
                <div className="cover-slot" onClick={() => onAlbum(a)}>
                  <Sleeve album={a} size={180} />
                </div>
                <div className="info">
                  <div className="kicker">{a.year} · {a.genre}</div>
                  <h3 onClick={() => onAlbum(a)}>{a.title}</h3>
                  <div className="artist-line">{a.artist}</div>
                  <Blurb text={blurb} />
                  <div className="info-actions">
                    <a
                      className={"read-more" + (hasEssay ? "" : " coming")}
                      href="#"
                      onClick={(e) => { e.preventDefault(); if (hasEssay) onAlbum(a); }}
                    >
                      {hasEssay ? "Read the essay" : "Essay, coming soon"}
                      {hasEssay && (
                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
                          <path d="M5 12h14M13 5l7 7-7 7" />
                        </svg>
                      )}
                    </a>
                    <span className="mono info-len">{a.length}</span>
                    {commentCounts[a.slug] > 0 && (
                      <button className="info-comments" onClick={() => onAlbum(a)} title={commentCounts[a.slug] + " comments"}>
                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true"><path d="M21 11.5a8.38 8.38 0 0 1-8.5 8.5 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.7a8.5 8.5 0 0 1-.9-3.8A8.38 8.38 0 0 1 12.5 3 8.38 8.38 0 0 1 21 11.5z"/></svg>
                        {commentCounts[a.slug]}
                      </button>
                    )}
                    <VinylBuy album={a} inline />
                  </div>
                  <div className="own-actions">
                    <button
                      className={"own-btn own" + (owned ? " active" : "")}
                      onClick={() => toggleOwned(a)}
                      title={owned ? "In your collection, click to remove" : "Add this to your collection"}
                    >
                      {owned ? (
                        <>
                          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4"><polyline points="4 12 10 18 20 6" /></svg>
                          In collection
                        </>
                      ) : (
                        <>
                          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 5v14M5 12h14" /></svg>
                          Own it
                        </>
                      )}
                    </button>
                    <button
                      className={"own-btn wish" + (wished ? " active" : "")}
                      onClick={() => toggleWished(a)}
                      title={wished ? "On your wishlist, click to remove" : "Add this to your wishlist"}
                    >
                      <svg viewBox="0 0 24 24" fill={wished ? "currentColor" : "none"} stroke="currentColor" strokeWidth="1.8">
                        <path d="M12 21C12 21 4 14.8 4 9.2 4 6.3 6.1 4.4 8.5 4.4 10.1 4.4 11.4 5.4 12 6.5 12.6 5.4 13.9 4.4 15.5 4.4 17.9 4.4 20 6.3 20 9.2 20 14.8 12 21 12 21Z" />
                      </svg>
                      {wished ? "Wishlisted" : "Wishlist"}
                    </button>
                  </div>
                  <div className="meta">
                    <span>{a.label}</span>
                    <span>{a.producer}</span>
                    <span>{a.country}</span>
                  </div>
                </div>
                <div className="side">
                  <VinylBuy album={a} />
                  <div className="side-stat">
                    <b>Studio</b>
                    <p>{a.studio}</p>
                  </div>
                  <div className="side-stat">
                    <b>Why it matters</b>
                    <p>{side || `${a.producer} produced. Released on ${a.label}.`}</p>
                  </div>
                </div>
              </article>
              {interjection && (
                <div className="interjection" aria-hidden="true">
                  <span>{interjection}</span>
                </div>
              )}
            </React.Fragment>
          );
        })}
      </main>

      <div className="progress">
        <span>In your collection</span>
        <div className="bar"><span style={{ width: (ownedCount / albums.length * 100) + "%" }} /></div>
        <span className="progress-count">{ownedCount} / {albums.length}</span>
      </div>

      {window.Nominations && <Nominations albums={albums} />}
      {window.ListDiscussion && <ListDiscussion />}

      {add && (
        <AddRecordFlow
          albums={albums}
          intent={add.intent}
          preset={{ album: add.album }}
          onAdd={handleAdd}
          onClose={() => setAdd(null)}
        />
      )}
    </div>
  );
}

window.ListPage = ListPage;
