/* global React, window */
// Worship Planner — Handbells tab view.
// Renders window.HANDBELLS[sunday.id] (reading lessons + a seasonal "general"
// shelf) and window.HANDBELL_SHELVES (season repertoire, for the weighted
// "show me 5 more" refresh).
//
// Design: each piece is a card with a slim left rail showing its OCTAVE range
// (or bell count) + HMA difficulty level — the two things a director filters on
// first — over an octave/level filter bar. Owned pieces are boosted + badged;
// cards click-to-add into the service plan like hymns/anthems. The seasonal
// shelf offers a weighted-random draw with a reset.
//
// Exposed as window.HandbellsView; planner.jsx mounts it on the "handbells" tab.
// Props: { sunday, hidden, selected, toggle }

(function () {
  const { useState, useEffect } = React;

  const SLOTS = ["ot", "psalm", "epistle", "gospel"];
  const READING_VISIBLE = 6;

  const slug = (s) => (s || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 44);

  function weightedSample(pool, k, excludeIds) {
    const bag = pool.filter((p) => !excludeIds.has(p.id)).map((p) => ({ p, w: Math.max(p.weight || 0.01, 0.01) }));
    const out = [];
    for (let n = 0; n < k && bag.length; n++) {
      const total = bag.reduce((s, x) => s + x.w, 0);
      let r = Math.random() * total, i = 0;
      while (i < bag.length - 1 && r > bag[i].w) { r -= bag[i].w; i++; }
      out.push(bag[i].p);
      bag.splice(i, 1);
    }
    return out;
  }

  const isBells = (v) => /bell/i.test(v || "");

  // ---- plan glue ----
  function planKeyFor(piece, passage) { return `handbell:${passage}:${piece.id || slug(piece.title)}`; }
  function planHymnFor(piece) {
    const bits = [];
    if (piece.arranger) bits.push("arr. " + piece.arranger);
    if (piece.octaves) bits.push(isBells(piece.octaves) ? piece.octaves : piece.octaves + " oct");
    if (piece.difficulty) bits.push("Lv " + piece.difficulty);
    const meta = bits.join(" · ");
    return { title: piece.title, note: [meta, piece.note || ""].filter(Boolean).join(" — "), handbell: true };
  }

  // ---- filter matching ----
  function passesFilter(piece, octSel, lvlSel) {
    if (octSel.size && !octSel.has(piece.octaves || "—")) return false;
    if (lvlSel.size && !lvlSel.has(piece.difficulty || "—")) return false;
    return true;
  }

  // ---------- piece card ----------
  function PieceCard({ piece, slot, passage, selected, toggle }) {
    const k = planKeyFor(piece, passage);
    const isSelected = selected.has(k);
    const oct = piece.octaves;
    return (
      <div className={`wp-hb-card ${isSelected ? "is-selected" : ""} ${piece.owned ? "is-owned" : ""} ${slot === "discovery" ? "is-discovery" : ""}`}
        data-passage={passage}>
        <button className="wp-hb-check" onClick={() => toggle(k, planHymnFor(piece), passage)}
          aria-pressed={isSelected} title={isSelected ? "Remove from plan" : "Add to service plan"}>
          <svg width="14" height="14" viewBox="0 0 14 14">
            {isSelected ?
              <path d="M3 7 L6 10 L11 4" stroke="currentColor" strokeWidth="1.75" fill="none" /> :
              <circle cx="7" cy="7" r="5" stroke="currentColor" strokeWidth="1" fill="none" opacity="0.4" />}
          </svg>
        </button>

        <div className="wp-hb-rail">
          {oct ? (isBells(oct)
            ? <span className="wp-hb-bells">{oct}</span>
            : <><span className="wp-hb-oct">{oct}</span><span className="wp-hb-oct-u">oct</span></>)
            : <span className="wp-hb-bells">—</span>}
          {piece.difficulty
            ? <span className="wp-hb-lvl">Lv {piece.difficulty}</span>
            : <span className="wp-hb-lvl is-unknown">level n/a</span>}
        </div>

        <div className="wp-hb-body">
          <div className="wp-hb-title">
            {piece.title}
            {piece.owned && <span className="wp-hb-star" title="In the church's owned library">★</span>}
          </div>
          {piece.arranger && <div className="wp-hb-arr">arr. {piece.arranger}</div>}
          {piece.instrumentation &&
            <div className="wp-hb-instr">
              <span className="wp-hb-instr-plus" aria-hidden="true">+</span>
              {piece.instrumentation}
            </div>}
          {piece.note && <p className="wp-hb-note">{piece.note}</p>}
          <div className="wp-hb-foot">
            {piece.owned && <span className="wp-hb-chip is-owned">★ In our library</span>}
            {slot === "discovery" && <span className="wp-hb-chip is-disc">Discovery</span>}
            {piece.tag && piece.tag.toLowerCase() !== "owned" && slot !== "discovery" &&
              <span className="wp-hb-chip">{piece.tag}</span>}
            {piece.publisher && <span className="wp-hb-arr">{piece.publisher}</span>}
            {piece.url &&
              <a className="wp-hb-instr" style={{ marginLeft: "auto", textDecoration: "none" }}
                href={piece.url} target="_blank" rel="noopener noreferrer"
                title="Open the vendor listing to hear a demo or order copies">Listing ↗</a>}
          </div>
        </div>
      </div>);
  }

  // ---------- reading column ----------
  function ReadingColumn({ pkey, label, reading, pieces, octSel, lvlSel, selected, toggle }) {
    const [showOwned, setShowOwned] = useState(false);
    const [showMore, setShowMore] = useState(false);
    const filtered = pieces.filter((p) => passesFilter(p, octSel, lvlSel));
    const visible = filtered.filter((p) => p.display === "default" || p.display == null).slice(0, READING_VISIBLE);
    const moreOwned = filtered.filter((p) => p.display === "more_owned");
    const more = filtered.filter((p) => p.display === "more");
    const card = (p, i) => <PieceCard key={p.id + i} piece={p} passage={pkey} selected={selected} toggle={toggle} />;

    return (
      <article className="wp-hb-col" data-passage={pkey}>
        <section className="wp-passage" data-passage={pkey}>
          <div className="wp-passage-marker"><span className="wp-passage-tag">{label}</span></div>
          <div className="wp-passage-body">
            <div className="wp-passage-ref">{reading?.ref}</div>
            {reading?.theme && <p className="wp-passage-theme">{reading.theme}</p>}
          </div>
        </section>
        {filtered.length === 0 ?
          <div className="wp-hb-empty">
            <p className="wp-hb-empty-text">
              {pieces.length === 0 ? "No specific handbell suggestions for this reading."
                : "No pieces match the current octave / level filter."}
            </p>
          </div> :
          <>
            <div className="wp-hb-list">{visible.map(card)}</div>
            {moreOwned.length > 0 &&
              <button className="wp-disclosure wp-hb-disc is-owned" onClick={() => setShowOwned((v) => !v)}>
                <span className={`wp-disclosure-caret ${showOwned ? "on" : ""}`}>
                  <svg width="9" height="9" viewBox="0 0 9 9"><path d="M2 2 L7 4.5 L2 7 Z" fill="currentColor" /></svg>
                </span>
                {showOwned ? "Hide owned" : `More owned (${moreOwned.length})`}
              </button>}
            {showOwned && <div className="wp-hb-list wp-hb-sublist">{moreOwned.map(card)}</div>}
            {more.length > 0 &&
              <button className="wp-disclosure wp-hb-disc" onClick={() => setShowMore((v) => !v)}>
                <span className={`wp-disclosure-caret ${showMore ? "on" : ""}`}>
                  <svg width="9" height="9" viewBox="0 0 9 9"><path d="M2 2 L7 4.5 L2 7 Z" fill="currentColor" /></svg>
                </span>
                {showMore ? "Show fewer" : `Show ${more.length} more`}
                <span className="wp-disclosure-hint">further afield</span>
              </button>}
            {showMore && <div className="wp-hb-list wp-hb-sublist">{more.map(card)}</div>}
          </>}
      </article>);
  }

  // ---------- seasonal shelf ----------
  function SeasonalSection({ general, readingIds, octSel, lvlSel, selected, toggle }) {
    const [revealed, setRevealed] = useState([]);
    useEffect(() => { setRevealed([]); }, [general && general.shelf]);
    if (!general || !general.shelf) return null;
    const defaultPieces = (general.default || []).filter((p) => passesFilter(p, octSel, lvlSel));
    const shelf = (window.HANDBELL_SHELVES || {})[general.shelf] || [];
    const revealedShown = revealed.filter((p) => passesFilter(p, octSel, lvlSel));

    const showMore = () => {
      const shown = new Set([
        ...(general.default || []).map((p) => p.id),
        ...revealed.map((p) => p.id),
        ...readingIds,
      ]);
      const five = weightedSample(shelf, 5, shown);
      if (five.length) setRevealed((prev) => [...prev, ...five]);
    };

    return (
      <section className="wp-hb-seasonal">
        <div className="wp-hb-seasonal-h">
          <div className="wp-hb-seasonal-h-l">
            <span className="wp-hb-seasonal-title">For the day</span>
            <span className="wp-hb-shelf">{general.shelf}</span>
          </div>
          <span className="wp-hb-seasonal-sub">Seasonal repertoire — owned + discovery mix</span>
        </div>
        <div className="wp-hb-grid">
          {defaultPieces.map((p, i) => <PieceCard key={p.id + i} piece={p} slot={p.slot} passage="general" selected={selected} toggle={toggle} />)}
          {revealedShown.length > 0 &&
            <div className="wp-hb-reveal-divider" aria-hidden="true"><span>{revealedShown.length} more from the shelf</span></div>}
          {revealedShown.map((p, i) => <PieceCard key={"r" + p.id + i} piece={p} slot="discovery" passage="general" selected={selected} toggle={toggle} />)}
        </div>
        {shelf.length > 0 &&
          <div className="wp-hb-shuffle">
            <button className="wp-hb-shuffle-btn" onClick={showMore}>
              <svg width="13" height="13" viewBox="0 0 13 13" aria-hidden="true">
                <path d="M1.5 3 H7 a3.5 3.5 0 0 1 3.5 3.5 M11.5 10 H6 a3.5 3.5 0 0 1-3.5-3.5" stroke="currentColor" strokeWidth="1.2" fill="none" strokeLinecap="round" />
                <path d="M9 1.5 L11.5 3 L9 4.5 M4 8.5 L1.5 10 L4 11.5" stroke="currentColor" strokeWidth="1.2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              Show me 5 more suggestions
            </button>
            <span className="wp-hb-shuffle-hint">
              weighted shuffle of the {general.shelf} shelf ({shelf.length})
              {revealed.length > 0 && <button className="wp-hb-shuffle-reset" onClick={() => setRevealed([])}>reset</button>}
            </span>
          </div>}
      </section>);
  }

  // ---------- octave / level filter bar ----------
  function FilterBar({ octaves, levels, octSel, lvlSel, setOctSel, setLvlSel }) {
    if (octaves.length <= 1 && levels.length <= 1) return null;
    const chip = (val, sel, setSel) => {
      const on = sel.has(val);
      return (
        <button key={val} className={`wp-hb-chk ${on ? "on" : ""}`}
          onClick={() => setSel((prev) => { const n = new Set(prev); n.has(val) ? n.delete(val) : n.add(val); return n; })}>
          <span className="wp-hb-chk-box" aria-hidden="true" />
          {val}
        </button>);
    };
    const anyOn = octSel.size || lvlSel.size;
    return (
      <div className="wp-hb-filters">
        {octaves.length > 1 &&
          <div className="wp-hb-filter-row">
            <span className="wp-hb-filter-label">Octaves</span>
            <div className="wp-hb-filter-set">{octaves.map((o) => chip(o, octSel, setOctSel))}</div>
          </div>}
        {levels.length > 1 &&
          <div className="wp-hb-filter-row">
            <span className="wp-hb-filter-label">Level</span>
            <div className="wp-hb-filter-set">{levels.map((l) => chip(l, lvlSel, setLvlSel))}</div>
            {anyOn ?
              <button className="wp-hb-filter-clear" onClick={() => { setOctSel(new Set()); setLvlSel(new Set()); }}>Clear</button> : null}
          </div>}
        {levels.length <= 1 && anyOn ?
          <button className="wp-hb-filter-clear" onClick={() => { setOctSel(new Set()); setLvlSel(new Set()); }}>Clear</button> : null}
      </div>);
  }

  function HandbellsView({ sunday, hidden, selected, toggle, effTrack }) {
    const used = hidden || new Set();
    const sel = selected || new Set();
    const tog = toggle || (() => {});
    const readingOf = (p) => (window.readingForTrack ? window.readingForTrack(sunday, p, effTrack) : sunday.readings[p]);
    const psalmAlt = !!(window.parseEffTrack && window.parseEffTrack(effTrack).alt);
    const piecesFor = (p) => (p === "psalm" && psalmAlt) ? (entry.psalm_alt || []) : (entry[p] || []);
    const entry = (window.HANDBELLS || {})[sunday.id];
    const [octSel, setOctSel] = useState(() => new Set());
    const [lvlSel, setLvlSel] = useState(() => new Set());
    useEffect(() => { setOctSel(new Set()); setLvlSel(new Set()); }, [sunday.id]);

    if (!entry) {
      return (
        <section className="wp-hb-empty" style={{ marginTop: 8 }}>
          <p className="wp-hb-empty-text">No handbell suggestions have been indexed for {sunday.day} yet.</p>
        </section>);
    }

    const total = SLOTS.reduce((n, p) => n + ((entry[p] || []).length), 0);
    const readingIds = SLOTS.flatMap((p) => (entry[p] || []).map((x) => x.id));
    const cols = SLOTS.filter((p) => !used.has(p));

    if (total === 0 && (!entry.general || !entry.general.shelf)) {
      return (
        <section className="wp-hb-empty" style={{ marginTop: 8 }}>
          <p className="wp-hb-empty-text">No specific handbell suggestions for {sunday.day} — the hymn planner never keyed this service.</p>
        </section>);
    }

    // Filter options: distinct octave + level values present this Sunday.
    const allPieces = [...SLOTS.flatMap((p) => entry[p] || []), ...((entry.general && entry.general.default) || [])];
    const octaves = [...new Set(allPieces.map((p) => p.octaves).filter(Boolean))]
      .sort((a, b) => (isBells(a) - isBells(b)) || a.localeCompare(b, undefined, { numeric: true }));
    const levels = [...new Set(allPieces.map((p) => p.difficulty).filter(Boolean))]
      .sort((a, b) => String(a).localeCompare(String(b), undefined, { numeric: true }));

    return (
      <div className="wp-hb">
        <FilterBar octaves={octaves} levels={levels}
          octSel={octSel} lvlSel={lvlSel} setOctSel={setOctSel} setLvlSel={setLvlSel} />

        {total > 0 && <>
          <div className="wp-hb-cols-h">
            <span>Handbells by reading</span>
            <span className="wp-hb-cols-h-hint">Ranked by fit — owned pieces boosted</span>
          </div>
          <div className="wp-hb-cols" data-cols={cols.length}>
            {cols.map((p) =>
              <ReadingColumn key={p} pkey={p} label={window.PASSAGE_LABELS[p]}
                reading={readingOf(p)} pieces={piecesFor(p)}
                octSel={octSel} lvlSel={lvlSel} selected={sel} toggle={tog} />)}
          </div>
        </>}

        <SeasonalSection general={entry.general} readingIds={readingIds}
          octSel={octSel} lvlSel={lvlSel} selected={sel} toggle={tog} />
      </div>);
  }

  window.HandbellsView = HandbellsView;
})();
