/* ============================================================
   PAGE 1 — PERFORMANCE
   Weekly trend (Revenue / Royalty / EBITDA), auto-refreshing
   performance statements, Top/Bottom franchise-owner contributors.
   ============================================================ */

/* week index → month index (0=Jan) using brand month boundaries */
const MONTH_BOUNDS = [0, 4, 8, 13, 17, 22, 26, 30, 35, 39, 43, 48, 53];
const MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
function weekToMonth(wi) {for (let m = 11; m >= 0; m--) if (wi >= MONTH_BOUNDS[m]) return m;return 0;}
function monthWeekRange(m) {return [MONTH_BOUNDS[m], MONTH_BOUNDS[m + 1]];}

function sumRange(arr, a, b) {let s = 0;for (let i = a; i < b; i++) s += arr[i] || 0;return s;}

// Tone thresholds for plan attainment. Great now requires clear upside to plan;
// the watch band is intentionally tighter so modest misses surface sooner.
const PLAN_TONE_GREAT_PCT = 1;
const PLAN_TONE_WATCH_FLOOR_PCT = -1;

const MONTH_NAMES_FULL = ["January","February","March","April","May","June","July","August","September","October","November","December"];
const isNumP = (v) => typeof v === "number" && !isNaN(v);

/* build the plain-language performance statements.
   Revenue/Royalty: 3 weekly-based statements (this week / MTD / YTD).
   EBITDA: 2 month-based statements (last completed month / YTD) because
   EBITDA only arrives monthly from the P&L — no weekly/current-week figure. */
