/* global React, Mascot, useMoneyData, floorFromRevenue, useUtilizationWindows, useDemandCurve, suggestedPrice, useBusinessMetrics,
   DemoChip, money, dayLabel, timeLabel, SplitCell, SplitStrip, PageSkeleton,
   Sparkline, HBar, PairedBars, InteractiveChart, useRackRate, TWILIGHT_SHARE */
// Business — a single B2B SaaS-style KPI dashboard replacing the old
// Money / Insights / People page split. Everything here is the course's
// OWN numbers: revenue is gross booking value or the course's net take-
// home, never Sandbox's rev-share cut or take % — the manager should
// never have to wonder "how much is Sandbox making off me."
//
// Layout: date-range control (presets + custom + vs-prior-period compare)
// → KPI tile row → four trend charts → a secondary strip of
// pricing/demand, sell-through, the month-matched floor guarantee and a
// receipts ledger (kept, since a course still needs to reconcile its own
// bookings — just without a Rev-Share line item).

const isoDate = (d) => { const p = n => String(n).padStart(2, '0'); return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`; };
const addDays = (dayStr, n) => { const d = new Date(`${dayStr}T00:00:00Z`); d.setUTCDate(d.getUTCDate() + n); return isoDate(d); };
const toInstant = (dayStr) => `${dayStr}T00:00:00.000Z`;
const dayShort = (dayStr) => new Date(`${dayStr}T00:00:00Z`).toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' });

const RANGE_PRESETS = [
  { key: '7d', label: '7D', days: 7 },
  { key: '30d', label: '30D', days: 30 },
  { key: '90d', label: '90D', days: 90 },
  { key: 'ytd', label: 'YTD', days: null },
];
function presetRange(key) {
  const today = isoDate(new Date());
  const toStr = addDays(today, 1); // exclusive upper bound
  if (key === 'ytd') return { fromStr: `${today.slice(0, 4)}-01-01`, toStr };
  const preset = RANGE_PRESETS.find(p => p.key === key) || RANGE_PRESETS[1];
  return { fromStr: addDays(today, -(preset.days - 1)), toStr };
}
function prevRange({ fromStr, toStr }) {
  const lenDays = Math.round((new Date(toStr) - new Date(fromStr)) / 864e5);
  return { fromStr: addDays(fromStr, -lenDays), toStr: fromStr };
}

function aggregateDays(days) {
  const sum = (k) => days.reduce((s, d) => s + (d[k] || 0), 0);
  const seatsAvail = sum('seatsAvail'), seatsBooked = sum('seatsBooked'), players = sum('players'), noShows = sum('noShows');
  return {
    revenue: sum('revenue'), seatsAvail, seatsBooked, rounds: sum('rounds'), players,
    newPlayers: sum('newPlayers'), returningPlayers: sum('returningPlayers'), noShows,
    utilization: seatsAvail ? seatsBooked / seatsAvail : null,
    returningShare: players ? sum('returningPlayers') / players : null,
    noShowRate: (seatsBooked + noShows) ? noShows / (seatsBooked + noShows) : null,
  };
}
// mode 'pct' = relative % change; 'pts' = absolute percentage-point change
// (for values that are already rates, 0..1). invert: lower is better.
function deltaInfo(cur, prev, { mode = 'pct', invert = false } = {}) {
  if (cur == null || prev == null) return { str: null, good: null };
  let raw;
  if (mode === 'pts') raw = Math.round((cur - prev) * 100);
  else {
    if (prev === 0) return cur === 0 ? { str: '±0%', good: null } : { str: null, good: null };
    raw = Math.round(((cur - prev) / prev) * 100);
  }
  const good = invert ? raw <= 0 : raw >= 0;
  return { str: `${raw > 0 ? '+' : ''}${raw}${mode === 'pts' ? ' pts' : '%'}`, good };
}

function KpiTile({ label, value, delta, points, sparkColor }) {
  return (
    <div className="card" style={{ padding: '16px 18px' }}>
      <div className="eyebrow">{label}</div>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 10, marginTop: 8 }}>
        <div className="metric metric-md">{value}</div>
        {points && points.length > 1 && (
          <div style={{ width: 60, height: 24, flexShrink: 0 }}>
            <Sparkline points={points} width={60} height={24} strokeWidth={1.6} stroke={sparkColor || 'var(--cream)'}/>
          </div>
        )}
      </div>
      {delta.str != null && (
        <div style={{ marginTop: 6, fontFamily: 'var(--font-mono)', fontSize: 11, fontWeight: 700, color: delta.good == null ? 'var(--ink-faint)' : (delta.good ? 'var(--cream)' : 'var(--loss-soft)') }}>
          {delta.str} <span style={{ opacity: 0.55, fontWeight: 600 }}>vs prior period</span>
        </div>
      )}
    </div>
  );
}

function DateRangeBar({ presetKey, onPreset, fromStr, toStr, onCustom, compare, onCompare }) {
  const todayStr = isoDate(new Date());
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', marginTop: 14 }}>
      <div style={{ display: 'flex', border: '1px solid var(--line-strong)', borderRadius: 999, overflow: 'hidden' }}>
        {RANGE_PRESETS.map(p => (
          <button key={p.key} onClick={() => onPreset(p.key)} style={{
            padding: '6px 14px', fontSize: 11.5, fontWeight: 700, fontFamily: 'var(--font-mono)', border: 'none', cursor: 'pointer',
            background: presetKey === p.key ? 'var(--cream)' : 'transparent', color: presetKey === p.key ? 'var(--forest)' : 'var(--paper)',
          }}>{p.label}</button>
        ))}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <input type="date" value={fromStr} max={addDays(toStr, -1)}
          onChange={e => onCustom(e.target.value, addDays(toStr, -1) < e.target.value ? addDays(e.target.value, 1) : toStr)}
          style={{ width: 138, padding: '6px 9px', borderRadius: 'var(--r-xs)', border: '1px solid var(--line-strong)', background: 'var(--surface)', fontSize: 12, color: 'var(--ink)', colorScheme: 'dark' }}/>
        <span style={{ opacity: 0.4, fontSize: 12 }}>→</span>
        <input type="date" value={addDays(toStr, -1)} min={fromStr} max={todayStr}
          onChange={e => onCustom(fromStr, addDays(e.target.value, 1))}
          style={{ width: 138, padding: '6px 9px', borderRadius: 'var(--r-xs)', border: '1px solid var(--line-strong)', background: 'var(--surface)', fontSize: 12, color: 'var(--ink)', colorScheme: 'dark' }}/>
      </div>
      <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, fontWeight: 600, color: 'var(--paper)', cursor: 'pointer', marginLeft: 'auto' }}>
        <input type="checkbox" checked={compare} onChange={e => onCompare(e.target.checked)}/>
        Compare to prior period
      </label>
    </div>
  );
}

function BusinessPanel({ course }) {
  const [presetKey, setPresetKey] = React.useState('30d');
  const [customRange, setCustomRange] = React.useState(null);
  const [compare, setCompare] = React.useState(true);

  const range = customRange || presetRange(presetKey);
  const prev = prevRange(range);

  const biz = useBusinessMetrics(course.id, toInstant(range.fromStr), toInstant(range.toStr));
  const bizPrev = useBusinessMetrics(course.id, toInstant(prev.fromStr), toInstant(prev.toStr));
  const moneyData = useMoneyData(course.id, course);
  const util = useUtilizationWindows(course.id);
  const demand = useDemandCurve(course.id);
  const [rack] = useRackRate(course);

  if (!biz || !bizPrev || !moneyData || !util || !demand) {
    return <div style={{ maxWidth: 1140, margin: '0 auto' }}><PageSkeleton/></div>;
  }

  const demo = !!(biz.demo || moneyData.demo || util.demo);
  const agg = aggregateDays(biz.days);
  const aggPrev = aggregateDays(bizPrev.days);
  const showDelta = compare;
  const D = (cur, prev2, opts) => (showDelta ? deltaInfo(cur, prev2, opts) : { str: null, good: null });

  const tw = util.windows.find(w => w.key === 'twilight') || { rate: null, seats: 0, booked: 0 };
  // Same suggestion the Tee Times price slider shows — two different numbers
  // under one word ("suggested") would be worse than showing none.
  const price = suggestedPrice(course, util, demand, rack);
  const floor = floorFromRevenue(moneyData.monthlyNet, moneyData.monthsOfHistory);
  const founding = moneyData.takePct <= 12;

  const cats = biz.days.map(d => dayShort(d.dayStr));

  return (
    <div style={{ maxWidth: 1140, margin: '0 auto', position: 'relative' }}>
      {demo && <DemoChip/>}
      <div className="eyebrow">Business · {range.fromStr} → {addDays(range.toStr, -1)}</div>
      <div className="display" style={{ fontSize: 26, color: 'var(--paper)', marginTop: 5 }}>Your Business, At a Glance</div>
      <DateRangeBar presetKey={presetKey} onPreset={k => { setPresetKey(k); setCustomRange(null); }}
        fromStr={range.fromStr} toStr={range.toStr}
        onCustom={(f, t) => { setCustomRange({ fromStr: f, toStr: t }); setPresetKey(null); }}
        compare={compare} onCompare={setCompare}/>

      {/* KPI tiles — the "at a glance" row every SaaS dashboard opens with */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(148px, 1fr))', gap: 12, marginTop: 22 }}>
        <KpiTile label="Revenue" value={money(agg.revenue)} points={biz.days.map(d => d.revenue)}
          delta={D(agg.revenue, aggPrev.revenue)}/>
        <KpiTile label="Rounds Played" value={agg.rounds.toLocaleString()} points={biz.days.map(d => d.rounds)}
          delta={D(agg.rounds, aggPrev.rounds)}/>
        <KpiTile label="Players" value={agg.players.toLocaleString()} points={biz.days.map(d => d.players)}
          delta={D(agg.players, aggPrev.players)}/>
        <KpiTile label="Utilization" value={agg.utilization != null ? `${Math.round(agg.utilization * 100)}%` : '—'}
          points={biz.days.map(d => (d.seatsAvail ? d.seatsBooked / d.seatsAvail : 0))}
          delta={D(agg.utilization, aggPrev.utilization, { mode: 'pts' })}/>
        <KpiTile label="Repeat Players" value={agg.returningShare != null ? `${Math.round(agg.returningShare * 100)}%` : '—'}
          points={biz.days.map(d => (d.players ? d.returningPlayers / d.players : 0))}
          delta={D(agg.returningShare, aggPrev.returningShare, { mode: 'pts' })}/>
        <KpiTile label="No-Show Rate" value={agg.noShowRate != null ? `${Math.round(agg.noShowRate * 100)}%` : '—'}
          points={biz.days.map(d => ((d.seatsBooked + d.noShows) ? d.noShows / (d.seatsBooked + d.noShows) : 0))}
          delta={D(agg.noShowRate, aggPrev.noShowRate, { mode: 'pts', invert: true })}/>
        <KpiTile label="Avg. Round Time" value={biz.avgRoundMin != null ? `${biz.avgRoundMin}m` : '—'}
          delta={D(biz.avgRoundMin, bizPrev.avgRoundMin, { invert: true })}/>
      </div>

      {/* Trend charts */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(420px, 1fr))', gap: 16, marginTop: 24 }}>
        <div className="card fade-in" style={{ padding: 20 }}>
          <div className="eyebrow">Revenue Per Day</div>
          <div style={{ marginTop: 12, color: 'var(--paper)' }}>
            <InteractiveChart type="line" legend={false} format={money} categories={cats}
              series={[{ key: 'revenue', label: 'Revenue', color: 'var(--paper)', values: biz.days.map(d => d.revenue) }]}/>
          </div>
        </div>
        <div className="card fade-in" style={{ padding: 20 }}>
          <div className="eyebrow">Utilization Rate Per Day</div>
          <div style={{ fontSize: 11.5, opacity: 0.55, marginTop: 3 }}>% of available slots booked</div>
          <div style={{ marginTop: 9, color: 'var(--paper)' }}>
            <InteractiveChart type="line" legend={false} format={(v) => `${v}%`} categories={cats}
              series={[{ key: 'util', label: 'Utilization', color: 'var(--paper)', values: biz.days.map(d => (d.seatsAvail ? Math.round((d.seatsBooked / d.seatsAvail) * 100) : 0)) }]}/>
          </div>
        </div>
        <div className="card fade-in" style={{ padding: 20 }}>
          <div className="eyebrow">Rounds & Players Per Day</div>
          <div style={{ marginTop: 12, color: 'var(--paper)' }}>
            <InteractiveChart type="line" categories={cats}
              series={[
                { key: 'rounds', label: 'Rounds', color: 'var(--paper)', values: biz.days.map(d => d.rounds) },
                { key: 'players', label: 'Players', color: 'var(--moss-light, #3E8A57)', values: biz.days.map(d => d.players) },
              ]}/>
          </div>
        </div>
        <div className="card fade-in" style={{ padding: 20 }}>
          <div className="eyebrow">New vs. Returning Players</div>
          <div style={{ marginTop: 12, color: 'var(--paper)' }}>
            <InteractiveChart type="bar" format={(v) => `${v} players`} categories={cats}
              series={[
                { key: 'new', label: 'New', color: 'var(--moss-light, #3E8A57)', values: biz.days.map(d => d.newPlayers) },
                { key: 'returning', label: 'Returning', color: 'var(--paper)', values: biz.days.map(d => d.returningPlayers) },
              ]}/>
          </div>
        </div>
      </div>

      {/* Pricing & demand — Sandbox's suggestion, never a rev-share number */}
      <SplitStrip style={{ marginTop: 24 }}>
        <SplitCell label="Sandbox Suggests" value={money(price.suggested)} emphasis
          sub={price.basis === 'rack'
            ? `Per golfer, twilight — about ${Math.round(TWILIGHT_SHARE * 100)}% of your $${price.rack} tee time rate. You always set the final price.`
            : 'Per golfer, twilight. You always set the final price — this is only a suggestion.'}/>
        <SplitCell label="Waitlist Demand Ahead" value={demand.total} border
          sub="Golfers with open windows on your course"/>
        <SplitCell label="Twilight Seats · 90d" value={`${tw.booked}/${tw.seats}`} border
          sub="Seats filled in your designated Sandbox windows"/>
      </SplitStrip>

      {/* Sell-through by window */}
      <div className="card fade-in" style={{ padding: 22, marginTop: 20 }}>
        <div className="eyebrow">Sell-Through By Window · Last 90 Days</div>
        <div style={{ marginTop: 12 }}>
          {util.windows.map(w => (
            <HBar key={w.key} label={w.label} value={w.rate || 0} max={1} highlight={w.key === 'twilight'}
              color="var(--paper)" track="rgba(234,226,206,0.16)"
              rightLabel={w.rate != null ? `${Math.round(w.rate * 100)}%` : '—'}/>
          ))}
        </div>
        <div style={{ fontSize: 11, opacity: 0.5, marginTop: 10 }}>
          Twilight is your designated Sandbox window — the others show where a second window could work.
        </div>
      </div>

      {/* Month-matched floor — framed on the course's own revenue, never
          what Sandbox keeps. */}
      <div className="card fade-in" style={{ padding: 22, marginTop: 20 }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
          <div style={{ minWidth: 0, flex: '1 1 260px' }}>
            <div className="eyebrow">The Month-Matched Floor</div>
            <div className="display" style={{ fontSize: 22, color: 'var(--paper)', marginTop: 6 }}>
              "Your revenue via Sandbox doesn't go backwards — year over year, month by month."
            </div>
          </div>
          {floor.mode === 'active' && (
            <span className="pill-mono" style={{
              flexShrink: 0, marginTop: 2, whiteSpace: 'normal', textAlign: 'center', lineHeight: 1.4,
              ...(floor.belowCount === 0
                ? { background: 'var(--cream)', color: 'var(--forest)' }
                : { background: 'rgba(231,184,167,0.16)', color: 'var(--loss-soft)', border: '1px solid rgba(231,184,167,0.4)' }),
            }}>
              {floor.belowCount === 0 ? '✓ Above The Floor All Year' : `${floor.belowCount} Month${floor.belowCount > 1 ? 's' : ''} Below Floor`}
            </span>
          )}
          {founding && (
            <span className="pill-mono" style={{ background: 'var(--cream)', color: 'var(--forest)' }}>★ Founding Rate · Locked While You Renew</span>
          )}
        </div>

        {floor.mode === 'active' ? (
          <>
            <div style={{ marginTop: 18 }}>
              <PairedBars data={floor.months} aLabel="Same Month Last Year" bLabel="This Year" format={money}/>
            </div>
            <div style={{ fontSize: 12, opacity: 0.6, marginTop: 12, lineHeight: 1.5 }}>
              Sandbox guarantees your monthly revenue never falls below what you made in the same month a year earlier.
              Reconciled quarterly — a payment is due only for months that genuinely went backwards
              {floor.shortfallQuarter > 0
                ? <> · <b style={{ color: 'var(--loss-soft)' }}>current quarter shortfall {money(floor.shortfallQuarter)}</b></>
                : ' · nothing owed this quarter.'}
            </div>
          </>
        ) : (
          <>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(130px, 1fr))', gap: 8, marginTop: 16 }}>
              {floor.months.map(m => (
                <div key={m.label} style={{
                  border: m.b > 0 ? '1px solid rgba(234,226,206,0.35)' : '1px dashed var(--line-strong)',
                  borderRadius: 'var(--r-sm)', padding: '10px 12px', opacity: m.b > 0 ? 1 : 0.5,
                }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                    <span className="eyebrow">{m.label}</span>
                    {m.b > 0 && <span style={{ fontSize: 10, opacity: 0.6, fontFamily: 'var(--font-mono)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Locked</span>}
                  </div>
                  <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 700, color: 'var(--paper)', marginTop: 4 }}>
                    {m.b > 0 ? money(m.b) : '—'}
                  </div>
                </div>
              ))}
            </div>
            <div style={{ fontSize: 12, opacity: 0.6, marginTop: 12, lineHeight: 1.5 }}>
              <b>Year 1 — recording your baseline.</b> Each completed month's revenue locks in as that month's floor next year.
              From Year 2, Sandbox guarantees your revenue never falls below the same month a year earlier — or it pays the difference.
            </div>
          </>
        )}
      </div>

      {/* Receipts — proof every booking is paid in full, no rev-share column */}
      <div className="card fade-in" style={{ padding: 22, marginTop: 20 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
          <div className="eyebrow">Receipts · Latest Bookings</div>
          <span className="pill-mono" style={{ background: 'var(--cream)', color: 'var(--forest)' }}>Always Paid In Full</span>
        </div>
        {moneyData.receipts.length === 0 ? (
          <div style={{ textAlign: 'center', padding: '22px 0' }}>
            <Mascot size={90} style={{ margin: '0 auto 8px' }}/>
            <div style={{ fontSize: 13, opacity: 0.6 }}>Receipts appear here as Sandbox bookings land.</div>
          </div>
        ) : (
          <div style={{ marginTop: 12 }}>
            <div style={{ display: 'flex', padding: '6px 0', fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '0.1em', textTransform: 'uppercase', opacity: 0.5, borderBottom: '1px solid var(--line)' }}>
              <span style={{ flex: 2 }}>Golfer</span>
              <span style={{ flex: 2 }}>Tee Time</span>
              <span style={{ flex: 1, textAlign: 'right' }}>Booked</span>
              <span style={{ flex: 1, textAlign: 'right' }}>Your Net</span>
            </div>
            {moneyData.receipts.map(r => (
              <div key={r.id} style={{ display: 'flex', alignItems: 'baseline', padding: '9px 0', borderBottom: '1px solid var(--line-soft)', fontSize: 13 }}>
                <span style={{ flex: 2, fontWeight: 700, color: 'var(--paper)' }}>{r.golfer}{r.twilight ? ' *' : ''}</span>
                <span style={{ flex: 2, fontFamily: 'var(--font-mono)', fontSize: 11, opacity: 0.65 }}>{dayLabel(r.whenISO)} · {timeLabel(r.whenISO)}</span>
                <span style={{ flex: 1, textAlign: 'right', fontFamily: 'var(--font-mono)', fontSize: 12 }}>{money(r.gross)}</span>
                <span style={{ flex: 1, textAlign: 'right', fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 700, color: 'var(--paper)' }}>{money(r.net)}</span>
              </div>
            ))}
          </div>
        )}
        <div style={{ fontSize: 11, opacity: 0.5, marginTop: 12 }}>
          Reserve-only for now — figures are booked value, not captured payment. * = twilight window.
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { BusinessPanel });
