/* ============================================================
   PAGE 3 — TERRITORY (Map + Watch List + Explorer)
   Geographic map (toggle any driver) on top; a prioritized
   Watch List and a fully filterable owner Explorer below.
   Built so an FBC can filter to their own book in one click.
   ============================================================ */

/* contacts pace: 10/yr goal prorated to where we are in the year (Book of Business) */
const EXP_CONTACTS_GOAL = (MM.goals.engagement && MM.goals.engagement.targetYear) || 10;
const EXP_CONTACTS_PACE = MM.config.currentWeek / MM.config.weeks * EXP_CONTACTS_GOAL;
const expContactsTone = (c) => c >= EXP_CONTACTS_PACE ? "great" : c >= EXP_CONTACTS_PACE * 0.6 ? "average" : "opportunity";

/* revenue window for the selected time filter (shared by map, watch & explorer) */
function rangeWindow(o, range) {
  const ci = MM.config.currentWeek - 1;
  if (range === "week") return { cur: o.weekly.rev2026[ci] || 0, prior: o.weekly.rev2025[ci] || 0 };
  if (range === "rolling") {
    let c = 0,p = 0;
    for (let i = Math.max(0, ci - 3); i <= ci; i++) {c += o.weekly.rev2026[i] || 0;p += o.weekly.rev2025[i] || 0;}
    return { cur: c, prior: p };
  }
  return { cur: o.ytd2026, prior: o.ytd2025 };
}

/* derive the per-owner metrics the page needs (single place to swap).
   `range` selects the revenue window so the whole page honours the filter. */
function deriveOwner(o, range) {
  range = range || "ytd";
  const w = rangeWindow(o, range);
  const rev = w.cur,priorRev = w.prior;
  const growth = priorRev ? (rev - priorRev) / priorRev * 100 : 0;
  const convYoy = o.conv2026 - o.conv2025;
  const freqYoy = o.freq2025 ? (o.freq2026 - o.freq2025) / o.freq2025 * 100 : 0; // YoY % change
  const churnYoy = o.churn2026 - o.churn2025; // + = churn rose (bad)
  const fbc = MM.fbcs.find((f) => f.id === o.fbcId);
  const flags = [];
  if (growth < 0) flags.push({ k: "declining", label: "Declining revenue", tone: "opportunity", sev: Math.abs(rev - priorRev) });
  if (o.contactsYtd < EXP_CONTACTS_PACE) flags.push({ k: "contacts", label: o.contactsYtd === 0 ? "No FranConnect contacts" : "Behind on FranConnect contacts", tone: growth < 0 || o.contactsYtd === 0 ? "opportunity" : "average", sev: (growth < 0 ? 9e5 : 2e5) + (EXP_CONTACTS_PACE - o.contactsYtd) * 4e4 });
  if (convYoy < 0) flags.push({ k: "conv", label: "Conversion slipping", tone: "average", sev: Math.abs(convYoy) * 4e4 });
  if (churnYoy > 0) flags.push({ k: "churn", label: "Churn rising", tone: "average", sev: churnYoy * 5e4 });
  if (freqYoy < 0) flags.push({ k: "freq", label: "Frequency down", tone: "average", sev: Math.abs(freqYoy) * 1e6 });
  const priority = flags.reduce((s, f) => s + f.sev, 0);
  return {
    ...o, rev, priorRev, growth, convYoy, freqYoy, churnYoy, fbc, flags, priority,
    npsSpring: fbc ? fbc.npsSpring : 0
  };
}

const MAP_METRICS = [
{ value: "revenue", label: "Revenue", hint: "Dot color shows revenue size in the selected window — darker pink is higher." },
{ value: "growth", label: "YoY Growth", hint: "Dot color shows revenue growth vs 2025 — red dots are shrinking, green are growing." },
{ value: "conversion", label: "Conversion", hint: "Dot color shows the year-over-year change in estimates closed." },
{ value: "frequency", label: "Frequency", hint: "Dot color shows the year-over-year % change in cleans per customer — any positive change is the goal." },
{ value: "retention", label: "Retention", hint: "Dot color shows churn improvement vs last year — green dots improved." },
{ value: "engagement", label: "Engagement", hint: "Dot color shows each owner's FranConnect contacts logged YTD against the 10-per-year pace." },
{ value: "nps", label: "NPS", hint: "Dot color shows each franchisee's NPS rating — promoter, passive or detractor." }];


