/* global React, window */
// =========================================================
// AudienceShelf — audience selection, moved to the front of the flow.
//
// Organised the way the business thinks (account, category, market), NOT the
// way our evidence is sourced. Provenance — do we already know these people —
// is a BADGE on each card, read off the same coverage model the panel mix uses.
// So "never asked them" is a state a segment is in, not a shelf you browse.
// =========================================================
const { useState: useStateAS, useMemo: useMemoAS } = React;

// Coverage history for the shelf's own segments. Merged into the Framework's
// table so one function answers "what do we know about these people?" everywhere.
Object.assign(window.FW.SEGMENT_HISTORY, {
  "rd shopper": { studies: 9, responses: 5400, since: 2021 },
  "retailer a platform buyer": { studies: 7, responses: 4100, since: 2022 },
  "retailer b co-op member": { studies: 9, responses: 5300, since: 2021 },
  "retailer c full-price shopper": { studies: 3, responses: 1750, since: 2024 },
  "amazon-first buyer": { studies: 2, responses: 980, since: 2025 },
  "off-price shopper": { studies: 0, responses: 0 },
  "fashion-forward 25–34": { studies: 4, responses: 2700, since: 2023 },
  "conservative 35–55 buyer": { studies: 5, responses: 3100, since: 2022 },
  "north american consumer": { studies: 12, responses: 6100, since: 2021 },
  "uk & ireland consumer": { studies: 6, responses: 3400, since: 2022 },
  "dach consumer": { studies: 5, responses: 2900, since: 2022 },
  "japan premium shopper": { studies: 1, responses: 480, since: 2025 },
  "nordics consumer": { studies: 0, responses: 0 },
  "retailer a sell-in audience": { studies: 6, responses: 3900, since: 2023 },
  "retailer b footwear sell-in audience": { studies: 5, responses: 3300, since: 2023 },
  "dtc core + lapsed": { studies: 8, responses: 4700, since: 2022 }
});

// Recently used sits OUTSIDE the category rail, not inside it: the audiences a
// team reaches for again are spread across categories, so filtering them by the
// category you happen to have selected hides the ones you'd actually reuse.
// Each recent card names its own category instead.
const RECENT = { id: "recent", label: "Recently used", note: "Audiences your team has fielded in the last few months, across every category. Picking one sets its category too." };

// Markets leads: where you're asking scopes everything under it.
const GROUPS = [
  { id: "market", label: "Markets", tab: "Markets", note: "Geography as an audience, not a filter." },
  { id: "account", label: "Accounts & retailers", tab: "Accounts", note: "Segments built around a specific retail partner's shopper." },
  { id: "category", label: "Brand perception", tab: "Brand perception", note: "How this category divides, regardless of where they buy." }
];

