/* Shared data model for the SPIF tracker prototype.
 *
 * "Today" is the reference date that drives all countdowns and statuses.
 * It tracks the REAL current date (local time, normalized to midnight) so
 * every window, countdown, and status stays live as the calendar advances.
 * It is refreshed on every load (see loadFromStorage), so it's always
 * current even for previously-saved data.
 */

const _now = new Date();
const TODAY = new Date(_now.getFullYear(), _now.getMonth(), _now.getDate());

/* Qualifying industry segments for the June Industry Classification SPIFFs
 * (shared by PBD-CAT and PBD-CST). */
const SEGMENTS = [
  { key: "distributor", label: "Distributor", retention: "86%" },
  { key: "oem",         label: "OEM",         retention: "85%" },
  { key: "fboAmo",      label: "FBO / AMO",   retention: "83%" },
];

const SPIFFS = {
  manager: {
    id: "mgr-new-hire-2026q2",
    name: "New-Hire Onboard Bonus",
    audience: "Managers",
    status: "active",
    payout: 500,
    payoutLabel: "$500 per qualifying hire",
    windowDays: 120,
    eligibilityStart: "2026-03-01",
    eligibilityEnd:   "2026-05-30",
    rule: "Manager earns $500 for each new hire onboarded (offer signed by 5/30/26) who closes at least one sale within 120 days of their start date.",
  },
  csam: {
    id: "csam-pbexpo-2026",
    name: "PBExpo Booth Commission",
    audience: "CSAMs",
    status: "active",
    payout: 0.25,
    payoutLabel: "25% commission · first-year booths",
    rule: "CSAM reps earn 25% commission on first-year PBExpo booth sales for accounts in their book of business. Account must not already be worked by a PBExpo rep; renewals ineligible.",
  },
  csamPresentations: {
    id: "csam-presentation-sprint-2026",
    name: "Presentation Sprint",
    audience: "CSAMs",
    status: "active",
    payout: 250,
    payoutLabel: "$250 contest + $1,000 team pool",
    // Individual contest
    contestStart: "2026-05-22",
    contestEnd:   "2026-05-29",
    contestPrize: 250,
    contestMinMst: 6,
    // Team challenge
    teamStart: "2026-05-22",
    teamEnd:   "2026-06-05",
    individualBenchmark: 5,      // presentations per rep
    teamBenchmark: 30,           // collective benchmark (5 each)
    teamTarget: 36,              // benchmark + 20%
    teamPool: 1000,
    ebruId: "c-ebru",
    ebruBonus: 200,
    eligibilityThreshold: 0.8,   // must hit 80% of individual benchmark → 4
    halfDayDate: "2026-06-12",
    rule: "Two challenges. Individual: the CSAM with the most presentations from May 22–29 earns $250 (minimum 6 MST meetings to qualify). Team: if the team conducts 36+ presentations (20% over the 30 benchmark) from May 22–June 5, the team splits a $1,000 pool, Ebru earns an extra $200, and everyone takes a ½ day on June 12. Anyone below 80% of their individual benchmark (4 presentations) is not eligible for payout or PTO.",
  },
  aeDemos: {
    id: "ae-demo-sprint-2026",
    name: "Demo Benchmark Sprint",
    audience: "AEs",
    status: "active",
    payoutLabel: "Highest overall attainment wins",
    start: "2026-06-08",
    end:   "2026-07-03",
    weeks: 4,
    totalExpected: 36,
    categories: [
      { key: "advertising", label: "Advertising",        short: "Ads", weekly: 1 },
      { key: "marketData",  label: "Market Data Reports", short: "Mkt Data", weekly: 1 },
      { key: "integration", label: "Integrations",        short: "Integ.", weekly: 1 },
      { key: "partsStore",  label: "Parts Store",         short: "Store", weekly: 1 },
      { key: "otherSku",    label: "Other Add-on SKUs",   short: "Add-ons", weekly: 5 },
    ],
    weekRanges: [
      { label: "Week 1", start: "2026-06-08", end: "2026-06-12" },
      { label: "Week 2", start: "2026-06-15", end: "2026-06-19" },
      { label: "Week 3", start: "2026-06-22", end: "2026-06-26" },
      { label: "Week 4", start: "2026-06-29", end: "2026-07-03" },
    ],
    rule: "Four-week demo sprint (Jun 8 – Jul 3). Each week, AEs are expected to deliver demos across five categories: 1 Advertising, 1 Market Data Report, 1 Integration, 1 Parts Store, and 5 Other Add-on SKUs — 9 per week, 36 total. Winners are determined by the highest overall benchmark attainment across the five subcategories combined over the full contest. Standings are tracked and shared weekly.",
  },
  catSpiff: {
    id: "pbd-cat-2026-06",
    code: "PBD-CAT",
    name: "Customer Acquisitions SPIFF",
    audience: "AEs",
    status: "active",
    kind: "cat",
    maxPayout: 1000,
    payoutLabel: "Up to $1,000 · industry-segment closes",
    start: "2026-06-01",
    end:   "2026-06-30",
    segments: SEGMENTS,
    tiers: [
      { label: "1st qualifying close in segment", amount: 300 },
      { label: "2nd qualifying close in segment", amount: 400 },
      { label: "3+ closes or highest segment rev", amount: 300, trophy: true },
    ],
    rule: "Close 2+ new deals in a qualifying industry segment by June 30. Applies to new business only — all deals must be fully signed and activated by June 30. $300 for the 1st qualifying close, +$400 for the 2nd, and +$300 (plus the trophy) for 3+ closes or the highest segment revenue.",
  },
  cstSpiff: {
    id: "pbd-cst-2026-06",
    code: "PBD-CST",
    name: "Customer Success SPIFF",
    audience: "CSAMs",
    status: "active",
    kind: "cst",
    maxPayout: 1000,
    payoutLabel: "Up to $1,000 · renewals & upgrades",
    start: "2026-06-01",
    end:   "2026-06-30",
    segments: SEGMENTS,
    tiers: [
      { label: "Full-rate renewal (0% discount) in segment", amount: 300 },
      { label: "Upgrade closed in qualifying segment", amount: 400 },
      { label: "Highest combined segment rev by CSAM", amount: 300, trophy: true },
    ],
    rule: "Renew or upgrade an account in a qualifying segment with zero discount, or expand a current account. Applies to renewals and upgrades — no discount threshold; deals must close by June 30. $300 for a full-rate renewal, +$400 for an upgrade, and +$300 (plus the trophy) for the highest combined segment revenue.",
  },
  partStore: {
    id: "partstore-2026",
    code: "PartStore",
    name: "PartStore SPIFF",
    audience: "AEs & CSAMs",
    status: "active",
    kind: "partStore",
    payoutLabel: "Commission on activated inventory",
    start: "2026-06-01",
    signBy: "2026-06-30",          // last day to sign an eligible deal
    inventoryDeadline: "2026-08-01", // customer must upload inventory by here
    rules: [
      { n: 1, title: "Commission Trigger", body: "Deal signature alone does not qualify. Commission is activated only when the customer lists inventory on PartStore." },
      { n: 2, title: "Closing Window — June 30", body: "All deals signed by June 30 remain eligible. This honors work in progress and provides a clear path to payout." },
      { n: 3, title: "Inventory Upload Deadline — August 1", body: "Customers have until August 1 to upload inventory. Commission is credited once inventory is live." },
    ],
    rule: "Open to all AEs and CSAMs. Commission activates only when the customer lists inventory on PartStore — signature alone does not qualify. All deals signed by June 30 remain eligible; customers have until August 1 to upload inventory, and commission is credited once inventory is live.",
  },
};