/* "I'm viewing as" quick filter + page-wide time window */
function ViewAsBar({ fbcIds, setFbcIds, range, setRange }) {
  const isAll = fbcIds.length === 0 || fbcIds.length === MM.fbcs.length;
  const single = fbcIds.length === 1 ? fbcIds[0] : null;
  return (
    <div className="viewas">
      <span className="viewas-lab">I’m viewing as
        <InfoDot text="Pick your name to instantly filter the map, watch list and explorer to just your book of business." /></span>
      <div className="viewas-pills">
        <button className={"viewas-pill" + (isAll ? " on" : "")} onClick={() => setFbcIds([])}>Whole system</button>
        {MM.fbcs.map((f) =>
        <button key={f.id} className={"viewas-pill" + (single === f.id ? " on" : "")}
        onClick={() => setFbcIds([f.id])}>
            <span className="fbcdot" style={{ background: f.color }}></span>{f.name}
          </button>
        )}
      </div>
      <div className="viewas-range">
        <span className="viewas-range-lab">Time window
          <InfoDot text="Sets the revenue window for the whole page. Every card below — the map, the concern callouts and the owner table — recalculates against the same period you pick here." /></span>
        <Segmented size="sm"
        options={[{ value: "ytd", label: "YTD" }, { value: "rolling", label: "Rolling 4 wks" }, { value: "week", label: "W" + String(MM.config.currentWeek).padStart(2, "0") }]}
        value={range} onChange={setRange} />
      </div>
    </div>);

}

/* ---- Explorer table (a.k.a. Book of Business) ---- */
const COLS = [
{ key: "name", label: "Franchise Owner", num: false },
{ key: "rev", label: "Revenue", num: true, fmt: (v) => MM.fmt.money(v) },
{ key: "growth", label: "YoY Growth", num: true, fmt: (v) => MM.fmt.pct(v), tone: (v) => v >= 0 ? "up" : "down" },
{ key: "convYoy", label: "Conv YoY", num: true, fmt: (v) => (v >= 0 ? "+" : "") + v.toFixed(1), tone: (v) => v >= 0 ? "up" : "down" },
{ key: "freqYoy", label: "Freq YoY", num: true, fmt: (v) => MM.fmt.pct(v), tone: (v) => v > 0 ? "up" : "down" },
{ key: "churnYoy", label: "Churn YoY", num: true, fmt: (v) => (v >= 0 ? "+" : "") + v.toFixed(1), tone: (v) => v <= 0 ? "up" : "down" },
{ key: "contactsYtd", label: "Contacts YTD", num: true }];