// Every segment is a bundle: a name a stakeholder recognises, and the criteria
// underneath it. The criteria are the double-click, never the headline.
const SEGMENTS = [
  { id: "rd", group: "account", name: "RD shopper", mark: "RD", color: "#0f9b8e",
    desc: "Bought at Retailer D in this category in the last 12 months. Skews younger and more price-aware than our direct buyer.",
    criteria: ["Shopped Retailer D · 12mo", "18–44", "US", "Category buyer"], cats: ["Running Footwear", "Men's Footwear", "Women's Footwear"] },
  { id: "retailer a", group: "account", name: "Retailer A platform buyer", mark: "ZAL", color: "#6d4cd9",
    desc: "Buys footwear on the platform several times a year; browses by brand tile and sale.",
    criteria: ["Retailer A · 12mo", "18–44", "DE / NL / SE", "2+ orders"], cats: ["Women's Lifestyle", "Running Footwear", "Men's Footwear"] },
  { id: "retailer b", group: "account", name: "Retailer B co-op member", mark: "Retailer B", color: "#3a8518",
    desc: "Shops Retailer B first for anything outdoors; dividend-driven and gear-literate.",
    criteria: ["Co-op member", "25–54", "US", "Outdoor footwear · 18mo"], cats: ["Running Footwear", "Men's Footwear"] },
  { id: "retailer c", group: "account", name: "Retailer C full-price shopper", mark: "NOR", color: "#d6563a",
    desc: "Buys at full price in department retail; judges the brand by who it sits next to on the floor.",
    criteria: ["Retailer C · 12mo", "28–55", "US", "$125k+ household"], cats: ["Women's Lifestyle", "Women's Footwear", "Accessories"] },
  { id: "amazon", group: "account", name: "Amazon-first buyer", mark: "AMZ", color: "#8c5800",
    desc: "Starts every search on the marketplace; brand matters less than review count and delivery date.",
    criteria: ["Marketplace-first", "25–54", "US / UK", "Reviews decide"], cats: ["Running Footwear", "Men's Footwear", "Accessories"] },
  { id: "offprice", group: "account", name: "Off-price shopper", mark: "OFF", color: "#0070eb",
    desc: "Shops TJX, outlet and end-of-season. Sees the range a year late and at a different number.",
    criteria: ["Off-price · 12mo", "25–54", "US", "Markdown-led"], cats: ["Women's Lifestyle", "Men's Footwear", "Running Footwear"] },

  { id: "loyalist", group: "category", name: "Core loyalist", mark: "CL", color: "#6d4cd9",
    desc: "Buys the franchise every season; knows the line better than most of the team.",
    criteria: ["Owns 2+ pairs", "Bought · 12mo", "Any market"], cats: ["*"] },
  { id: "switcher", group: "category", name: "Trend-led switcher", mark: "TS", color: "#d6563a",
    desc: "Follows silhouettes over brands; buys into whatever reads new.",
    criteria: ["Bought 3+ brands · 24mo", "18–34", "Social-led discovery"], cats: ["*"] },
  { id: "runner", group: "category", name: "Everyday runner", mark: "ER", color: "#0f9b8e",
    desc: "Runs three or four times a week without racing. Buys on fit and durability, replaces on a schedule.",
    criteria: ["Runs 3×/wk", "25–54", "2+ pairs a year"], cats: ["Running Footwear", "Men's Footwear", "Women's Footwear"] },
  { id: "racer", group: "category", name: "Weekend racer", mark: "WR", color: "#0070eb",
    desc: "Trains toward events. Specification-literate and the first to notice a midsole change.",
    criteria: ["Races 2+/yr", "25–44", "Specialty retail"], cats: ["Running Footwear"] },
  { id: "ff2534", group: "category", name: "Fashion-forward 25–34", mark: "FF", color: "#d69d2b",
    desc: "Reads the category as fashion first. Buys early, pays full price, moves on fast.",
    criteria: ["25–34", "Full-price buyer", "Social-led discovery"], cats: ["Women's Lifestyle", "Women's Footwear", "Accessories", "Graphic Tees"] },
  { id: "cons3555", group: "category", name: "Conservative 35–55 buyer", mark: "CB", color: "#3a8518",
    desc: "Buys less often and keeps things longer. Wants the version of the trend that will still look right next year.",
    criteria: ["35–55", "$75k+ household", "1–2 purchases/yr"], cats: ["Women's Lifestyle", "Men's Outerwear", "Men's Bottoms"] },
  { id: "value", group: "category", name: "Value seeker", mark: "VS", color: "#0070eb",
    desc: "Waits for markdown or shops off-price; price decides the call.",
    criteria: ["Markdown-led", "Any age", "Price-first"], cats: ["*"] },
  { id: "lapsed", group: "category", name: "Lapsed customer", mark: "LC", color: "#8c5800",
    desc: "Owned the brand once, moved to a competitor — the reason why is the finding.",
    criteria: ["No purchase · 24mo", "Owns a competitor", "Any market"], cats: ["*"] },

  { id: "na", group: "market", name: "North American consumer", mark: "NA", color: "#3a8518",
    desc: "US and Canada, balanced to the market on age, region and household income.",
    criteria: ["US / CA", "Census-balanced", "Category buyer"], cats: ["*"] },
  { id: "uki", group: "market", name: "UK & Ireland consumer", mark: "UK", color: "#6d4cd9",
    desc: "Balanced across the UK and Ireland; reads high-street and online retail differently to the US.",
    criteria: ["UK / IE", "Census-balanced", "Category buyer"], cats: ["*"] },
  { id: "dach", group: "market", name: "DACH consumer", mark: "DE", color: "#0070eb",
    desc: "Germany, Austria and Switzerland. Specification and returns policy weigh heavier here.",
    criteria: ["DE / AT / CH", "Census-balanced", "Category buyer"], cats: ["*"] },
  { id: "jp", group: "market", name: "Japan premium shopper", mark: "JP", color: "#d6563a",
    desc: "Premium-tier buyer in Japan. Hard to field at income quota — worth knowing before you commit.",
    criteria: ["JP", "¥8M+ household", "Premium retail"], cats: ["*"] },
  { id: "nordics", group: "market", name: "Nordics consumer", mark: "SE", color: "#8c5800",
    desc: "Sweden, Denmark and Norway. We have no history here yet.",
    criteria: ["SE / DK / NO", "Census-balanced", "Category buyer"], cats: ["*"] }
];