function buildStatements(series, metricLabel, metric) {
  const cw = MM.config.currentWeek; // latest actual week (1-based)
  const ci = cw - 1; // 0-based index
  const r26 = series.y2026,r25 = series.y2025,plan = series.plan;

  function cmp(cur, base) {const d = cur - base;return { d, pct: base ? d / base * 100 : 0 };}

  // Holistic verdict: reconcile YoY growth AND attainment vs plan into one
  // honest headline + tone. Growing YoY but behind plan should NOT read as an
  // unqualified "strong result".
  function verdict(yoyPct, planPct) {
    // -2..2: strong ahead / ahead / on-plan / watch / behind.
    // Plan now needs clear positive attainment for an "ahead" read, and
    // small misses move into watch sooner instead of being treated as on plan.
    const band = (p) => p >= 5 ? 2 : p >= PLAN_TONE_GREAT_PCT ? 1 : p >= 0 ? 0 : p > PLAN_TONE_WATCH_FLOOR_PCT ? -1 : -2;
    const y = band(yoyPct), pl = band(planPct);
    const up = (s) => (s >= 0 ? "+" : "");
    // both strong
    if (y >= 1 && pl >= 1) return { word: "ahead on both fronts", tone: "great" };
    // growing but missing plan (the case the user flagged)
    if (y >= 1 && pl <= -1) return { word: "growing YoY but behind plan", tone: "average" };
    if (y >= 1 && pl === 0) return { word: "growing YoY, on plan", tone: "average" };
    // flat/declining YoY
    if (y <= -1 && pl <= -1) return { word: "behind on plan and last year", tone: "opportunity" };
    if (y <= -1 && pl >= 1) return { word: "below last year but ahead of plan", tone: "average" };
    if (y <= -1) return { word: "below last year", tone: "opportunity" };
    // roughly flat YoY
    if (pl >= 1) return { word: "on last year, ahead of plan", tone: "great" };
    if (pl <= -1) return { word: "on last year but behind plan", tone: "average" };
    return { word: "holding steady, on plan", tone: "info" };
  }

  // 1) current week vs same week last year + vs plan
  const wkYoY = cmp(r26[ci], r25[ci]);
  const wkPlan = cmp(r26[ci], plan[ci]);
  const wkT = verdict(wkYoY.pct, wkPlan.pct);

  // 2) month-to-date: weeks of current month with actuals
  //    (week 1 of a month → use the PRIOR completed month)
  let mIdx = weekToMonth(ci);
  const [ms] = monthWeekRange(mIdx);
  if (ci === ms) {mIdx = Math.max(0, mIdx - 1);} // first week of month → prior month
  const [a, b] = monthWeekRange(mIdx);
  const bb = Math.min(b, cw); // only through current week
  const mo26 = sumRange(r26, a, bb),mo25 = sumRange(r25, a, bb),moPlan = sumRange(plan, a, bb);
  const moYoY = cmp(mo26, mo25),moPlanC = cmp(mo26, moPlan);
  const moT = verdict(moYoY.pct, moPlanC.pct);

  // 3) YTD
  const y26 = sumRange(r26, 0, cw),y25 = sumRange(r25, 0, cw),yPlan = sumRange(plan, 0, cw);
  const ytdYoY = cmp(y26, y25),ytdPlan = cmp(y26, yPlan);
  const ytdT = verdict(ytdYoY.pct, ytdPlan.pct);

  // recent trend: last 4 weeks vs the prior 4 weeks (momentum signal for YTD)
  const rs = Math.max(0, cw - 4), ps = Math.max(0, cw - 8);
  const last4 = sumRange(r26, rs, cw), prev4 = sumRange(r26, ps, rs);
  const trendPct = prev4 ? (last4 - prev4) / prev4 * 100 : 0;
  const trendWord = trendPct >= 2 ? "and momentum is building" : trendPct <= -2 ? "and momentum is cooling" : "with steady momentum";

  // contextual reason — attributed to drivers + the owners that moved most.
  // recomputes every week from live data.
  var reason = window.MM_INSIGHTS ? MM_INSIGHTS.statementReason(ytdYoY.pct) : null;

  // ---- EBITDA: month-based statements from the P&L points ----
  if (metric === "ebitda") {
    const eb = MM.ebitdaInfo || {};
    const pts = (eb.points || []).slice().sort((p, q) => p.month - q.month);
    if (!pts.length) {
      return [{ tag: "EBITDA", scope: "Awaiting P&L", tone: "info",
        text: <>EBITDA actuals will populate here once the monthly P&amp;L files are loaded.</> }];
    }
    const last = pts[pts.length - 1];
    const prev = pts.length > 1 ? pts[pts.length - 2] : null;
    // last completed month = cumulative latest minus cumulative prior month
    const mAct = last.actual - (prev ? prev.actual : 0);
    const mBud = (last.budget != null && (!prev || prev.budget != null)) ? last.budget - (prev ? prev.budget : 0) : null;
    const mPri = (last.prior != null && (!prev || prev.prior != null)) ? last.prior - (prev ? prev.prior : 0) : null;
    const mYoY = cmp(mAct, mPri || 0), mPlanC = cmp(mAct, mBud || 0);
    const mT = verdict(mYoY.pct, mPlanC.pct);
    // YTD = latest cumulative point
    const eYtd = cmp(last.actual, last.prior || 0), eYtdPlan = cmp(last.actual, last.budget || 0);
    const eT = verdict(eYtd.pct, eYtdPlan.pct);
    const monthName = MONTH_NAMES_FULL[last.month - 1];
    // (No driver/owner attribution for EBITDA — it moves on cost & margin, not
    //  the revenue drivers; attaching the revenue reason would mislead.)
    return [
      { tag: monthName, scope: "Last completed month", tone: mT.tone,
        text: <>was <b>{mT.word}</b> — EBITDA of <b>{MM.fmt.money(mAct)}</b>{isNumP(mPri) ? <>, {ptext(mYoY)} YoY</> : null}{isNumP(mBud) ? <> and {ptext(mPlanC)} vs plan</> : null}</> },
      { tag: "2026 YTD", scope: "Year to date · thru " + monthName, tone: eT.tone,
        text: <>is <b>{eT.word}</b> — <b>{MM.fmt.money(last.actual)}</b>{isNumP(last.prior) ? <>, {ptext(eYtd)} YoY</> : null}{isNumP(last.budget) ? <> and {ptext(eYtdPlan)} vs plan</> : null}</> }];
  }

  return [
  { tag: "W" + String(cw).padStart(2, "0"), scope: "This week",
    tone: wkT.tone, reason: reason,
    text: <>was <b>{wkT.word}</b> — {metricLabel} of <b>{MM.fmt.money(r26[ci])}</b>, {ptext(wkYoY)} YoY and {ptext(wkPlan)} vs plan</> },
  { tag: MONTH_NAMES[mIdx], scope: "Month to date",
    tone: moT.tone, reason: reason,
    text: <>was <b>{moT.word}</b> — <b>{MM.fmt.money(mo26)}</b> so far, {ptext(moYoY)} YoY and {ptext(moPlanC)} vs plan</> },
  { tag: "2026 YTD", scope: "Year to date",
    tone: ytdT.tone, reason: reason,
    text: <>is <b>{ytdT.word}</b> {trendWord} — <b>{MM.fmt.money(y26)}</b>, {ptext(ytdYoY)} YoY and {ptext(ytdPlan)} vs plan</> }];

}
function ptext(c) {
  const up = c.d >= 0;
  return <span className={"st-num " + (up ? "up" : "down")}>{MM.fmt.money(c.d, { signed: true })} ({MM.fmt.pct(c.pct)})</span>;
}