/* Manager roster — hires start empty for real data entry. */
const MANAGERS = [
  { id: "m-stefani", name: "Stefani Mikov", title: "Sales Manager", hires: [] },
  { id: "m-eric",    name: "Eric Cicero",   title: "Sales Manager", hires: [] },
];

/* AE Demo Sprint seed. We're at the end of Week 2 (today = Jun 19), so
 * seed demos only across Weeks 1–2 (all dates in the past). buildAeDemos
 * spreads a per-category total across the two elapsed weeks. */
const _aeWeekPools = [
  ["2026-06-08","2026-06-09","2026-06-10","2026-06-11","2026-06-12"], // Week 1
  ["2026-06-15","2026-06-16","2026-06-17","2026-06-18","2026-06-19"], // Week 2
];
const _aeAccounts = ["Delta TechOps","Lufthansa Technik","AAR Corp","StandardAero","GE Aerospace","Honeywell Aero","Collins Aerospace","Safran","MTU Maintenance","ST Engineering","Bombardier","Embraer Services"];
function buildAeDemos(prefix, c) {
  const cats = ["advertising","marketData","integration","partsStore","otherSku"];
  const out = [];
  let k = 0;
  for (const cat of cats) {
    const total = c[cat] || 0;
    for (let i = 0; i < total; i++) {
      const wk = i % _aeWeekPools.length;          // alternate weeks → even split
      const pool = _aeWeekPools[wk];
      const date = pool[(i + k) % pool.length];
      out.push({ id: `${prefix}-${cat}-${i}`, category: cat, date, account: _aeAccounts[(k + i) % _aeAccounts.length] });
    }
    k++;
  }
  return out;
}