function Explorer({ owners, stateFilter, setStateFilter, onPick, rangeLabel, range }) {
  const [q, setQ] = useState("");
  const [sort, setSort] = useState({ key: "priority", dir: "desc" });
  const [chips, setChips] = useState({ concern: false });
  const toggleChip = (k) => setChips((c) => ({ ...c, [k]: !c[k] }));

  let rows = owners.slice();
  if (stateFilter) rows = rows.filter((o) => o.state === stateFilter);
  if (q.trim()) {
    const s = q.toLowerCase();
    rows = rows.filter((o) => o.name.toLowerCase().includes(s) || o.city.toLowerCase().includes(s) || o.state.toLowerCase().includes(s) || o.contact.toLowerCase().includes(s));
  }
  if (chips.concern) rows = rows.filter((o) => o.flags.length);
  if (chips.declining) rows = rows.filter((o) => o.growth < 0);
  if (chips.lowcontacts) rows = rows.filter((o) => o.contactsYtd < EXP_CONTACTS_PACE);
  if (chips.churn) rows = rows.filter((o) => o.churnYoy > 0);
  if (chips.conv) rows = rows.filter((o) => o.convYoy < 0);

  const concernCount = owners.filter((o) => o.flags.length).length;

  rows.sort((a, b) => {
    let A = a[sort.key],B = b[sort.key];
    if (sort.key === "name") {A = a.name;B = b.name;}
    if (typeof A === "string") return sort.dir === "asc" ? A.localeCompare(B) : B.localeCompare(A);
    if (typeof A === "boolean") {A = A ? 1 : 0;B = B ? 1 : 0;}
    return sort.dir === "asc" ? A - B : B - A;
  });

  function setSortKey(k) {
    setSort((s) => s.key === k ? { key: k, dir: s.dir === "asc" ? "desc" : "asc" } : { key: k, dir: k === "name" ? "asc" : "desc" });
  }

  const cols = COLS.map((c) => c.key === "rev" ? { ...c, label: "Revenue " + (rangeLabel || "YTD") } : c);

  return (
    <Card eyebrow="Your book" title="Book of Business"
    info="Every owner in your view — search, sort any column, and stack filters. Owners with one or more concern callouts are flagged inline; use Concerns only to focus on those who need attention this period."
    right={<div className="exp-head-right">
        {concernCount > 0 && <Pill tone="opportunity" soft>{concernCount + " concern" + (concernCount !== 1 ? "s" : "")}</Pill>}
        <span className="exp-count">{rows.length} owner{rows.length !== 1 ? "s" : ""}</span>
      </div>}>
      <p className="range-note">{rangeStatement(range)}</p>
      <div className="exp-controls" data-comment-anchor="a69ff412c4-div-132-7">
        <div className="exp-search">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
          <input placeholder="Search owner, city, or contact…" value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
        <div className="exp-chips">
          <button className={"exp-chip concern" + (chips.concern ? " on" : "")} onClick={() => toggleChip("concern")}>
            <span className="concern-ic" aria-hidden="true">▲</span>Concerns only
          </button>
          <button className={"exp-chip" + (chips.declining ? " on" : "")} onClick={() => toggleChip("declining")}>Declining revenue</button>
          <button className={"exp-chip" + (chips.lowcontacts ? " on" : "")} onClick={() => toggleChip("lowcontacts")}>Behind on contacts</button>
          <button className={"exp-chip" + (chips.churn ? " on" : "")} onClick={() => toggleChip("churn")}>Churn rising</button>
          <button className={"exp-chip" + (chips.conv ? " on" : "")} onClick={() => toggleChip("conv")}>Conversion down</button>
        </div>
        {stateFilter &&
        <button className="exp-statefilter" onClick={() => setStateFilter(null)}>
            {window.US_STATE_NAMES[stateFilter]} ✕
          </button>
        }
      </div>
      <div className="exp-scroll">
        <table className="exp-table">
          <thead>
            <tr>
              {cols.map((c) =>
              <th key={c.key} className={c.num ? "num" : ""} onClick={() => setSortKey(c.key)}>
                  <span className="th-inner">{c.label}<span className={"th-arrow" + (sort.key === c.key ? " on" : "")}>{sort.key === c.key ? sort.dir === "asc" ? "▲" : "▼" : "▾"}</span></span>
                </th>
              )}
            </tr>
          </thead>
          <tbody>
            {rows.map((o) =>
            <tr key={o.id} onClick={() => onPick(o)}>
                <td>
                  <button className="exp-owner exp-owner-button" type="button" onClick={(e) => {e.stopPropagation();onPick(o);}} aria-label={"Open owner details for " + o.contact + " in " + o.city + ", " + o.state}>{o.contact}</button>
                  <span className="exp-sub">{o.city}, {o.state}</span>
                  {o.flags.length > 0 &&
                  <span className="exp-flags">
                    {o.flags.slice(0, 3).map((f) => <span key={f.k} className={"flag tone-" + f.tone}>{f.label}</span>)}
                  </span>
                  }
                </td>
                {COLS.slice(1, -1).map((c) =>
              <td key={c.key} className={"num" + (c.tone ? " tone-" + c.tone(o[c.key]) : "")}>{c.fmt(o[c.key])}</td>
              )}
                <td className="num"><span className={"contacts-cell tone-" + expContactsTone(o.contactsYtd)}>{o.contactsYtd}<span className="contacts-goal">/10</span></span></td>
              </tr>
            )}
          </tbody>
        </table>
        {rows.length === 0 && <p className="empty">No owners match these filters.</p>}
      </div>
    </Card>);

}