/* ---------- KPI budget-goal card ---------- */
const MONTH_FULL = ["January","February","March","April","May","June","July","August","September","October","November","December"];
function GoalKpi({ label, info, metric }) {
  const s = MM.weeklySeries(metric, []); // whole system
  const cw = MM.config.currentWeek;
  // EBITDA actuals come from monthly P&L and may lag the weekly revenue data.
  const ebInfo = (MM.ebitdaInfo) || {};
  const ebThroughWeek = metric === "ebitda" ? (ebInfo.throughWeek || 0) : 0;
  const ebMonth = metric === "ebitda" && ebInfo.latestMonth ? MONTH_FULL[ebInfo.latestMonth - 1] : null;
  // for EBITDA, only count up to the last P&L week (actuals lag revenue)
  const effWeek = metric === "ebitda" && ebThroughWeek ? ebThroughWeek : cw;
  // do we actually have any real 2026 actuals in the window?
  const hasActuals = s.y2026.slice(0, effWeek).some((v) => typeof v === "number" && !isNaN(v));
  const ytd = sumRange(s.y2026, 0, effWeek);
  // For EBITDA, use the P&L's own budget figure (accurate) instead of the
  // synthetic plan shape. Take the latest month's cumulative budget point.
  let ytdPlan;
  if (metric === "ebitda" && ebInfo.points && ebInfo.points.length) {
    const last = ebInfo.points[ebInfo.points.length - 1];
    ytdPlan = (typeof last.budget === "number") ? last.budget : sumRange(s.plan, 0, effWeek);
  } else {
    ytdPlan = sumRange(s.plan, 0, effWeek);
  }
  const target = metric === "ebitda" ? MM.goals.ebitda.target :
  metric === "royalty" ? MM.goals.royalty.target :
  MM.goals.revenue.target;
  const vsPlan = ytdPlan ? (ytd - ytdPlan) / ytdPlan * 100 : 0;
  const aheadTone = !hasActuals ? "average" :
    vsPlan >= PLAN_TONE_GREAT_PCT ? "great" :
    vsPlan > PLAN_TONE_WATCH_FLOOR_PCT ? "average" :
    "opportunity";
  if (!hasActuals) {
    return (
      <div className="goalkpi">
        <div className="goalkpi-top">
          <span className="goalkpi-label">{label}{info && <InfoDot text={info} />}</span>
          <Pill tone="average" soft>actuals pending</Pill>
        </div>
        <div className="goalkpi-val">—<span className="goalkpi-of"> YTD</span></div>
        <Progress value={0} max={target} marker={ytdPlan} markerLabel={"Plan to date: " + MM.fmt.money(ytdPlan)} tone="average" height={9} />
        <div className="goalkpi-foot">
          <span>Awaiting P&amp;L upload</span>
          <span className="goalkpi-target">Target {MM.fmt.money(target, { dp: 1 })}</span>
        </div>
      </div>);
  }
  return (
    <div className="goalkpi">
      <div className="goalkpi-top">
        <span className="goalkpi-label">{label}{info && <InfoDot text={info} />}</span>
        <Pill tone={aheadTone} soft>{MM.fmt.pct(vsPlan)} vs plan</Pill>
      </div>
      <div className="goalkpi-val">{MM.fmt.money(ytd)}<span className="goalkpi-of"> YTD{ebMonth ? " · thru " + ebMonth : ""}</span></div>
      <Progress value={ytd} max={target} marker={ytdPlan} markerLabel={"Plan to date: " + MM.fmt.money(ytdPlan) + (ebMonth ? " (through " + ebMonth + ")." : " — where the 2026 plan says you should be by Week " + MM.config.currentWeek + ".")} tone={aheadTone} height={9} />
      <div className="goalkpi-foot">
        <span>{MM.fmt.pct(ytd / target * 100, 0).replace("+", "")} of annual budget</span>
        <span className="goalkpi-target">Target {MM.fmt.money(target, { dp: 1 })}</span>
      </div>
    </div>);

}

