/* iBD Coliseum — seed data (believable internal AI use cases) */
// (data layer: categories, cards, comments, metrics, season, tiers)

// category meta
const CATEGORIES = [
  { id: "workflow", label: "Workflow & Productivity", color: "#6366F1" },
  { id: "communication", label: "Communication & Collaboration", color: "#7A5AE0" },
  { id: "sales", label: "Sales Channel & Marketplace Support", color: "#B45309" },
  { id: "reporting", label: "Reporting, Data & Insights", color: "#2A6FDB" },
  { id: "knowledge", label: "Knowledge Management & Training", color: "#1F8A5B" },
  { id: "creative", label: "Creative, Content & Experience", color: "#E5484D" },
  { id: "general", label: "General / Other", color: "#6E6E73" },
];

function storedEntryLink(raw) {
  return (raw || "").trim();
}

/** For href only — display uses storedEntryLink so pasted text stays exact. */
function hrefForStoredLink(raw) {
  const s = storedEntryLink(raw);
  if (!s) return "";
  if (/^https?:\/\//i.test(s)) return s;
  if (/^\//.test(s)) return s;
  if (/^\/\//.test(s)) return "https:" + s;
  return "https://" + s;
}

function normalizeExternalUrl(raw) {
  return hrefForStoredLink(raw);
}

function extractExternalUrl(text) {
  const s = (text || "").trim();
  if (!s) return "";
  const match = s.match(/https?:\/\/[^\s<>"')\]]+/i);
  if (match) return match[0].replace(/[.,;:!?)]+$/, "");
  const bare = s.match(/(?:^|\s)(claude\.ai\/[^\s<>"')\]]+)/i);
  if (bare) return hrefForStoredLink(bare[1]);
  return "";
}

function resolveEntryLink(card) {
  if (!card) return "";
  return storedEntryLink(card.skill);
}

function slugifyTitle(s) {
  return (s || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "skill";
}

/** File extensions treated as downloadable (not opened as a Claude link). */
const DOWNLOADABLE_EXTS = [
  "skill",
  "pdf", "doc", "docx", "xls", "xlsx", "xlsm", "ppt", "pptx", "csv", "tsv", "txt", "md", "rtf",
  "odt", "ods", "odp",
  "zip", "rar", "7z", "tar", "gz", "tgz",
  "json", "xml", "yaml", "yml", "html", "htm", "css", "js", "ts", "py", "ipynb",
  "png", "jpg", "jpeg", "gif", "webp", "svg", "mp4", "mov", "mp3", "wav",
];
const DOWNLOAD_EXT_GROUP = DOWNLOADABLE_EXTS.join("|");
const DOWNLOAD_EXT_RE = new RegExp("\\.(" + DOWNLOAD_EXT_GROUP + ")(\\?|#|$)", "i");
const DOWNLOAD_FILE_RE = new RegExp("([^/?#]+\\.(" + DOWNLOAD_EXT_GROUP + "))", "i");

function isClaudeShareUrl(raw) {
  return /claude\.ai/i.test(storedEntryLink(raw));
}

function isDownloadableShareUrl(raw) {
  return DOWNLOAD_EXT_RE.test(storedEntryLink(raw));
}

/** @deprecated use isDownloadableShareUrl */
function isSkillFileUrl(raw) {
  return isDownloadableShareUrl(raw);
}

function downloadExtFromUrl(raw) {
  const m = storedEntryLink(raw).match(DOWNLOAD_EXT_RE);
  return m ? m[1].toLowerCase() : null;
}

function downloadNameFromShare(raw, title) {
  const s = storedEntryLink(raw);
  const m = s.match(DOWNLOAD_FILE_RE);
  if (m) return m[1];
  const ext = downloadExtFromUrl(raw) || "bin";
  return slugifyTitle(title) + "." + ext;
}

function downloadLabelForShare(share) {
  if (!share || share.type !== "download") return "Download";
  return share.downloadExt === "skill" ? "Download skill" : "Download file";
}

/** Classify what a card shares: Claude/open link, downloadable file, or nothing. */
function classifyEntryShare(raw, title) {
  const value = storedEntryLink(raw);
  if (!value) return { type: "none", value: "", href: "" };
  if (isClaudeShareUrl(value)) {
    return { type: "link", value, href: hrefForStoredLink(value) };
  }
  if (isDownloadableShareUrl(value)) {
    const downloadExt = downloadExtFromUrl(value);
    const share = {
      type: "download",
      value,
      href: hrefForStoredLink(value),
      downloadExt,
      downloadName: downloadNameFromShare(value, title),
    };
    share.downloadLabel = downloadLabelForShare(share);
    return share;
  }
  return { type: "link", value, href: hrefForStoredLink(value) };
}

function classifyEntryShareFromCard(card) {
  return classifyEntryShare(card && card.skill, card && card.title);
}

// tools with little brand-ish dot colors
const TOOLS = {
  "Claude":        "#FF6A00",
  "Claude Code":   "#0A0A0A",
  "Artifacts":     "#2A6FDB",
  "Excel":         "#1F8A5B",
  "Slack":         "#7A5AE0",
  "Figma":         "#E5484D",
  "Google Sheets": "#1F8A5B",
};

// avatar palette (deterministic by initials)
const AV_COLORS = ["#0A0A0A", "#FF6A00", "#2A6FDB", "#1F8A5B", "#7A5AE0", "#3A3A3C", "#CC5400"];
function avatarColor(name) {
  let h = 0; for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
  return AV_COLORS[h % AV_COLORS.length];
}
function initials(name) { return name.split(" ").map(s => s[0]).slice(0, 2).join("").toUpperCase(); }

// group: B = power user (seed), C = hesitant first-timer
const CARDS = [
  {
    id: "c1", cat: "reporting", group: "B", seed: true,
    title: "Weekly Amazon sales report: 3 hrs \u2192 18 min",
    desc: "Claude turns the raw Seller Central CSV export into a formatted weekly summary with WoW deltas, top movers, and a written 'so what' paragraph for the standup.",
    author: "Daniel Cho", team: "BPM", tools: ["Claude", "Excel"],
    votes: 47, when: "3d", hasRepro: true, hasLink: true,
  },
  {
    id: "c2", cat: "communication", group: "C",
    title: "First time trying AI \u2014 return-request reply drafts",
    desc: "I used Claude to draft warm, on-brand replies for damaged-case return tickets. Pasted our tone guide once, now I get a solid first draft I just tweak. Saved me a ton of second-guessing.",
    author: "Mina Seo", team: "CEM", tools: ["Claude"],
    votes: 31, when: "1d", hasRepro: true, hasLink: false,
  },
  {
    id: "c3", cat: "sales", group: "B", seed: true,
    title: "Bulk-renamed 2,300 product SKUs with a script",
    desc: "Claude Code wrote a Python script that normalized our messy SKU naming across the catalog export and flagged 40 duplicates. Reviewed, ran it, done in an afternoon.",
    author: "Aaron Klein", team: "WebD", tools: ["Claude Code"],
    votes: 38, when: "5d", hasRepro: true, hasLink: true,
  },
  {
    id: "c4", cat: "reporting", group: "B", seed: true,
    title: "Competitor case-design teardown digest",
    desc: "Fed Claude 12 competitor product pages + reviews; got back a structured teardown of materials, price tiers, and the top 5 complaints customers repeat. Reused it for the Q3 planning doc.",
    author: "Priya Nair", team: "Design", tools: ["Claude", "Artifacts"],
    votes: 29, when: "2d", hasRepro: true, hasLink: false,
  },
  {
    id: "c5", cat: "creative", group: "C",
    title: "10 PDP headline variants in one sitting",
    desc: "Tried generating A/B headline + bullet variants for the MagFit page. Gave it our voice rules and the spec sheet. Picked 3 to test. Honestly faster than my usual blank-page stare.",
    author: "Jordan Lee", team: "Marketing", tools: ["Claude"],
    votes: 22, when: "4h", hasRepro: true, hasLink: false,
  },
  {
    id: "c6", cat: "workflow", group: "C",
    title: "Turned a chaotic vendor email thread into a decision log",
    desc: "Pasted a 40-message supplier thread and asked Claude for a clean timeline + open decisions + who owns what. First thing I've ever 'made' with AI and it actually helped.",
    author: "Sofia Alvarez", team: "Production", tools: ["Claude"],
    votes: 26, when: "6h", hasRepro: true, hasLink: false,
  },
  {
    id: "c7", cat: "communication", group: "B", seed: true,
    title: "Slack bot that summarizes the #cs-escalations channel",
    desc: "Built a small Artifact that pulls the day's escalations and posts a 3-bullet end-of-day summary to the CX leads. Took an evening; the team asked me to keep it running.",
    author: "Aaron Klein", team: "WebD", tools: ["Claude Code", "Slack"],
    votes: 34, when: "1d", hasRepro: true, hasLink: true,
  },
  {
    id: "c8", cat: "reporting", group: "C",
    title: "Survey CSV \u2192 tagged insights in 15 minutes",
    desc: "Our post-purchase survey had 600 free-text responses. Claude clustered them into themes and pulled representative quotes. I used to dread this. Sharing the prompt below.",
    author: "Hana Kim", team: "CEM", tools: ["Claude", "Google Sheets"],
    votes: 19, when: "8h", hasRepro: true, hasLink: false,
  },
  {
    id: "c9", cat: "knowledge", group: "C",
    title: "Plain-English summary of a 60-page material spec",
    desc: "A supplier sent a dense TPU material datasheet. Asked Claude for the 'what changed vs last version + risks' view. Not perfect, but a great starting map. My first real AI win.",
    author: "Marco Rossi", team: "TD", tools: ["Claude"],
    votes: 14, when: "11h", hasRepro: false, hasLink: false,
  },
  {
    id: "c10", cat: "creative", group: "B", seed: true,
    title: "Repurposed one launch blog into 6 channel posts",
    desc: "Gave Claude the Ultra Hybrid launch post + our channel guidelines; got tailored copy for IG, LinkedIn, email, and the Amazon A+ module. Edited lightly, shipped same day.",
    author: "Jordan Lee", team: "Marketing", tools: ["Claude", "Figma"],
    votes: 25, when: "3d", hasRepro: true, hasLink: true,
  },
  {
    id: "c11", cat: "knowledge", group: "B", seed: true,
    title: "Onboarding checklist generator for new CX hires",
    desc: "Built an Artifact where a manager picks a role and gets a tailored week-1 checklist with links. New hires stopped pinging us for 'where is X'.",
    author: "Daniel Cho", team: "BPM", tools: ["Artifacts"],
    votes: 21, when: "6d", hasRepro: true, hasLink: true,
  },
  {
    id: "c12", cat: "reporting", group: "C",
    title: "Monthly finance variance commentary, drafted",
    desc: "I gave Claude the budget-vs-actual table and our last 2 commentaries as examples. It drafted the variance narrative in our style. I review and correct numbers \u2014 huge time saver.",
    author: "Grace Park", team: "AWE", tools: ["Claude", "Excel"],
    votes: 17, when: "2d", hasRepro: true, hasLink: false,
  },
  {
    id: "c13", cat: "general", group: "C", seed: true,
    title: "Coliseum hello test skill (example)",
    desc: "A tiny downloadable .skill file for testing installs from a Coliseum card. Download it, drag it into Claude, and ask it to run the test.",
    author: "Luke Park", team: "WebD", tools: ["Claude"],
    skill: "https://ibdcoliseum.vercel.app/samples/coliseum-hello-test.skill",
    votes: 3, when: "1h", hasRepro: true, hasLink: true,
  },
];

// reproduction-step text shown in expanded view
const REPRO = {
  c1: ["Export Seller Central → 'Business Reports' as CSV", "Paste into Claude with prompt: 'Summarize WoW, flag top 5 movers, write a 3-sentence standup note in our voice'", "Drop the output into the #sales-standup template"],
  c2: ["Paste our CX tone guide once at the top of the chat", "For each ticket: 'Draft a warm reply for this damaged-case return, offer replacement, keep it under 90 words'", "Skim, personalize the name + order #, send"],
  c13: [
    "Open this card and click Download skill",
    "Drag coliseum-hello-test.skill into a Claude chat (or add via Settings › Capabilities)",
    "Ask: \"Run the Coliseum hello test.\" You should see: Coliseum test skill is working!",
  ],
  default: ["Open Claude", "Paste the source material + a clear instruction with your desired format", "Review the output and adjust before using"],
};

// card detail: the exact prompt used + a snippet of what Claude produced
const DETAILS = {
  c1: {
    prompt: "Attached is our weekly Amazon Seller Central business report (CSV). Summarize week-over-week: total sales delta %, the top 5 movers up and down with their % change, and write a 3-sentence 'so what' note for our Monday standup in a plain, direct voice. Treat blank cells as 0 and flag any you find.",
    output: "Week of Jun 2–8 · Sales $412,800 (+8.4% WoW)\n▲ Ultra Hybrid case +31% · MagFit charger +22% · Screen protector 2pk +18%\n▼ Tempered glass −14% (stockout Wed–Fri) · Car mount −9%\nSo what: Growth held even with the glass stockout — restock is the single biggest lever for next week. 2 blank cells in 'returns' flagged and treated as 0.",
  },
  c2: {
    prompt: "Here is our CX tone guide. Draft a warm, on-brand reply to this damaged-case return request: offer a free replacement, apologize once without grovelling, keep it under 90 words, and end with a friendly sign-off.",
    output: "Hi Jordan — so sorry your case arrived damaged, that's on us. I've put a free replacement on its way (no need to return the old one), arriving in 3–5 days. If anything else looks off when it lands, just reply here and I'll sort it straight away. Thanks for giving us the chance to make it right! — Mina, Spigen Care",
  },
  c3: {
    prompt: "Write a Python script that reads catalog_export.csv, normalizes the SKU column (uppercase, strip whitespace, collapse double dashes), flags exact duplicates, and writes a cleaned file plus a duplicates report. Explain each step in a comment so a non-engineer can follow.",
    output: "✓ Script written (cleans 2,317 rows)\n• normalize_sku(): upper() → strip() → re.sub('--+','-')\n• duplicates.csv: 40 collisions found (e.g. ACS-01234 ×3)\n• output: catalog_clean.csv\nRun: python normalize_skus.py catalog_export.csv",
  },
  c4: {
    prompt: "I'm pasting 12 competitor product pages and their top reviews. Give me a structured teardown: materials used, price tiers, and the top 5 complaints customers repeat across brands. Format as a table I can drop into a planning doc.",
    output: "| Brand | Material | Price tier | — |\n| A | TPU + PC | $$  |\n| B | Aramid   | $$$ |\nTop repeated complaints: 1) yellowing (5 brands) 2) bulky MagSafe ring 3) slippery finish 4) weak button click 5) lint in ports.",
  },
  c5: {
    prompt: "Here are our brand voice rules and the spec sheet for the MagFit case. Generate 10 PDP headline variants + a 1-line subhead for each, ranging from benefit-led to feature-led. No hype words ('revolutionary', 'game-changing').",
    output: "1. Snaps on. Stays on. — Full-strength MagSafe hold, every time.\n2. Slim enough to forget, strong enough to trust.\n3. The case that finds your charger for you.\n…(7 more) — picked #1, #4, #9 to A/B test.",
  },
  c6: {
    prompt: "Below is a 40-message supplier email thread. Turn it into a clean decision log: a timeline of what was agreed, the open decisions still pending, and a 'who owns what' table with names and due dates.",
    output: "Timeline: 5/3 MOQ agreed (5k) · 5/9 price locked $2.14/unit · 5/14 tooling ETA slipped to 6/30\nOpen: final Pantone sign-off; air vs sea freight\nOwners: Sofia → Pantone (6/2) · Vendor → tooling photos (5/28) · Finance → freight quote (6/1)",
  },
  c7: {
    prompt: "Build a small tool that reads the day's messages in #cs-escalations and posts a 3-bullet end-of-day summary to the CX leads: how many escalations, the top recurring theme, and anything still unresolved overnight.",
    output: "EOD · #cs-escalations\n• 14 escalations today (−3 vs avg)\n• Top theme: delayed MagFit pre-orders (6 of 14)\n• Unresolved overnight: 2 (refund disputes — flagged to Mina)",
  },
  c8: {
    prompt: "Attached are 600 free-text answers from our post-purchase survey (CSV). Cluster them into themes, give each theme a count and 2 representative quotes, and rank themes by frequency.",
    output: "1. Fast shipping praise (188) — \"arrived next day\" · \"quicker than Amazon\"\n2. Packaging too hard to open (96) — \"needed scissors\"\n3. Wanted more colors (74)\n…(8 themes total). Prompt shared below.",
  },
  c9: {
    prompt: "This is a 60-page TPU material datasheet (PDF). Give me a plain-English summary: what changed versus the previous revision, and the top risks or trade-offs I should flag to the design team. Keep it to one page.",
    output: "What changed vs Rev B: hardness 85A→90A (stiffer), added UV stabilizer, recycled content 20%→35%.\nRisks: stiffer shell may reduce drop absorption at corners; recycled content can vary batch-to-batch — request COA per lot.",
  },
  c10: {
    prompt: "Here's our Ultra Hybrid launch blog post and our channel guidelines. Repurpose it into tailored copy for Instagram caption, LinkedIn post, marketing email, and an Amazon A+ module — match the length and tone rules for each channel.",
    output: "IG: \"Clear case, zero yellowing. The Ultra Hybrid is back ☀️ #Spigen\" (+ 4 hashtags)\nLinkedIn: 3-sentence POV on durability testing…\nEmail: subject \"It stays clear. We tested it 500×.\"\nA+ module: 3 feature blurbs ≤ 60 chars each.",
  },
  c11: {
    prompt: "Build a tool where a manager picks a new hire's role and gets a tailored week-1 onboarding checklist with links to the right docs, accounts, and people to meet. Roles: CX agent, Sales ops, Designer.",
    output: "Role: CX Agent — Week 1\n☐ Zendesk access (IT ticket link)\n☐ Read tone guide + 5 sample tickets\n☐ Shadow Mina (Tue), Hana (Thu)\n☐ First 3 supervised replies by Fri",
  },
  c12: {
    prompt: "Here is this month's budget-vs-actual table and our last two variance commentaries as style examples. Draft the variance narrative in the same voice — explain the 3 biggest swings and whether each is timing or structural. I'll verify the numbers.",
    output: "Marketing came in 12% under budget — timing, not savings: the Q3 campaign shifted to next month.\nFreight ran 8% over, structural: carrier rate increase now baked in.\nHeadcount on plan. Net favorable variance $48k, largely timing.",
  },
  c13: {
    prompt: "Say you are testing the Coliseum hello skill install.",
    output: "",
  },
  default: {
    prompt: "Here's my material: [paste your file or text]. I want to <do the thing in the title> — give me the result in a format I can use right away, and keep it concise.",
    output: "Claude returns a structured first draft you can review and adjust — see the contributor's reproduction steps for the exact recipe.",
  },
};

// Install hint for downloadable .skill files
const SKILL = {
  note: "Drag the .skill file into any Claude chat, or add it via Settings › Capabilities to keep it permanently.",
};

// seed comment threads (cardId -> [{author, team, when, text}])
const COMMENTS = {
  c1: [
    { author: "Grace Park", team: "AWE", when: "2d", text: "Stealing this for our monthly close — does the WoW prompt handle blank cells okay?" },
    { author: "Daniel Cho", team: "BPM", when: "2d", text: "Yep, I add 'treat blanks as 0 and flag them' to the prompt. Works well." },
  ],
  c2: [
    { author: "Hana Kim", team: "CEM", when: "20h", text: "This is so helpful — pasting the tone guide once was the trick I was missing. Thank you!" },
  ],
  c3: [
    { author: "Priya Nair", team: "Design", when: "4d", text: "Did it catch SKUs with trailing spaces? That's bitten us before." },
    { author: "Aaron Klein", team: "WebD", when: "4d", text: "It did — I had it strip whitespace and lowercase before comparing. Happy to share the script." },
  ],
  c6: [
    { author: "Marco Rossi", team: "TD", when: "5h", text: "First AI thing I'd actually reuse. The 'who owns what' table is gold for supplier threads." },
  ],
};

// admin metrics
const METRICS = {
  pilotUsers: 47,
  submitters: 18,                       // → submission rate
  submissionRate: 38,                   // %
  engaged: 30,                          // viewed ≥3 or upvoted
  engagementRate: 64,                   // %
  repeatRate: 23,                       // %
  newCardsThisQuarter: 21,              // excl seeds (target ≥15)
  totalCards: 12,                       // seeds shown in gallery sample
  avgVotesPerCard: 3.2,
  totalUpvotes: 343,
  // weekly submissions split by source
  weekly: [
    { wk: "W1", seed: 10, b: 2, c: 1 },
    { wk: "W2", seed: 0, b: 3, c: 4 },
    { wk: "W3", seed: 0, b: 2, c: 6 },
    { wk: "W4", seed: 0, b: 1, c: 7 },
  ],
  // participation split
  participation: [
    { id: "c", label: "Group C", note: "Hesitant \u2014 first-time participants", pct: 53, count: 25, color: "var(--accent)" },
    { id: "b", label: "Group B", note: "Power users / seed contributors", pct: 17, count: 8, color: "var(--ink)" },
    { id: "n", label: "Not yet active", note: "Have access, haven't engaged", pct: 30, count: 14, color: "var(--gray-300)" },
  ],
  // category distribution
  catDist: [
    { id: "reporting", n: 6 }, { id: "communication", n: 5 }, { id: "sales", n: 2 },
    { id: "knowledge", n: 3 }, { id: "creative", n: 2 }, { id: "workflow", n: 2 },
    { id: "general", n: 1 },
  ],
  // submissions per department (company org units)
  byDept: [
    { dept: "WebD", n: 7 },
    { dept: "CEM", n: 6 },
    { dept: "Marketing", n: 5 },
    { dept: "Design", n: 4 },
    { dept: "BPM", n: 4 },
    { dept: "Production", n: 3 },
    { dept: "AWE", n: 3 },
    { dept: "TD", n: 2 },
    { dept: "Smart Electronics", n: 2 },
    { dept: "IP", n: 1 },
    { dept: "LIFE", n: 1 },
    { dept: "Tesla EV", n: 1 },
    { dept: "Legato", n: 0 },
  ],
  // submissions per quarter (current = Q3 '26)
  quarterly: [
    { q: "Q4 '25", n: 6 },
    { q: "Q1 '26", n: 11 },
    { q: "Q2 '26", n: 17 },
    { q: "Q3 '26", n: 21, current: true },
  ],
  // cumulative votes + comments across the pilot (weekly)
  activity: [
    { t: "W1", votes: 22,  comments: 4 },
    { t: "W2", votes: 41,  comments: 9 },
    { t: "W3", votes: 68,  comments: 15 },
    { t: "W4", votes: 110, comments: 22 },
    { t: "W5", votes: 165, comments: 30 },
    { t: "W6", votes: 235, comments: 38 },
    { t: "W7", votes: 300, comments: 46 },
    { t: "W8", votes: 343, comments: 52 },
  ],
};

/* ---------- Season / quarterly calendar (Jan–Mar, Apr–Jun, Jul–Sep, Oct–Dec) ---------- */
function seasonForDate(input) {
  const d = input ? new Date(input) : new Date();
  const y = d.getFullYear();
  const m = d.getMonth();
  const quarter = m <= 2 ? 1 : m <= 5 ? 2 : m <= 8 ? 3 : 4;
  const bounds = [
    { sm: 0, sd: 1, em: 2, ed: 31 },
    { sm: 3, sd: 1, em: 5, ed: 30 },
    { sm: 6, sd: 1, em: 8, ed: 30 },
    { sm: 9, sd: 1, em: 11, ed: 31 },
  ][quarter - 1];
  const pad = (n) => String(n).padStart(2, "0");
  const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  const startISO = `${y}-${pad(bounds.sm + 1)}-${pad(bounds.sd)}`;
  const endISO = `${y}-${pad(bounds.em + 1)}-${pad(bounds.ed)}`;
  return {
    year: y,
    quarter,
    key: `${y}-S${quarter}`,
    label: `${y} Season ${quarter}`,
    no: String(quarter).padStart(2, "0"),
    quarterLabel: `Q${quarter} ${y}`,
    start: `${months[bounds.sm]} ${bounds.sd}`,
    end: `${months[bounds.em]} ${bounds.ed}`,
    startISO,
    endISO,
  };
}

function currentSeason() {
  return seasonForDate(new Date());
}

function seasonEndMs(seasonInfo) {
  return Date.parse(seasonInfo.endISO + "T23:59:59.999");
}

function isSeasonEnded(seasonInfo, when) {
  const t = when ? new Date(when).getTime() : Date.now();
  return t > seasonEndMs(seasonInfo);
}

function entrySeasonKey(entry) {
  if (entry && entry.seasonKey) return entry.seasonKey;
  if (entry && entry.createdAt) return seasonForDate(entry.createdAt).key;
  return currentSeason().key;
}

function entrySeasonLabel(entry) {
  if (entry && entry.seasonLabel) return entry.seasonLabel;
  if (entry && entry.createdAt) return seasonForDate(entry.createdAt).label;
  return currentSeason().label;
}

function entryInCurrentSeason(entry) {
  const dbCur = window.COLISEUM_DATA.DB_CURRENT_SEASON;
  if (dbCur && entry && entry.seasonId) return entry.seasonId === dbCur.id;
  return entrySeasonKey(entry) === currentSeason().key;
}

function visibleGallerySeasons(cards, now) {
  const tabs = [{ key: "all", label: "All seasons" }];
  const seen = new Set();
  (cards || []).forEach((c) => {
    const key = entrySeasonKey(c);
    if (seen.has(key)) return;
    seen.add(key);
    tabs.push({ key, label: entrySeasonLabel(c) });
  });
  tabs.sort((a, b) => {
    if (a.key === "all") return -1;
    if (b.key === "all") return 1;
    return b.key.localeCompare(a.key);
  });
  if (tabs.length === 1) tabs.push(seasonForDate(now));
  return tabs;
}

function buildSeasonMeta(refDate) {
  const s = seasonForDate(refDate);
  const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  const midMonth = [1, 4, 7, 10][s.quarter - 1];
  return {
    ...s,
    title: `The ${s.quarterLabel} Coliseum`,
    tagline: "A fresh season every quarter. Three months to explore, share, and learn together — no scoring, no elimination, just momentum building across the org.",
    duration: "3 months · one quarter",
    today: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric" }),
    endsIn: "—",
    progressPct: 0,
    stats: { shared: 0, contributors: 0, departments: 0 },
    timeline: [
      { tag: `Month 1 · ${monthNames[midMonth - 1]}`, date: s.start, t: "Season opens", state: "done",
        d: "A clean quarter begins. Share what you try and watch the wall fill up for this season." },
      { tag: `Mid-season`, date: "—", t: "Mid-season check-in", state: "now",
        d: "Halfway through the quarter — a quick recap of what the org has built so far." },
      { tag: "End of season", date: s.end, t: "Season ends", state: "todo", end: true,
        d: "This season closes on " + s.end + ". The next season opens the very next day." },
    ],
  };
}

// Live season meta — recomputed from today's calendar quarter.
const SEASON = buildSeasonMeta();

function seasonProgress(S) {
  const start = Date.parse(S.startISO);
  const end = Date.parse(S.endISO);
  const now = Date.now();
  if (!start || !end || end <= start) {
    return { pct: S.progressPct, endsIn: S.endsIn, today: S.today, started: true };
  }
  const pct = Math.round(Math.min(100, Math.max(0, ((now - start) / (end - start)) * 100)));
  const dayMs = 86400000;
  const fmt = (ms) => new Date(ms).toLocaleDateString(undefined, { month: "short", day: "numeric" });
  if (now < start) {
    const days = Math.ceil((start - now) / dayMs);
    return { pct: 0, endsIn: "Opens in " + days + " days", today: fmt(now), started: false };
  }
  const daysLeft = Math.max(0, Math.ceil((end - now) / dayMs));
  return { pct, endsIn: daysLeft + " days", today: fmt(now), started: true };
}

/* ---------- Competition / seasonal missions ---------- */
// Multiple executives can each post a mission (topic/brief); users submit
// card-style entries and discuss. Several missions run concurrently.
const EXECUTIVES = [
  { name: "Luke Park", role: "Executive sponsor · iBD", team: "iBD · Executive", color: "#FF6A00" },
];

const TEAMS = ["IP", "AWE", "WebD", "BPM", "TD", "CEM", "Marketing", "Production", "Design", "Smart Elec", "LIFE", "Tesla EV", "Legato"];

const COMPETITION = {
  executives: EXECUTIVES,
  missions: [
    {
      id: "mission-q3",
      no: "03",
      title: "Reclaim an Hour",
      tagline: "Quarterly mission",
      color: "#FF6A00",
      brief: "Show one workflow where AI handed you back at least an hour this week — and make it repeatable for a colleague.",
      detail: "We're not after the flashiest demo. We want the everyday wins: the report you stopped dreading, the inbox you tamed, the spreadsheet that now writes itself. Post the before/after, the prompt or steps you used, and who else on your team could borrow it tomorrow.",
      prompts: [
        "What task ate your time before, and how long did it take?",
        "What did you do with AI instead — paste the prompt or steps.",
        "Roughly how much time did it save, and who else could reuse it?",
      ],
      tags: ["Time-saving", "Any department", "Repeatable"],
      postedBy: "Luke Park",
      postedRole: "Executive sponsor · iBD",
      opened: "Jun 2",
      closes: "Jun 30",
      daysLeft: 15,
      reward: "Top 3 demoed live at the town hall · +150 Coliseum points to every valid entry",
      entries: [
        { id: "e1", author: "Mina Seo", team: "CEM", when: "2d", votes: 14, cat: "communication", tools: ["Claude"],
          title: "Return-request replies: 40 min → 8 min a day",
          desc: "Pasted our tone guide once; now every damaged-case reply is a 1-tweak first draft. Reused by 3 teammates already.", cardId: "c2" },
        { id: "e2", author: "Daniel Cho", team: "BPM", when: "3d", votes: 11, cat: "reporting", tools: ["Claude", "Excel"],
          title: "Monday sales report, 3 hrs back every week",
          desc: "The Seller Central CSV → standup summary recipe. Anyone on Sales Ops can run it now.", cardId: "c1" },
        { id: "e3", author: "Sofia Alvarez", team: "Production", when: "1d", votes: 8, cat: "workflow", tools: ["Claude"],
          title: "Vendor email chaos → a clean decision log",
          desc: "40-message supplier thread becomes a timeline + owners in 5 minutes. My first AI win, honestly.", cardId: "c6" },
        { id: "e4", author: "Grace Park", team: "AWE", when: "5h", votes: 5, cat: "reporting", tools: ["Claude", "Excel"],
          title: "Variance commentary drafted in our voice",
          desc: "Budget-vs-actual table + last 2 write-ups as examples → first-draft narrative. I just verify the numbers.", cardId: "c12" },
      ],
      discussion: [
        { author: "Hana Kim", team: "CEM", when: "1d", text: "Does it count if the time saved is spread across the week rather than one big task?" },
        { author: "Luke Park", team: "iBD · Executive", when: "22h", text: "Absolutely — small daily wins are exactly what we want to surface. Add up the week and tell us the recipe.", exec: true },
        { author: "Marco Rossi", team: "TD", when: "20h", text: "Can we submit something a teammate built if we adapted it? Want to credit the original." },
        { author: "Luke Park", team: "iBD · Executive", when: "18h", text: "Yes, credit them in the entry and you both earn points. Reuse is the whole point.", exec: true },
      ],
    },
    {
      id: "mission-onboard",
      no: "04",
      title: "Onboard in Half the Time",
      tagline: "Operations challenge",
      color: "#2A6FDB",
      brief: "Build something that gets a new hire genuinely productive in days, not weeks — a checklist, a guide, or a little assistant.",
      detail: "Every team re-explains the same first-week questions. Use AI to capture that knowledge once and hand it to the next hire. Show what you built, how a manager runs it, and which team could adopt it on Monday.",
      prompts: [
        "Which onboarding step is slow or repetitive today?",
        "What did you build with AI to speed it up — share the prompt or tool.",
        "How would another team adopt it as-is?",
      ],
      tags: ["Onboarding", "Ops & People", "Reusable"],
      postedBy: "Sarah Yoon",
      postedRole: "VP, Operations",
      opened: "Jun 9",
      closes: "Jul 7",
      daysLeft: 22,
      reward: "Winning playbook ships company-wide · +150 Coliseum points to every valid entry",
      entries: [
        { id: "eo1", author: "Daniel Cho", team: "BPM", when: "1d", votes: 9, cat: "knowledge", tools: ["Artifacts"],
          title: "Role-aware week-1 checklist generator",
          desc: "Manager picks a role, gets a tailored onboarding checklist with the right docs and people to meet. New hires stopped pinging us for 'where is X'.", cardId: "c11" },
        { id: "eo2", author: "Aaron Klein", team: "WebD", when: "6h", votes: 6, cat: "communication", tools: ["Claude Code", "Slack"],
          title: "A bot that answers 'how do we do X here?'",
          desc: "Pointed it at our team wiki; new devs ask it in Slack instead of interrupting seniors. Cut my onboarding 1:1s in half.", cardId: "c7" },
      ],
      discussion: [
        { author: "Priya Nair", team: "Design", when: "10h", text: "Could this be a shared template other teams just swap their docs into?" },
        { author: "Sarah Yoon", team: "Operations · Executive", when: "8h", text: "That's exactly the win — build it so Ops can clone it per team. Bonus points for a 2-minute setup guide.", exec: true },
      ],
    },
  ],
  past: [
    { id: "m-q2", no: "02", title: "Tame the Inbox", winner: "One thread → a daily 3-bullet escalation digest",
      author: "Aaron Klein", team: "WebD", entries: 19, quarter: "Q2 '26", postedBy: "Luke Park" },
    { id: "m-q1", no: "01", title: "Kill the Copy-Paste", winner: "Bulk-renamed 2,300 SKUs with a reviewed script",
      author: "Aaron Klein", team: "WebD", entries: 12, quarter: "Q1 '26", postedBy: "David Min" },
  ],
};

window.COLISEUM_DATA = { CATEGORIES, TOOLS, CARDS, REPRO, COMMENTS, DETAILS, SKILL, METRICS, SEASON, COMPETITION, TEAMS, avatarColor, initials, storedEntryLink, hrefForStoredLink, normalizeExternalUrl, extractExternalUrl, resolveEntryLink, DOWNLOADABLE_EXTS, isClaudeShareUrl, isDownloadableShareUrl, isSkillFileUrl, classifyEntryShare, classifyEntryShareFromCard, slugifyTitle, downloadLabelForShare };


/* ---------- Tiers / ranking ---------- */
const TIERS = [
  { id: "cardboard", name: "Cardboard", min: 0,    color: "#C2A06B" },
  { id: "wood",      name: "Wood",      min: 50,   color: "#8A5A33" },
  { id: "iron",      name: "Iron",      min: 150,  color: "#7C8694" },
  { id: "bronze",    name: "Bronze",    min: 300,  color: "#C77B30" },
  { id: "silver",    name: "Silver",    min: 500,  color: "#8FA0B6" },
  { id: "gold",      name: "Gold",      min: 750,  color: "#D8A12A" },
  { id: "platinum",  name: "Platinum",  min: 1050, color: "#3FB6A8" },
  { id: "diamond",   name: "Diamond",   min: 1500, color: "#37BEE6" },
];
const PTS_PER_UPVOTE = 10;
const PTS_PER_COMMENT = 10;
const PTS_PER_SUBMISSION = 30;
const VOTES_PER_QUARTER = 3;

function seasonUpvotesUsed(votedSet, cards) {
  const key = currentSeason().key;
  return cards.filter((c) => votedSet.has(c.id) && entrySeasonKey(c) === key).length;
}

function voteQuota(votedSet, cards) {
  const used = seasonUpvotesUsed(votedSet, cards);
  return { used, limit: VOTES_PER_QUARTER, remaining: Math.max(0, VOTES_PER_QUARTER - used) };
}

// Points reward the actor: upvotes they GAVE + comments they POSTED + their submissions.
// When DB engagement data is available (window.COLISEUM_DATA.ENGAGEMENT keyed by display
// name) points come from that; otherwise (demo mode) fall back to upvotes received on
// the author's own cards.
function authorStats(cards, author) {
  const curKey = currentSeason().key;
  const mine = cards.filter((c) => c.author === author && entrySeasonKey(c) === curKey);
  const votes = mine.reduce((s, c) => s + c.votes, 0);
  const eng = (window.COLISEUM_DATA.ENGAGEMENT || {})[author] || null;
  const upvotesGiven = eng ? eng.upvotesGiven : 0;
  const commentsMade = eng ? eng.commentsMade : 0;
  const submissions = eng ? eng.submissions : mine.length;
  const awardPoints = eng ? (eng.awardPoints || 0) : 0;
  const points = eng
    ? upvotesGiven * PTS_PER_UPVOTE + commentsMade * PTS_PER_COMMENT + submissions * PTS_PER_SUBMISSION + awardPoints
    : 0;
  const team = mine[0] ? mine[0].team : (eng && eng.team ? eng.team : "iBD · Pilot");
  return { author, team, votes, submissions, upvotesGiven, commentsMade, awardPoints, points, cards: mine };
}

// tier info for a points total: current tier, next tier, points needed, progress 0..1
function tierFor(points) {
  let idx = 0;
  for (let i = 0; i < TIERS.length; i++) if (points >= TIERS[i].min) idx = i;
  const tier = TIERS[idx];
  const next = TIERS[idx + 1] || null;
  const toNext = next ? next.min - points : 0;
  const span = next ? next.min - tier.min : 1;
  const progress = next ? Math.min(1, (points - tier.min) / span) : 1;
  return { tier, next, toNext, progress, index: idx };
}

function isOwnEntry(entry, userId, displayName) {
  if (userId && entry.authorId) return entry.authorId === userId;
  if (displayName && entry.author === displayName) return true;
  if (entry.author === "You" && !entry.authorId) return true;
  return false;
}

window.COLISEUM_DATA.TIERS = TIERS;
window.COLISEUM_DATA.authorStats = authorStats;
window.COLISEUM_DATA.tierFor = tierFor;
window.COLISEUM_DATA.isOwnEntry = isOwnEntry;
window.COLISEUM_DATA.seasonForDate = seasonForDate;
window.COLISEUM_DATA.currentSeason = currentSeason;
window.COLISEUM_DATA.entrySeasonKey = entrySeasonKey;
window.COLISEUM_DATA.entrySeasonLabel = entrySeasonLabel;
window.COLISEUM_DATA.entryInCurrentSeason = entryInCurrentSeason;
window.COLISEUM_DATA.isSeasonEnded = isSeasonEnded;
window.COLISEUM_DATA.visibleGallerySeasons = visibleGallerySeasons;
window.COLISEUM_DATA.buildSeasonMeta = buildSeasonMeta;
window.COLISEUM_DATA.seasonProgress = seasonProgress;
window.COLISEUM_DATA.PTS_PER_UPVOTE = PTS_PER_UPVOTE;
window.COLISEUM_DATA.PTS_PER_COMMENT = PTS_PER_COMMENT;
window.COLISEUM_DATA.PTS_PER_SUBMISSION = PTS_PER_SUBMISSION;
window.COLISEUM_DATA.VOTES_PER_QUARTER = VOTES_PER_QUARTER;
window.COLISEUM_DATA.seasonUpvotesUsed = seasonUpvotesUsed;
window.COLISEUM_DATA.voteQuota = voteQuota;