// Recently used is a flat list of the segments themselves — not saved bundles.
// Each entry points at a real segment, so the card, its coverage and its selected
// state are the same object the category view shows; only the recency is extra.
// Only groups with history of their own can appear here: a group we've never
// asked can't be one we recently fielded.
const RECENTS = [
  { id: "retailer a", cat: "Women's Lifestyle", used: "Used 4 weeks ago" },
  { id: "ff2534", cat: "Women's Lifestyle", used: "Used 4 weeks ago" },
  { id: "retailer c", cat: "Women's Lifestyle", used: "Used 4 weeks ago" },
  { id: "retailer b", cat: "Running Footwear", used: "Used in March" },
  { id: "rd", cat: "Running Footwear", used: "Used in March" },
  { id: "na", cat: "Running Footwear", used: "Used in March" },
  { id: "cons3555", cat: "Men's Footwear", used: "Used last week" },
  { id: "uki", cat: "Men's Footwear", used: "Used last week" }
];

const segById = (id) => SEGMENTS.find(s => s.id === id) || null;
// A drafted profile and a shelf segment are the same thing to everything
// downstream, so there's one shape and one converter.
const PALETTE = ["#0f9b8e", "#6d4cd9", "#d6563a", "#3a8518", "#0070eb", "#8c5800"];
function asSegment(p, i, covName) {
  if (p && p.mark && p.id) return p;
  const name = (p && p.name) || "Audience segment";
  const mark = name.split(/[\s&]+/).filter(Boolean).slice(0, 2).map(w => w[0]).join("").toUpperCase() || "AS";
  return { id: "draft-" + name.toLowerCase().replace(/[^a-z0-9]+/g, "-"), name, mark,
    // covName: the group this profile was drafted against, so its history
    // resolves even when the brand's own language for it is different.
    covName: covName || (p && p.covName) || name,
    color: (p && p.color) || PALETTE[(i || 0) % PALETTE.length], group: "draft",
    desc: (p && p.desc) || "Drawn from your objective.",
    criteria: (p && p.criteria) || ["From the brief"] };
}
// One place decides how a segment reads on trust, so the shelf badge, the
// audience card and the calculator line item can never disagree.
function segCoverage(seg) {
  const c = window.FW.coverageOf(seg.covName || seg.name);
  return { ...c, needsRecruiting: c.strength <= 15 };
}

function CovBadge({ seg, compact }) {
  const c = segCoverage(seg);
  return (
    <span className={"as-cov" + (compact ? " as-cov--sm" : "")} style={{ color: c.tone }} title={window.FW.coverageLine(seg.covName || seg.name)}>
      <i style={{ background: c.tone, opacity: c.strength <= 15 ? 0.3 : 1 }} />
      {c.needsRecruiting ? "Needs recruiting" : c.label}
    </span>
  );
}