/* ---------- Contributors list ---------- */
function ContributorList({ rows, kind, onPick }) {
  const maxAbs = Math.max(...rows.map((r) => Math.abs(r.yoyDelta)), 1);
  return (
    <ol className="clist">
      {rows.map((r, i) =>
      <li className="crow clickable" key={r.id}
      onClick={() => onPick && onPick(r.id)} role="button" tabIndex={0}
      onKeyDown={(e) => {if (e.key === "Enter" || e.key === " ") {e.preventDefault();onPick && onPick(r.id);}}}>
          <span className="crank">{i + 1}</span>
          <span className="cmain">
            <span className="cname">{r.contact}</span>
            <span className="cmeta"><FbcTag id={r.fbcId} /><span className="cstate">{r.city}, {r.state}</span></span>
          </span>
          <span className="cbarwrap">
            <span className={"cbar " + (kind === "top" ? "up" : "down")}
          style={{ width: Math.abs(r.yoyDelta) / maxAbs * 100 + "%" }}></span>
          </span>
          <span className="cnums">
            <span className={"cdelta " + (r.yoyDelta >= 0 ? "up" : "down")}>{MM.fmt.money(r.yoyDelta, { signed: true })}</span>
            <span className="cpct">{MM.fmt.pct(r.yoyPct)} · {MM.fmt.money(r.current)}</span>
          </span>
        </li>
      )}
    </ol>);

}

