/* global React, window */
// Worship Planner — Choral Anthems tab view.
// Renders window.ANTHEMS[sunday.id] (per-Sunday picks by reading) and
// window.ANTHEM_POOLS (Sunday-independent communion/praise pickers), parallel
// to hymns/contemporary/solo/handbells.
//
// Picks are ALREADY ranked strong→thematic→loose; we PRESERVE that order and
// only group by bucket for display (grouping keeps order, never re-sorts).
//
// Lectionary track: a passage may carry parallel columns (standard + semi/comp
// in the Propers, or a cct column on a few Easter-season Sundays). We resolve
// ONE column per passage against the effective track so the Propers never
// double-show. `general` columns are service-wide and always show.
//
// Display intents: owned pieces get a ★ badge + wash; anchor picks (must-program
// favorites) get a gold frame + corner flag; alleluia:"unknown" is caution-flagged
// on Lent Sundays only; service_tags → chips; merit → a quality chip;
// last_sung/times_sung → a recency pill (future date = already scheduled).
// Cards click-to-add into the service plan, same as hymns/contemporary.
//
// Exposed as window.AnthemsView; planner.jsx mounts it on the "anthems" tab.

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

  const SLOTS = ["ot", "psalm", "epistle", "gospel"];
  const BUCKET_LABEL = { strong: "Strong match", thematic: "Thematic connection", loose: "Loose connection" };

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

  const LENT_IDS = new Set([
    "ash-wednesday", "lent-1", "lent-2", "lent-3", "lent-4", "lent-5",
    "palm-passion", "palm-sunday", "passion-sunday", "maundy-thursday", "good-friday",
  ]);
  function isLentDay(sunday) {
    if (LENT_IDS.has(sunday.id)) return true;
    const s = sunday.season || "";
    return /^Lent/.test(s) || s === "Holy Week";
  }

  // ---- per-passage track resolution ----
  function effParts(effTrack) {
    const t = effTrack || "n/a";
    if (window.parseEffTrack) {
      try { return window.parseEffTrack(t); } catch (_) { /* fall through */ }
    }
    return {
      isCct: /cct/.test(t),
      lect: /semi/.test(t) ? "semi" : /comp/.test(t) ? "comp" : null,
    };
  }
  function colsForPassage(entry, passage, effTrack) {
    const all = (entry.columns || []).filter((c) => c.passage === passage);
    if (!all.length) return [];
    const { isCct, lect, alt } = effParts(effTrack);
    // UMC alternate psalm: when the toggle is on, the psalm column swaps to the
    // track:"alt" picks (and never double-shows with the RCL column).
    if (passage === "psalm" && alt) { const c = all.find((x) => x.track === "alt"); if (c) return [c]; }
    if (isCct) { const c = all.find((x) => x.track === "cct"); if (c) return [c]; }
    if (lect) { const c = all.find((x) => x.track === lect); if (c) return [c]; }
    const std = all.filter((x) => x.track === "standard" || !x.track);
    if (std.length) return std;
    return all.filter((x) => (window.trackAllows ? window.trackAllows(x.track, effTrack) : true));
  }
  function picksForPassage(entry, passage, effTrack) {
    return colsForPassage(entry, passage, effTrack).flatMap((c) =>
      (c.picks || []).map((p) => ({ ...p, ref: p.ref || c.ref })));
  }
  function bucketize(picks) {
    const b = { strong: [], thematic: [], loose: [] };
    for (const p of picks) (b[p.bucket] || b.loose).push(p);
    return b;
  }

  // ---- plan glue ----
  function planKeyFor(pick, passage) { return `anthem:${passage}:${slug(pick.title)}`; }
  function planHymnFor(pick) {
    const bits = [];
    if (pick.composer) bits.push(pick.composer);
    if (pick.voicing) bits.push(pick.voicing);
    const meta = bits.join(" · ");
    return { title: pick.title, note: [meta, pick.note || ""].filter(Boolean).join(" — "), anthem: true, _composer: pick.composer || "" };
  }

  // ---- vendor link ----
  function anthemUrl(p) {
    if (p.url) return p.url;
    if (p.pepper_id) return "https://www.jwpepper.com/" + encodeURIComponent(p.pepper_id) + ".item";
    const q = encodeURIComponent([p.title, (p.composer || "").replace(/,.*$/, ""), "choral anthem"].filter(Boolean).join(" "));
    return "https://www.jwpepper.com/sheet-music/search.jsp?keywords=" + q;
  }
  function anthemLinkLabel(p) {
    if (p.url && !/jwpepper/i.test(p.url)) return "Listen / order";
    if (p.url || p.pepper_id) return "JW Pepper";
    return "Find a copy";
  }

  // ---- recency ----
  const NOW = new Date();
  function recency(p) {
    if (!p.last_sung) return null;
    const d = new Date(p.last_sung + "T00:00:00");
    if (isNaN(d)) return null;
    const days = Math.round((d - NOW) / 86400000);
    if (days > 0) {
      const mo = Math.round(days / 30);
      return { future: true, label: mo >= 1 ? `Scheduled in ${mo} mo` : "Scheduled soon" };
    }
    const ago = -days;
    let rel;
    if (ago < 45) rel = "last month";
    else { const mo = Math.round(ago / 30); rel = mo < 12 ? `${mo} mo ago` : `${Math.round(ago / 365)} yr ago`; }
    const t = p.times_sung > 1 ? ` · ${p.times_sung}×` : "";
    return { future: false, label: `Sung ${rel}${t}` };
  }

  const MERIT_LABEL = { exceptional: "Exceptional", strong: "Notable", notable: "Notable" };

  // ---------- anthem card ----------
  function AnthemCard({ pick, passage, lentDay, isSelected, onToggle }) {
    const rec = recency(pick);
    const cautionAlleluia = lentDay && pick.alleluia === "unknown";
    return (
      <div className={`wp-an-card ${isSelected ? "is-selected" : ""} ${pick.owned ? "is-owned" : ""} ${pick.anchor ? "is-anchor" : ""}`}
        data-passage={passage} data-bucket={pick.bucket}>
        <button className="wp-an-check" onClick={onToggle}
          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-an-rail">
          <span className="wp-an-voicing">{pick.voicing || "—"}</span>
          {pick.instrumentation &&
            <span className="wp-an-instr">{pick.instrumentation}</span>}
        </div>

        <div className="wp-an-body">
          {pick.anchor &&
            <div className="wp-an-anchor-tag" title="A must-program favorite reserved for this Sunday">
              <svg width="9" height="9" viewBox="0 0 9 9" aria-hidden="true"><path d="M4.5 1 L4.5 8 M2 3 L7 3 M2.5 6 a2 2 0 0 0 4 0" stroke="currentColor" strokeWidth="0.9" fill="none" strokeLinecap="round" /></svg>
              Anchor
            </div>}
          <div className="wp-an-title">
            {pick.title}
            {pick.owned && <span className="wp-an-star" title="In the church's owned library">★</span>}
          </div>
          {pick.composer && <div className="wp-an-composer">{pick.composer}</div>}
          {pick.note && <p className="wp-an-note">{pick.note}</p>}

          <div className="wp-an-foot">
            {pick.owned && <span className="wp-an-chip is-owned">★ In our library</span>}
            {pick.merit && MERIT_LABEL[pick.merit] &&
              <span className="wp-an-chip is-merit" title="Standalone quality signal — best-seller / curated list">{MERIT_LABEL[pick.merit]}</span>}
            {(pick.service_tags || []).map((t) =>
              <span key={t} className={`wp-an-chip is-svc svc-${t}`}>{t}</span>)}
            {cautionAlleluia &&
              <span className="wp-an-chip is-caution" title="Text may contain Alleluia — set aside during Lent. Verify before programming.">⚠ Check Alleluia</span>}
            {rec &&
              <span className={`wp-an-chip is-rec ${rec.future ? "is-future" : ""}`}>{rec.label}</span>}
            {pick.publisher && <span className="wp-an-pub">{pick.publisher}</span>}
            <a className="wp-an-link" href={anthemUrl(pick)} target="_blank" rel="noopener noreferrer"
              title="Open a vendor page to hear a demo or order copies">
              {anthemLinkLabel(pick)}
              <svg width="9" height="9" viewBox="0 0 9 9" aria-hidden="true">
                <path d="M2.5 6.5 L6.5 2.5 M3.5 2.5 L6.5 2.5 L6.5 5.5" stroke="currentColor" strokeWidth="1" fill="none" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </a>
          </div>
        </div>
      </div>);
  }

  // ---------- reading column ----------
  // Cap the strong+thematic list at CAP cards; the rest reveal behind a
  // show-more (loose connections stay in their own collapsed disclosure).
  const CAP = 4;
  function AnthemColumn({ pkey, label, reading, picks, lentDay, selected, toggle }) {
    const [showLoose, setShowLoose] = useState(false);
    const [expanded, setExpanded] = useState(false);
    const b = bucketize(picks);
    const empty = picks.length === 0;

    const renderCard = (p) => {
      const k = planKeyFor(p, pkey);
      return <AnthemCard key={k} pick={p} passage={pkey} lentDay={lentDay}
        isSelected={selected.has(k)} onToggle={() => toggle(k, planHymnFor(p), pkey)} />;
    };

    // How many of strong / thematic to show when collapsed (strong first).
    const strongShown = expanded ? b.strong.length : Math.min(b.strong.length, CAP);
    const thematicShown = expanded ? b.thematic.length : Math.max(0, CAP - strongShown);
    const hidden = (b.strong.length - strongShown) + (b.thematic.length - thematicShown);

    return (
      <article className="wp-an-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>

        {empty ?
          <div className="wp-an-empty">
            <p className="wp-an-empty-text">
              {pkey === "psalm"
                ? "No anthem setting indexed for this psalm."
                : "No choral anthem indexed for this reading."}
            </p>
          </div> :
          <>
            {strongShown > 0 &&
              <div className="wp-bucket">
                <h4 className="wp-bucket-h">{BUCKET_LABEL.strong}</h4>
                <div className="wp-an-list">{b.strong.slice(0, strongShown).map(renderCard)}</div>
              </div>}
            {thematicShown > 0 &&
              <div className="wp-bucket">
                <h4 className="wp-bucket-h">{BUCKET_LABEL.thematic}</h4>
                <div className="wp-an-list">{b.thematic.slice(0, thematicShown).map(renderCard)}</div>
              </div>}
            {(hidden > 0 || expanded) && (b.strong.length + b.thematic.length) > CAP &&
              <button className="wp-disclosure wp-an-morebtn" onClick={() => setExpanded((v) => !v)}>
                <span className={`wp-disclosure-caret ${expanded ? "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>
                {expanded ? "Show fewer" : `Show ${hidden} more`}
                {!expanded && <span className="wp-disclosure-count">{b.strong.length + b.thematic.length}</span>}
              </button>}
            {b.loose.length > 0 &&
              <div className="wp-bucket wp-bucket-loose">
                <button className="wp-disclosure" onClick={() => setShowLoose((s) => !s)}>
                  <span className={`wp-disclosure-caret ${showLoose ? "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>
                  {showLoose ? "Hide loose connections" : "Loose connections"}
                  <span className="wp-disclosure-count">{b.loose.length}</span>
                  <span className="wp-disclosure-hint">mood / season fits, further afield</span>
                </button>
                {showLoose && <div className="wp-an-list">{b.loose.map(renderCard)}</div>}
              </div>}
          </>}
      </article>);
  }

  // ---------- general / service-wide strip ----------
  function AnthemGeneralStrip({ picks, lentDay, selected, toggle }) {
    if (!picks || picks.length === 0) return null;
    return (
      <section className="wp-an-whole">
        <div className="wp-an-whole-h">
          <span className="wp-an-whole-h-l">For the day</span>
          <span className="wp-an-whole-h-s">Service-wide &amp; seasonal anthems — not keyed to one reading</span>
        </div>
        <div className="wp-an-whole-grid">
          {picks.map((p) => {
            const k = planKeyFor(p, "general");
            return <AnthemCard key={k} pick={p} passage="whole" lentDay={lentDay}
              isSelected={selected.has(k)} onToggle={() => toggle(k, planHymnFor(p), "general")} />;
          })}
        </div>
      </section>);
  }

  // ---------- service pools (communion / praise) ----------
  // Handbell-shelf style: a curated opening mix (season-matched first, then an
  // owned+discovery interleave) plus a weighted "show me 5 more" that keeps
  // cycling through the pool, plus a reset.

  // Normalize the pool row: the generated data carries `scope`
  // (general|seasonal:<Season>) + `curation_note`; we read them as season/note.
  function normItem(raw, poolKey, i) {
    const scope = raw.scope || "";
    const season = raw.season || (/^seasonal:/.test(scope) ? scope.split(":")[1] : null);
    return {
      id: raw.id || `${poolKey[0]}${i}`,
      title: raw.title,
      composer: raw.composer || null,
      owned: !!raw.owned,
      season,
      voicing: raw.voicing || null,
      instrumentation: raw.instrumentation || null,
      publisher: raw.publisher || null,
      pepper_id: raw.pepper_id || null,
      url: raw.url || null,
      note: raw.curation_note || raw.note || null,
    };
  }

  function seasonMatches(sunday, season) {
    if (!season) return false;
    return (sunday.season || "").toLowerCase().includes(season.toLowerCase());
  }
  function sampleN(pool, k, excludeIds) {
    const bag = pool.filter((p) => !excludeIds.has(p.id));
    const out = [];
    for (let n = 0; n < k && bag.length; n++) {
      const idx = Math.floor(Math.random() * bag.length);
      out.push(bag[idx]);
      bag.splice(idx, 1);
    }
    return out;
  }
  function defaultMix(pool, sunday, n) {
    const seasonal = pool.filter((p) => seasonMatches(sunday, p.season));
    const seasonalIds = new Set(seasonal.map((p) => p.id));
    const owned = pool.filter((p) => p.owned && !seasonalIds.has(p.id));
    const disc = pool.filter((p) => !p.owned && !seasonalIds.has(p.id));
    const out = [...seasonal];
    let i = 0;
    while (out.length < n && (i < owned.length || i < disc.length)) {
      if (i < owned.length && out.length < n) out.push(owned[i]);
      if (i < disc.length && out.length < n) out.push(disc[i]);
      i++;
    }
    return out;
  }

  function PoolCard({ item, isSelected, onToggle }) {
    return (
      <div className={`wp-an-card wp-an-pool-card ${isSelected ? "is-selected" : ""} ${item.owned ? "is-owned" : ""}`}
        data-passage="whole">
        <button className="wp-an-check" onClick={onToggle}
          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-an-rail">
          <span className="wp-an-voicing">{item.voicing || "—"}</span>
          {item.instrumentation &&
            <span className="wp-an-instr">{item.instrumentation}</span>}
        </div>
        <div className="wp-an-body">
          <div className="wp-an-title">
            {item.title}
            {item.owned && <span className="wp-an-star" title="In the church's owned library">★</span>}
          </div>
          {item.composer && <div className="wp-an-composer">{item.composer}</div>}
          {item.note && <p className="wp-an-note">{item.note}</p>}
          <div className="wp-an-foot">
            {item.owned && <span className="wp-an-chip is-owned">★ In our library</span>}
            {item.season && <span className="wp-an-chip is-merit">{item.season}</span>}
            {item.publisher && <span className="wp-an-pub">{item.publisher}</span>}
            <a className="wp-an-link" href={anthemUrl(item)} target="_blank" rel="noopener noreferrer"
              title="Open a vendor page to hear a demo or order copies">
              {anthemLinkLabel(item)}
              <svg width="9" height="9" viewBox="0 0 9 9" aria-hidden="true">
                <path d="M2.5 6.5 L6.5 2.5 M3.5 2.5 L6.5 2.5 L6.5 5.5" stroke="currentColor" strokeWidth="1" fill="none" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </a>
          </div>
        </div>
      </div>);
  }

  function PoolShelf({ poolKey, label, sub, pool, sunday, selected, toggle }) {
    const N = 8;
    const [open, setOpen] = useState(false);
    const [revealed, setRevealed] = useState([]);
    useEffect(() => { setRevealed([]); }, [sunday.id]);

    if (!pool || pool.length === 0) return null;
    const def = defaultMix(pool, sunday, N);
    const shown = new Set([...def.map((p) => p.id), ...revealed.map((p) => p.id)]);
    const ownedN = pool.filter((p) => p.owned).length;

    const drawFive = () => {
      const five = sampleN(pool, 5, shown);
      if (five.length) setRevealed((prev) => [...prev, ...five]);
    };

    const planKey = (p) => `anthem:pool:${poolKey}:${p.id}`;
    const renderCard = (p) => {
      const k = planKey(p);
      return <PoolCard key={p.id} item={p}
        isSelected={selected.has(k)} onToggle={() => toggle(k, planHymnFor(p), "general")} />;
    };

    return (
      <section className={`wp-an-pool ${open ? "is-open" : ""}`} data-pool={poolKey}>
        <button className="wp-an-pool-h" onClick={() => setOpen((v) => !v)} aria-expanded={open}>
          <span className={`wp-disclosure-caret ${open ? "on" : ""}`}>
            <svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 2 L7 4.5 L2 7 Z" fill="currentColor" /></svg>
          </span>
          <span className="wp-an-pool-title">{label}</span>
          <span className="wp-an-pool-count">{pool.length}</span>
          <span className="wp-an-pool-sub">{sub} · ★ {ownedN} owned</span>
        </button>

        {open &&
          <div className="wp-an-pool-body">
            <div className="wp-an-whole-grid">
              {def.map(renderCard)}
              {revealed.length > 0 &&
                <div className="wp-an-pool-divider" aria-hidden="true">
                  <span>{revealed.length} more drawn from the pool</span>
                </div>}
              {revealed.map(renderCard)}
            </div>
            <div className="wp-an-pool-shuffle">
              <button className="wp-an-pool-shuffle-btn" onClick={drawFive}>
                <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>
                {revealed.length ? "Show me 5 more" : "Show me 5 more suggestions"}
              </button>
              <span className="wp-an-pool-shuffle-hint">
                random from the pool — press again to keep cycling
                {revealed.length > 0 &&
                  <button className="wp-an-pool-reset" onClick={() => setRevealed([])}>reset</button>}
              </span>
            </div>
          </div>}
      </section>);
  }

  function AnthemPools({ sunday, selected, toggle }) {
    const raw = window.ANTHEM_POOLS || {};
    const communion = (raw.communion || []).map((r, i) => normItem(r, "communion", i));
    const praise = (raw.praise || []).map((r, i) => normItem(r, "praise", i));
    if (!communion.length && !praise.length) return null;
    return (
      <section className="wp-an-pools">
        <div className="wp-an-pools-h">
          <span className="wp-an-pools-h-l">Service pools</span>
          <span className="wp-an-pools-h-s">
            Sunday-independent shelves — good general anthems to reach for any week
          </span>
        </div>
        <PoolShelf poolKey="communion" label="Communion" sub="anthems for the table"
          pool={communion} sunday={sunday} selected={selected} toggle={toggle} />
        <PoolShelf poolKey="praise" label="General praise" sub="anthems of praise &amp; thanks"
          pool={praise} sunday={sunday} selected={selected} toggle={toggle} />
      </section>);
  }

  function AnthemsView({ sunday, track, rclVersion, effTrack, hidden, selected, toggle }) {
    const used = hidden || new Set();
    const sel = selected || new Set();
    const tog = toggle || (() => {});
    const entry = (window.ANTHEMS || {})[sunday.id];
    const lentDay = isLentDay(sunday);
    const eff = effTrack || (window.effTrackFor ? window.effTrackFor(sunday.id, track, rclVersion) : "n/a");

    const hasPools = !!(window.ANTHEM_POOLS && ((window.ANTHEM_POOLS.communion || []).length || (window.ANTHEM_POOLS.praise || []).length));

    if (!entry) {
      return (
        <div className="wp-an">
          <section className="wp-an-empty" style={{ marginTop: 8 }}>
            <p className="wp-an-empty-text">No choral anthems have been indexed for {sunday.day} yet.</p>
          </section>
          <AnthemPools sunday={sunday} selected={sel} toggle={tog} />
        </div>);
    }

    const picks = {};
    for (const p of SLOTS) picks[p] = picksForPassage(entry, p, eff);
    const general = picksForPassage(entry, "general", eff);
    const totalReading = SLOTS.reduce((n, p) => n + picks[p].length, 0);

    const cols = SLOTS.filter((p) => {
      if (used.has(p)) return false;
      if (p === "psalm") return picks.psalm.length > 0;
      return true;
    });

    return (
      <div className="wp-an">
        {(totalReading > 0 || general.length > 0) ? <>
          <div className="wp-an-cols-h">
            <span>Choral anthems by reading</span>
            <span className="wp-an-cols-h-hint">
              Ranked strong → thematic → loose · ★ marks what the church owns
            </span>
          </div>
          <div className="wp-an-cols" data-cols={cols.length}>
            {cols.map((p) =>
              <AnthemColumn key={p} pkey={p} lentDay={lentDay}
                label={window.PASSAGE_LABELS[p]}
                reading={window.readingForTrack ? window.readingForTrack(sunday, p, eff) : sunday.readings[p]}
                picks={picks[p]} selected={sel} toggle={tog} />)}
          </div>
          <AnthemGeneralStrip picks={general} lentDay={lentDay} selected={sel} toggle={tog} />
        </> :
          <section className="wp-an-empty" style={{ marginTop: 8 }}>
            <p className="wp-an-empty-text">No choral anthem suggestions for {sunday.day} on this track.</p>
          </section>}

        <AnthemPools sunday={sunday} selected={sel} toggle={tog} />
      </div>);
  }

  window.AnthemsView = AnthemsView;
})();