function SegCard({ seg, on, onToggle, hideCat }) {
  return (
    <button type="button" className={"as-card" + (on ? " is-on" : "")} onClick={() => onToggle(seg)} aria-pressed={on}>
      <span className="as-card__hd">
        <span className="as-card__mark" style={{ background: seg.color }}>{seg.mark}</span>
        <span className="as-card__name">{seg.name}</span>
        <span className="as-card__tick" aria-hidden="true">
          {on && <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>}
        </span>
      </span>
      <CovBadge seg={seg} />
      {/* Criteria chips last: their count and length vary per segment, so anything
          below them would sit at a different height on every card. */}
      <span className="as-card__crit">
        {seg.cat && !hideCat && <b className="as-card__cat">{seg.cat}</b>}
        {seg.criteria.map(c => <i key={c}>{c}</i>)}
      </span>
    </button>
  );
}

// The 20% path. One screen: say it in words, or assemble it from criteria.
const CUSTOM_CRITERIA = {
  "Age": ["18–24", "25–34", "35–44", "45–54", "55+"],
  "Market": ["US", "Canada", "UK", "Germany", "France", "Japan", "Australia"],
  "Where they buy": ["Brand.com", "Specialty", "Department store", "Marketplace", "Off-price"],
  "Behavior": ["Bought us · 12mo", "Owns a competitor", "Lapsed 24mo+", "Full-price buyer", "Markdown-led"]
};
function CustomPane({ onAdd }) {
  const [text, setText] = useStateAS("");
  const [picked, setPicked] = useStateAS([]);
  const [name, setName] = useStateAS("");
  const toggle = (v) => setPicked(p => p.includes(v) ? p.filter(x => x !== v) : [...p, v]);
  const ready = name.trim() && (text.trim() || picked.length);
  return (
    <div className="as-custom">
      <div className="as-custom__lead">
        Describe them the way you'd say it out loud. We turn it into panel criteria and a question bank —
        that translation is the part CSMs do by hand today.
      </div>
      <label className="as-fld">
        <span className="as-fld__l">What do you call them?</span>
        <input className="as-fld__in" value={name} placeholder="e.g. Our lapsed premium buyer" onChange={e => setName(e.target.value)} />
      </label>
      <label className="as-fld">
        <span className="as-fld__l">Who are they?</span>
        <textarea className="as-fld__ta" rows={4} value={text} placeholder="e.g. Women 30–45 in the US who bought us before 2024, shop Retailer C or Retailer G, and have since moved to a competitor at the same price point." onChange={e => setText(e.target.value)} />
      </label>
      <div className="as-fld__l" style={{ marginBottom: 8 }}>Or pin the criteria you know</div>
      <div className="as-critgrid">
        {Object.keys(CUSTOM_CRITERIA).map(k => (
          <div key={k} className="as-critgrp">
            <div className="as-critgrp__l">{k}</div>
            <div className="as-critgrp__c">
              {CUSTOM_CRITERIA[k].map(v => (
                <button key={v} type="button" className={"as-chip" + (picked.includes(v) ? " is-on" : "")} onClick={() => toggle(v)}>{v}</button>
              ))}
            </div>
          </div>
        ))}
      </div>
      <div className="as-custom__foot">
        <span>{picked.length ? picked.length + " criteria pinned" : "No criteria pinned yet"}</span>
        <button type="button" className="as-primary" disabled={!ready} onClick={() => {
          onAdd({ id: "custom-" + Date.now(), group: "custom", name: name.trim(), mark: "＋", color: "#636c79",
            desc: text.trim() || "Built from " + picked.length + " pinned criteria.",
            criteria: picked.length ? picked.slice(0, 4) : ["Described in language"], custom: true });
          setText(""); setPicked([]); setName("");
        }}>Add this segment</button>
      </div>
    </div>
  );
}

// What the brand sells decides who is qualified to answer and what our twins
// can speak to — so category isn't a filter applied after the fact, it's the
// first move. Each entry carries the honest depth of our history in it.
const CATEGORY_META = {
  "Women's Lifestyle": { studies: 14, note: "Color, material and silhouette questions read cleanly here." },
  "Women's Footwear": { studies: 9, note: "Fit and comfort language is well calibrated in this category." },
  "Men's Footwear": { studies: 11, note: "Strong history on price tier and channel." },
  "Running Footwear": { studies: 12, note: "Our deepest category — performance claims are backtested." },
  "Men's Outerwear": { studies: 6, note: "Seasonal and technical questions supported; color is thinner." },
  "Men's Bottoms": { studies: 5, note: "Fit and sizing supported. Fewer runs on styling." },
  "Graphic Tees": { studies: 3, note: "Limited history — expect more real people in the mix." },
  "Accessories": { studies: 4, note: "Gifting and occasion questions supported; sizing isn't relevant." }
};