/* AE roster — demos start empty for real data entry. (buildAeDemos above
 * is kept so sample data can be re-seeded later if wanted.) */
const AES = [
  { id: "ae-alex",     name: "Alex Georg",         demos: [] },
  { id: "ae-eleonora", name: "Eleonora Paratore",  demos: [] },
  { id: "ae-matteo",   name: "Matteo Pierangeli",  demos: [] },
  { id: "ae-ana",      name: "Ana Folgar",         demos: [] },
  { id: "ae-kateryna", name: "Kateryna Kolisnyk",  demos: [] },
  { id: "ae-lucas",    name: "Lucas Almieda",      demos: [] },
  { id: "ae-fahad",    name: "Fahad Kaisi",        demos: [] },
  { id: "ae-moctar",   name: "Moctar Diaby",       demos: [] },
  { id: "ae-olga",     name: "Olga Zatser",        demos: [] },
  { id: "ae-rebeca",   name: "Rebeca Valero",      demos: [] },
  { id: "ae-stan",     name: "Stan Lee",           demos: [] },
  { id: "ae-shoban",   name: "Shoban Saravanan",   demos: [] },
  { id: "ae-iskandar", name: "Iskandar Rafi",      demos: [] },
  { id: "ae-raja",     name: "Raja Daim",          demos: [] },
  { id: "ae-tabriel",  name: "Tabriel Johnson",    demos: [] },
  { id: "ae-william",  name: "William Chok",       demos: [] },
  { id: "ae-stefaan",  name: "Stefaan Siah",       demos: [] },
];

/* Presentation seed: spread dated demo presentations across the contest
 * window (May 22–29) and the rest of the team window (May 30 – Jun 2).
 * Today is June 2, 2026 — contest is closed, team challenge still running. */
const _contestDays = ["2026-05-22","2026-05-23","2026-05-26","2026-05-27","2026-05-28","2026-05-29"];
const _postDays    = ["2026-05-30","2026-06-01","2026-06-02"];
const _presAccounts = ["Aerodyne Components","SkyParts Intl","Pacific Avionics","Helitech Maintenance","FlightCore Systems","AvionPro Holdings","Lockheed Tier-2","JetStream MRO"];
function buildPres(prefix, contestCount, postCount) {
  const out = [];
  for (let i = 0; i < contestCount; i++)
    out.push({ id: `${prefix}-c${i}`, date: _contestDays[i % _contestDays.length], title: `${_presAccounts[i % _presAccounts.length]} demo` });
  for (let i = 0; i < postCount; i++)
    out.push({ id: `${prefix}-p${i}`, date: _postDays[i % _postDays.length], title: `${_presAccounts[(i + 4) % _presAccounts.length]} demo` });
  return out;
}

/* CSAM roster — presentations, MST meetings, and booth sales start empty
 * for real data entry. (buildPres above is kept for optional re-seeding.) */
const CSAMS = [
  { id: "c-ebru",     name: "Ebru Koknar",         mstMeetings: 0, presentations: [], sales: [] },
  { id: "c-adam",     name: "Adam Morsy",          mstMeetings: 0, presentations: [], sales: [] },
  { id: "c-leonardo", name: "Leonardo Gonzalez",   mstMeetings: 0, presentations: [], sales: [] },
  { id: "c-mona",     name: "Mona Arango",         mstMeetings: 0, presentations: [], sales: [] },
  { id: "c-carlos",   name: "Carlos Salazar",      mstMeetings: 0, presentations: [], sales: [] },
  { id: "c-luigi",    name: "Luigi Zanin Fighera", mstMeetings: 0, presentations: [], sales: [] },
  { id: "c-nadia",    name: "Nadia Herbert",       mstMeetings: 0, presentations: [], sales: [] },
];

/* ---------- helpers ---------- */