/* ---------- The page ---------- */
function PerformancePage({ metric, setMetric, fbcIds, setFbcIds }) {
  const [picked, setPicked] = React.useState(null);
  const [contribRange, setContribRange] = React.useState("ytd");
  const cw = MM.config.currentWeek;
  const RANGE_OPTS = [
  { value: "ytd", label: "YTD" },
  { value: "rolling", label: "Rolling 4 wks" },
  { value: "week", label: "W" + String(cw).padStart(2, "0") }];
  const RLAB = { ytd: "YTD", rolling: "4-Wk", week: "W" + String(cw).padStart(2, "0") };
  const OwnerDrawer = window.OwnerDrawer;
  // open the same owner drawer used on the FBO Detail page (same time window)
  function pickOwner(id) {
    const o = MM.owners.find((x) => x.id === id);
    if (o && window.deriveOwner) setPicked(window.deriveOwner(o, contribRange));
  }
  const isEbitda = metric === "ebitda";
  const effFbc = isEbitda ? [] : fbcIds;
  const series = MM.weeklySeries(metric, effFbc);
  const metricLabel = isEbitda ? "EBITDA" : metric === "royalty" ? "Royalty" : "Revenue";

  const lineSeries = [
  { key: "y2024", name: "2024", color: "#9aa2bd", type: "line", data: series.y2024 },
  { key: "y2025", name: "2025", color: "#071d49", type: "line", data: series.y2025 },
  { key: "y2026", name: "2026", color: "#e22f7c", type: "line", data: series.y2026 },
  { key: "plan", name: "2026 plan", color: "#1f8a5b", type: "plan", data: series.plan }];

  const statements = buildStatements(series, metricLabel, metric);

  // contributors follow the metric (EBITDA isn't per-owner → fall back to revenue)
  const cMetric = isEbitda ? "revenue" : metric;
  const contrib = MM.contributors(cMetric, effFbc, contribRange);
  const top = contrib.slice(0, 10);
  const bottom = contrib.slice(-10).reverse();

  const scopeLabel = isEbitda ? "Whole system" :
  fbcIds.length === 0 || fbcIds.length === MM.fbcs.length ? "Whole system" :
  fbcIds.length === 1 ? MM.fbcs.find((f) => f.id === fbcIds[0]).name + "’s book" :
  fbcIds.length + " FBCs";

  return (
    <div className="page" data-comment-anchor="2e9a27af2e-div-157-5">
      {/* control bar */}
      <div className="page-controls">
        <div className="controls-left">
          <FbcFilter fbcs={MM.fbcs} selected={fbcIds} onChange={setFbcIds}
          disabled={isEbitda}
          note={isEbitda ? "EBITDA is system-wide only" : null} />
          <Segmented
            options={[
            { value: "revenue", label: "Revenue" },
            { value: "royalty", label: "Royalty" },
            { value: "ebitda", label: "EBITDA" }]
            }
            value={metric} onChange={setMetric} />
          <span className="control-hint">
            {metric === "revenue" ? "Total cleaning revenue across the network." :
            metric === "royalty" ? "Royalty earned on network revenue (≈6%)." :
            "Franchisor EBITDA — reported system-wide only."}
          </span>
        </div>
      </div>

      {/* budget goals (page-1 goals: Royalty + EBITDA, with Revenue for context) */}
      <div className="goal-strip">
        <GoalKpi label="Revenue" metric="revenue"
        info="Total network cleaning revenue, year-to-date vs. the 2026 plan. The marker shows where plan says we should be by now." />
        <GoalKpi label="Royalty" metric="royalty"
        info="Goal ii — Achieve 2026 Total Molly Maid Royalty Budget above $19,432,852." />
        <GoalKpi label="EBITDA" metric="ebitda"
        info="Goal i — Achieve 2026 Total Molly Maid EBITDA Budget above $17,145,735. Reported system-wide only." />
      </div>

      {/* line chart */}
      <Card
        eyebrow={"Weekly trend · " + scopeLabel}
        title={metricLabel + " by Week — 53-Week View"}
        info="Each line is a full fiscal year of weekly totals. The dotted green line is the 2026 plan. Hover any week to see the dollar and percent difference from 2025 and from plan."
        sub="Hover any week for year-over-year and vs-plan differences.">
        
        <LineChart
          series={lineSeries}
          currentWeek={MM.config.currentWeek}
          formatY={(v, axis) => MM.fmt.money(v, { dp: axis ? 1 : 2 })} />
        
      </Card>

      {/* performance statements */}
      <Card
        eyebrow="Performance update"
        title="What the numbers are telling us"
        info={isEbitda
          ? "Plain-language read-outs for EBITDA. Because EBITDA is reported monthly from the P&L, it shows the last completed month and the year to date — there's no current-week figure."
          : "Plain-language read-outs that recalculate every week — this week, the current month to date, and the year to date, each reconciling year-over-year and vs-plan with the underlying drivers."}>
        
        <div className="statements">
          {statements.map((s, i) =>
          <div className="statement" key={i}>
              <div className={"st-tag tone-" + s.tone}>{s.tag}</div>
              <div className="st-body">
                <div className="st-scope">{s.scope}</div>
                <p className="st-text">{s.text}</p>
                {s.reason && <div className="st-reason">due to {s.reason.text}</div>}
              </div>
            </div>
          )}
        </div>
      </Card>

      {/* contributors */}
      <div className="contrib-head">
        <div className="contrib-head-text">
          <div className="eyebrow">Franchise owners</div>
          <h3 className="contrib-head-title">Top &amp; Bottom 10 Movers</h3>
        </div>
        <div className="contrib-range">
          <span className="contrib-range-lab">Time period
            <InfoDot text="Sets the window for the Top 10 and Bottom 10 below — year-to-date, the rolling last 4 weeks, or just the latest week. Mirrors the time filter on the Book of Business page." /></span>
          <Segmented size="sm" options={RANGE_OPTS} value={contribRange} onChange={setContribRange} />
        </div>
      </div>
      <div className="contrib-grid">
        <Card
          eyebrow="Movers"
          title="Top 10 Growth Contributors"
          info="Franchise owners adding the most year-over-year over the selected window. Ranked by dollar change."
          sub={isEbitda ? "Showing revenue — EBITDA isn’t tracked per owner." : "Biggest year-over-year gains · " + RLAB[contribRange]}>
          
          <ContributorList rows={top} kind="top" onPick={pickOwner} />
        </Card>
        <Card
          eyebrow="Prioritize"
          title="Bottom 10 YoY Growth"
          info="Franchise owners losing the most year-over-year over the selected window. These are your fastest opportunities to coach — start here."
          sub={"Biggest year-over-year declines · " + RLAB[contribRange]}>
          
          <ContributorList rows={bottom} kind="bottom" onPick={pickOwner} />
        </Card>
      </div>

      {OwnerDrawer && <OwnerDrawer owner={picked} onClose={() => setPicked(null)} rangeLabel={RLAB[contribRange]} />}
    </div>);

}

window.PerformancePage = PerformancePage;