/* ---- owner detail drawer ---- */
function OwnerDrawer({ owner, onClose, rangeLabel }) {
  if (!owner) return null;
  const m = [
  { l: "Revenue " + (rangeLabel || "YTD"), v: MM.fmt.money(owner.rev != null ? owner.rev : owner.ytd2026), t: null },
  { l: "YoY Growth", v: MM.fmt.pct(owner.growth), t: owner.growth >= 0 ? "great" : "opportunity" },
  { l: "Conversion YoY", v: (owner.convYoy >= 0 ? "+" : "") + owner.convYoy.toFixed(1) + " pts", t: owner.convYoy >= 0 ? "great" : "opportunity" },
  { l: "Frequency YoY", v: MM.fmt.pct(owner.freqYoy), t: owner.freqYoy > 0 ? "great" : "opportunity" },
  { l: "Churn YoY", v: (owner.churnYoy >= 0 ? "+" : "") + owner.churnYoy.toFixed(1) + " pts", t: owner.churnYoy <= 0 ? "great" : "opportunity" },
  { l: "FranConnect Contacts", v: owner.contactsYtd + " / 10 YTD", t: expContactsTone(owner.contactsYtd) }];

  return (
    <div className="drawer-scrim" onClick={onClose}>
      <aside className="drawer" onClick={(e) => e.stopPropagation()}>
        <div className="drawer-head">
          <div>
            <div className="eyebrow">Franchise Owner</div>
            <h3 className="drawer-title">{owner.contact}</h3>
            <p className="drawer-sub" data-comment-anchor="5319b15c08-p-224-13">{owner.name} · {owner.city}, {owner.state} · Est. {owner.yearStarted} · License {owner.license} · <FbcTag id={owner.fbcId} /></p>
          </div>
          <button className="drawer-x" onClick={onClose} aria-label="Close">✕</button>
        </div>
        {owner.flags.length > 0 &&
        <div className="drawer-flags">
            {owner.flags.map((f) => <span key={f.k} className={"flag tone-" + f.tone}>{f.label}</span>)}
          </div>
        }
        <div className="drawer-metrics">
          {m.map((x) =>
          <div className="drawer-metric" key={x.l}>
              <span className="dm-l">{x.l}</span>
              <span className={"dm-v" + (x.t ? " tone-" + x.t : "")}>{x.v}</span>
            </div>
          )}
        </div>
        <div className="drawer-note">
          <span className="eyebrow">Next best action</span>
          {(function () {
            var a = window.MM_INSIGHTS ? MM_INSIGHTS.ownerActions(owner, { limit: 1 })[0] : null;
            return a
              ? <div className={"drawer-action tone-" + a.tone}><span className="da-title">{a.title}</span><p className="da-detail">{a.detail}</p></div>
              : null;
          })()}
        </div>
        {window.__openFboDetail &&
        <button className="drawer-detail-cta" onClick={() => {window.__openFboDetail(owner.id);onClose();}}>
            Open full coaching detail
            <span className="ddc-arrow" aria-hidden="true">→</span>
          </button>
        }
      </aside>
    </div>);

}

const RANGE_SHORT = { ytd: "YTD", week: "Wk " + MM.config.currentWeek, rolling: "4-Wk" };

/* light-grey reminder of the active time window, shown inside each card below */
function rangeStatement(range) {
  const cw = MM.config.currentWeek;
  if (range === "week") return "Showing Week " + cw + " only · " + MM.weekDates(cw);
  if (range === "rolling") {
    const a = MM.weekDates(cw - 3).split("–")[0];
    const b = MM.weekDates(cw).split("–")[1];
    return "Showing the rolling last 4 weeks · weeks " + (cw - 3) + "–" + cw + " · " + a + "–" + b;
  }
  return "Showing year-to-date · weeks 1–" + cw + " of 2026";
}

function TerritoryPage({ fbcIds, setFbcIds }) {
  const [metric, setMetric] = useState("growth");
  const [range, setRange] = useState("ytd");
  const [stateFilter, setStateFilter] = useState(null);
  const [picked, setPicked] = useState(null);
  const derived = MM.ownersFor(fbcIds).map((o) => deriveOwner(o, range));
  const mHint = MAP_METRICS.find((x) => x.value === metric).hint;
  const rShort = RANGE_SHORT[range];

  return (
    <div className="page">
      <ViewAsBar fbcIds={fbcIds} setFbcIds={setFbcIds} range={range} setRange={setRange} />

      <Card eyebrow="Geography" title="Location Map"
      info="Every franchise location plotted as a dot. Dot size is the owner's revenue; dot color is the metric you pick below. Hover a dot for detail, click it to open the owner, or click a state to filter the table below.">
        <p className="range-note">{rangeStatement(range)}</p>
        <div className="map-metric-row" role="group" aria-label="Map metric">
          {MAP_METRICS.map((mm) =>
          <button key={mm.value} className={"mm-pill" + (metric === mm.value ? " on" : "")} onClick={() => setMetric(mm.value)}>{mm.label}</button>
          )}
        </div>
        <p className="map-hint">{mHint}</p>
        <USMap metric={metric} range={range} fbcIds={fbcIds}
        onPickState={setStateFilter} onPickOwner={(o) => setPicked(deriveOwner(o, range))} />
      </Card>

      <div className="territory-single" data-comment-anchor="eafc67199b-div-277-7">
        <Explorer owners={derived} stateFilter={stateFilter} setStateFilter={setStateFilter} onPick={setPicked} rangeLabel={rShort} range={range} />
      </div>

      <OwnerDrawer owner={picked} onClose={() => setPicked(null)} rangeLabel={rShort} />
    </div>);

}

Object.assign(window, { TerritoryPage, OwnerDrawer, deriveOwner, rangeWindow });