function daysBetween(a, b) {
  const ms = new Date(b) - new Date(a);
  return Math.round(ms / (1000 * 60 * 60 * 24));
}

function fmtDate(iso) {
  if (!iso) return "—";
  // Accept either a Date or an ISO string like "2026-03-01" — parse as
  // local time so the printed day matches the input.
  let date;
  if (iso instanceof Date) {
    date = iso;
  } else {
    const [y, m, d] = String(iso).split("-").map(Number);
    date = new Date(y, m - 1, d);
  }
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
}

function fmtDateShort(iso) {
  if (!iso) return "—";
  let date;
  if (iso instanceof Date) {
    date = iso;
  } else {
    const [y, m, d] = String(iso).split("-").map(Number);
    date = new Date(y, m - 1, d);
  }
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
}

function fmtMoney(n) {
  if (n == null) return "—";
  return "$" + Math.round(n).toLocaleString("en-US");
}

/* For a hire: returns
 *   { status, deadline, elapsed, remaining, totalDays, percent, qualified }
 * status ∈ { 'qualified', 'pending', 'expired' }
 */
function hireStatus(hire) {
  const start = new Date(hire.startDate);
  const deadline = new Date(start);
  deadline.setDate(deadline.getDate() + SPIFFS.manager.windowDays);
  const elapsed = daysBetween(hire.startDate, TODAY);
  const remaining = daysBetween(TODAY, deadline);
  const totalDays = SPIFFS.manager.windowDays;
  const percent = Math.max(0, Math.min(100, (elapsed / totalDays) * 100));
  let status = "pending";
  if (hire.firstSale && new Date(hire.firstSale) <= deadline) status = "qualified";
  else if (TODAY > deadline) status = "expired";
  return {
    status, deadline: deadline.toISOString().slice(0,10),
    elapsed, remaining, totalDays, percent,
    qualified: status === "qualified",
  };
}

function managerStats(mgr) {
  const hires = mgr.hires.map(h => ({ ...h, ...hireStatus(h) }));
  const qualified = hires.filter(h => h.status === "qualified").length;
  const pending   = hires.filter(h => h.status === "pending").length;
  const expired   = hires.filter(h => h.status === "expired").length;
  const earned    = qualified * SPIFFS.manager.payout;
  const potential = (pending) * SPIFFS.manager.payout;
  // soonest deadline among pending
  const pendingHires = hires.filter(h => h.status === "pending");
  const soonest = pendingHires.length
    ? pendingHires.reduce((a,b) => a.remaining < b.remaining ? a : b).remaining
    : null;
  return { hires, qualified, pending, expired, earned, potential, soonest, total: hires.length };
}

function csamStats(csam) {
  const sales = csam.sales || [];
  const totalCommission = sales.reduce((s, x) => s + x.commission, 0);
  const totalBooth = sales.reduce((s, x) => s + x.boothValue, 0);
  const paid = sales.filter(s => s.status === "paid").length;
  const pending = sales.filter(s => s.status === "pending").length;
  return { sales, totalCommission, totalBooth, paid, pending, count: sales.length };
}

function orgTotals() {
  const mgr = MANAGERS.map(m => managerStats(m));
  const csam = CSAMS.map(c => csamStats(c));
  return {
    manager: {
      totalHires:     mgr.reduce((s,x) => s + x.total, 0),
      qualified:      mgr.reduce((s,x) => s + x.qualified, 0),
      pending:        mgr.reduce((s,x) => s + x.pending, 0),
      expired:        mgr.reduce((s,x) => s + x.expired, 0),
      earned:         mgr.reduce((s,x) => s + x.earned, 0),
      potential:      mgr.reduce((s,x) => s + x.potential, 0),
    },
    csam: {
      sales:          csam.reduce((s,x) => s + x.count, 0),
      reps:           CSAMS.length,
      repsWithSales:  csam.filter(x => x.count > 0).length,
      commission:     csam.reduce((s,x) => s + x.totalCommission, 0),
      paid:           csam.reduce((s,x) => s + x.paid, 0),
      pending:        csam.reduce((s,x) => s + x.pending, 0),
      boothGross:     csam.reduce((s,x) => s + x.totalBooth, 0),
    },
  };
}

Object.assign(window, {
  TODAY, SPIFFS, MANAGERS, AES, CSAMS,
  daysBetween, fmtDate, fmtDateShort, fmtMoney,
  hireStatus, managerStats, csamStats, orgTotals,
});