// A recently used segment is the same segment wherever it appears — so inside its
// category it keeps its own card and just carries the recency as one more chip.
const withUsed = (s, cat) => {
  const r = RECENTS.find(x => x.id === s.id && x.cat === cat);
  return r ? { ...s, criteria: [...s.criteria, r.used] } : s;
};

function AudienceShelf({ open, category, onCategory, selected, onChange, onClose, inline }) {
  // mode: the rail's two parallel sections — "recent" (across categories) and
  // "cat" (one category, browsed by group tabs).
  const [mode, setMode] = useStateAS("recent");
  const [q, setQ] = useStateAS("");
  const [custom, setCustom] = useStateAS([]);
  if (!open) return null;
  const all = [...SEGMENTS, ...custom];
  const matchQ = (s) => !q || (s.name + " " + s.desc + " " + (s.cat || "")).toLowerCase().includes(q.toLowerCase());
  const inCategory = (s) => !category || s.cats == null || s.cats.includes("*") || s.cats.includes(category);
  // Every kind of audience for this category on one page, each under its own
  // headline. A tab per kind hid two thirds of the answer behind a click and
  // made "what could I ask?" a thing you had to go looking for.
  const sections = GROUPS.map(g => ({ g, items: all.filter(s => s.group === g.id).filter(inCategory).filter(matchQ).map(s => withUsed(s, category)) })).filter(s => s.items.length);
  const recents = RECENTS.map(r => { const s = segById(r.id); return s ? { ...s, cat: r.cat, criteria: [...s.criteria, r.used] } : null; })
    .filter(s => s && !segCoverage(s).needsRecruiting).filter(matchQ);
  // Recents are grouped by the category they were fielded in, so the same
  // stacked-section reading applies here as inside a category.
  const recentByCat = (window.SHELF_CATEGORIES || []).map(cat => ({ cat, items: recents.filter(s => s.cat === cat) })).filter(g => g.items.length);
  const selIds = new Set(selected.map(s => s.id));
  // A study is about one category, so the selection belongs to it: switching
  // category clears what was picked under the previous one rather than carrying
  // segments that don't qualify into the new study.
  const toggle = (seg) => onChange(prev => (prev || []).some(s => s.id === seg.id) ? prev.filter(s => s.id !== seg.id) : [...(prev || []), seg]);
  // A recent carries its own category, so reusing one answers both questions. If
  // that category isn't the current one, the switch applies here too — the recent
  // becomes the selection rather than joining a set from somewhere else.
  const toggleRecent = (seg) => {
    if (selIds.has(seg.id)) return toggle(seg);
    if (seg.cat && seg.cat !== category) { onCategory(seg.cat); onChange([seg]); return; }
    toggle(seg);
  };
  const anyThin = selected.some(s => segCoverage(s).needsRecruiting);
  const countFor = (c) => all.filter(s => s.group !== "custom" && (s.cats == null || s.cats.includes("*") || s.cats.includes(c))).length;
  const pickCategory = (c) => { setMode("cat"); if (c !== category) onChange([]); onCategory(c); };
  return (
    <div className={inline ? "as-inline" : "as-veil"} role={inline ? "group" : "dialog"} aria-label="Choose your audience">
      <div className={"as-modal" + (inline ? " as-modal--inline" : "")}>
        <div className="as-hd">
          <div>
            <div className="as-hd__ey">Audience</div>
            <div className="as-hd__t">Who should answer?</div>
          </div>
          {(category || mode !== "cat") && (
            <div className="as-hd__search">
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><circle cx="11" cy="11" r="7" /><line x1="20" y1="20" x2="16.6" y2="16.6" /></svg>
              <input value={q} placeholder="Search segments…" onChange={e => setQ(e.target.value)} />
            </div>
          )}
          {inline ? (
            <button className="as-back" onClick={onClose} style={(category || mode !== "cat") ? null : { marginLeft: "auto" }}>Done</button>
          ) : (
            <button className="as-x" onClick={onClose} aria-label="Close" style={(category || mode !== "cat") ? null : { marginLeft: "auto" }}>
              <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><line x1="6" y1="6" x2="18" y2="18" /><line x1="18" y1="6" x2="6" y2="18" /></svg>
            </button>
          )}
        </div>
        <div className="as-body">
          <div className="as-rail">
            <button className={"as-catrow as-catrow--recent" + (mode === "recent" ? " is-on" : "")} onClick={() => setMode("recent")}>
              <span className="as-catrow__n">{RECENT.label}</span>
              <span className="as-catrow__b">{recents.length}</span>
            </button>
            <div className="as-rail__h as-rail__h--sp">Category</div>
            {(window.SHELF_CATEGORIES || []).map(c => (
              <button key={c} className={"as-catrow" + (mode === "cat" && c === category ? " is-on" : "")} onClick={() => pickCategory(c)}>
                <span className="as-catrow__n">{c}</span>
                <span className="as-catrow__b">{countFor(c)}</span>
              </button>
            ))}
            <button className={"as-catrow as-catrow--own" + (mode === "custom" ? " is-on" : "")} onClick={() => setMode("custom")}>
              <span className="as-catrow__n">Build your own</span>
              <span className="as-catrow__b">{custom.length || "＋"}</span>
            </button>
          </div>
          <div className="as-main">
            {mode === "custom" ? (
              <>
                {!!custom.length && (
                  <div className="as-sec">
                    <div className="as-sec__h">Your own segments</div>
                    <div className="as-grid">{custom.map(s => <SegCard key={s.id} seg={s} on={selIds.has(s.id)} onToggle={toggle} />)}</div>
                  </div>
                )}
                <CustomPane onAdd={(seg) => { setCustom(c => [...c, seg]); onChange(prev => [...(prev || []), seg]); }} />
              </>
            ) : mode === "recent" ? (
              <>
                {recentByCat.map(({ cat, items }) => (
                  <div key={cat} className="as-sec">
                    <div className="as-sec__h">{cat}</div>
                    <div className="as-grid">
                      {items.map(s => <SegCard key={s.id} seg={s} on={selIds.has(s.id)} onToggle={toggleRecent} hideCat />)}
                    </div>
                  </div>
                ))}
                {!recents.length && <div className="as-empty">Nothing matches “{q}” in your recent audiences.</div>}
              </>
            ) : !category ? (
              <div className="as-pick">
                <div className="as-pick__t">Start with the category</div>
                <div className="as-pick__d">Pick what this study is about on the left. The groups qualified to answer for it appear here.</div>
              </div>
            ) : (
              <>
                {anyThin && (
                  <div className="as-thin">
                    One of your picks is a group we've never asked. They can still be in the study — we recruit real people for that group, which adds time and cost. The calculator shows both.
                  </div>
                )}
                {sections.map(({ g, items }) => (
                  <div key={g.id} className="as-sec">
                    <div className="as-sec__h">{g.label}</div>
                    <div className="as-grid">{items.map(s => <SegCard key={s.id} seg={s} on={selIds.has(s.id)} onToggle={toggle} />)}</div>
                  </div>
                ))}
                {!sections.length && <div className="as-empty">Nothing here for {category} yet. Build your own segment on the left.</div>}
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

// Resolved recents, for surfaces outside the shelf (the composer tray).
const recentSegments = () => RECENTS.map(r => { const s = segById(r.id); return s ? { ...s, cat: r.cat, used: r.used } : null; })
  .filter(s => s && !segCoverage(s).needsRecruiting);

Object.assign(window, { AudienceShelf, SEGMENTS, segById, segCoverage, CovBadge, asSegment, recentSegments, AUDIENCE_GROUPS: GROUPS,
  SHELF_CATEGORIES: ["Women's Lifestyle", "Women's Footwear", "Men's Footwear", "Running Footwear", "Men's Outerwear", "Men's Bottoms", "Graphic Tees", "Accessories"] });
