/* global React, Icon, money, monthLabel, HBar, InteractiveChart, PageSkeleton,
   DemoChip, ConfirmDialog, useNetwork, usePayoutLedger, useRevenueHistory,
   buildLedger, setPayoutStatus, fetchPayments, recentMonths, fromCents,
   PAYOUT_STATUS, TierPill */
// Money — what the network billed, what Sandbox kept, and what each course
// is owed.

const dollars = (cents) => money(Math.round(fromCents(cents)));

// ─── Period picker ────────────────────────────────────────────────────
// Calendar months, because the payout cadence is still undecided and a
// month is the grain everything else reports on. The schema stores an
// explicit start and end date, so this is a UI default rather than a
// commitment — fortnightly or per-event needs no migration.
function PeriodPicker({ periods, value, onChange }) {
  return (
    <div className="no-scrollbar" style={{ display: 'flex', gap: 6, overflowX: 'auto', paddingBottom: 2 }}>
      {periods.map(p => {
        const on = p.key === value.key;
        return (
          <button key={p.key} onClick={() => onChange(p)}
            style={{
              padding: '7px 13px', borderRadius: 999, cursor: 'pointer', whiteSpace: 'nowrap',
              font: 'inherit', fontSize: 12.5, fontWeight: on ? 700 : 600,
              background: on ? 'var(--cream)' : 'transparent',
              color: on ? 'var(--forest)' : 'var(--ink-muted)',
              border: `1px solid ${on ? 'var(--cream)' : 'var(--line-strong)'}`,
            }}>
            {p.label}
          </button>
        );
      })}
    </div>
  );
}

// ─── StatusPill ───────────────────────────────────────────────────────
// Shape carries the state, not just colour: paid is filled, processing is
// a solid outline, pending is dashed, failed gets the loss tone plus a
// filled dot. Consistent with how match results and tiers already read.
function StatusPill({ status }) {
  const s = {
    paid:       { background: 'var(--cream)', color: 'var(--forest)', border: '1px solid var(--cream)' },
    processing: { background: 'transparent', color: 'var(--ink)', border: '1px solid var(--ink-faint)' },
    pending:    { background: 'transparent', color: 'var(--ink-muted)', border: '1px dashed var(--ink-faint)' },
    failed:     { background: 'rgba(155,58,46,0.18)', color: 'var(--loss-soft, #E7B8A7)', border: '1px solid rgba(155,58,46,0.5)' },
  }[status] || {};
  return (
    <span className="pill-mono" style={{ ...s, fontSize: 9.5, padding: '3px 9px' }}>
      {(PAYOUT_STATUS[status] || {}).label || status}
    </span>
  );
}

// ─── Payout row actions ───────────────────────────────────────────────
function RowActions({ row, onChange, disabled }) {
  const btn = { padding: '4px 10px', fontSize: 11.5 };
  if (row.status === 'paid') {
    return <button className="btn btn-ghost" style={btn} disabled={disabled}
      onClick={() => onChange(row, 'pending')}>Reopen</button>;
  }
  return (
    <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
      {row.status !== 'processing' && (
        <button className="btn btn-ghost" style={btn} disabled={disabled}
          onClick={() => onChange(row, 'processing')}>Mark sent</button>
      )}
      <button className="btn btn-forest" style={btn} disabled={disabled}
        onClick={() => onChange(row, 'paid')}>Mark paid</button>
    </div>
  );
}

// ─── PayoutLedger ─────────────────────────────────────────────────────
function PayoutLedger({ onOpenCourse }) {
  const [net] = useNetwork();
  const periods = React.useMemo(() => recentMonths(6), []);
  const [period, setPeriod] = React.useState(periods[0]);
  const courses = net ? net.courses : [];
  const [ledger, reload] = usePayoutLedger(courses, period);
  const [busy, setBusy] = React.useState('');
  const [err, setErr] = React.useState('');
  const [confirm, setConfirm] = React.useState(null);

  if (!net || !ledger) return <PageSkeleton/>;

  const byId = {};
  courses.forEach(c => { byId[c.id] = c; });
  const rows = ledger.rows.slice().sort((a, b) => (b.gross_cents || 0) - (a.gross_cents || 0));
  const t = ledger.totals || {};

  async function build() {
    setBusy('build'); setErr('');
    try {
      const { written } = await buildLedger(courses, period);
      if (!written) setErr(`No booking revenue in ${period.label}, so there is nothing to pay out.`);
      reload();
    } catch (e) { setErr(e.message); }
    setBusy('');
  }

  async function change(row, status) {
    if (ledger.demo) { setErr('Demo Mode is on — these rows are generated, so nothing is written.'); return; }
    if (status === 'paid') { setConfirm({ row, status }); return; }
    await apply(row, status);
  }

  async function apply(row, status) {
    setBusy(row.id); setErr('');
    try { await setPayoutStatus(row.id, status); reload(); }
    catch (e) { setErr(e.message); }
    setBusy(''); setConfirm(null);
  }

  return (
    <div style={{ maxWidth: 1240, margin: '0 auto' }}>
      {ledger.demo && <div style={{ marginBottom: 12 }}><DemoChip corner={false}/></div>}

      <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', marginBottom: 16 }}>
        <PeriodPicker periods={periods} value={period} onChange={setPeriod}/>
        <div style={{ flex: 1 }}/>
        <button className="btn btn-forest" onClick={build} disabled={!!busy || ledger.demo}
          title={ledger.demo ? 'Turn Demo Mode off to build a real ledger' : `Recompute ${period.label} from bookings`}>
          {busy === 'build' ? 'Building…' : rows.length ? 'Rebuild from bookings' : 'Build this period'}
        </button>
      </div>

      {/* Totals */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(168px, 1fr))', gap: 12 }}>
        {[
          { label: 'Booking Value', value: dollars(t.gross), sub: period.label },
          { label: 'Sandbox Take', value: dollars(t.take), sub: t.gross ? `${Math.round((t.take / t.gross) * 100)}% blended` : '—' },
          { label: 'Courses Are Owed', value: dollars(t.net), sub: `${rows.length} course${rows.length === 1 ? '' : 's'}` },
          { label: 'Still Outstanding', value: dollars(t.outstanding), sub: `${t.outstandingCount || 0} unpaid · ${t.paidCount || 0} settled` },
        ].map(k => (
          <div key={k.label} className="card" style={{ padding: '15px 16px' }}>
            <div className="eyebrow">{k.label}</div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 26, color: 'var(--paper)', marginTop: 3, fontVariantNumeric: 'tabular-nums' }}>{k.value}</div>
            <div style={{ fontSize: 11.5, color: 'var(--ink-muted)', marginTop: 2 }}>{k.sub}</div>
          </div>
        ))}
      </div>

      {err && (
        <div className="card" style={{ padding: '13px 16px', marginTop: 14, borderLeft: '3px solid var(--loss)' }}>
          <div style={{ fontSize: 13, color: 'var(--ink-soft)', lineHeight: 1.5 }}>{err}</div>
        </div>
      )}

      {/* The Stripe position, stated once and honestly. */}
      <div className="card" style={{ padding: '14px 18px', marginTop: 14, borderLeft: '3px solid var(--cream)' }}>
        <div style={{ fontSize: 13, color: 'var(--ink-soft)', lineHeight: 1.55 }}>
          <strong style={{ color: 'var(--paper)' }}>Payments aren&rsquo;t running through Stripe yet.</strong>{' '}
          These figures are booking value times each course&rsquo;s split, so a payout marked paid records
          that <em>you</em> sent it. When Stripe goes live the charges land in <code style={{ fontFamily: 'var(--font-mono)', fontSize: 12 }}>payments</code> with
          the split recorded per charge, transfers fill in the reference column, and this screen stops
          needing the rebuild button.
        </div>
      </div>

      {/* Ledger */}
      <div className="card" style={{ marginTop: 16, padding: 0, overflow: 'hidden' }}>
        <div style={{ overflowX: 'auto' }}>
          <table className="table" style={{ minWidth: 860 }}>
            <thead>
              <tr>
                <th>Course</th>
                <th style={{ textAlign: 'right' }}>Booking value</th>
                <th style={{ textAlign: 'right' }}>Sandbox take</th>
                <th style={{ textAlign: 'right' }}>Course net</th>
                <th>Status</th>
                <th>Transfer</th>
                <th style={{ textAlign: 'right' }}>&nbsp;</th>
              </tr>
            </thead>
            <tbody>
              {rows.map(r => {
                const c = byId[r.course_id];
                const num = { textAlign: 'right', fontFamily: 'var(--font-mono)', fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap' };
                return (
                  <tr key={r.id}>
                    <td>
                      <button onClick={() => c && onOpenCourse(c)} disabled={!c}
                        style={{ background: 'none', border: 'none', padding: 0, font: 'inherit', cursor: c ? 'pointer' : 'default', color: 'var(--paper)', fontWeight: 700, textAlign: 'left' }}>
                        {c ? (c.short_name || c.name) : 'Unknown course'}
                      </button>
                      {c && <div style={{ marginTop: 3 }}><TierPill tier={c.tier} founding={c.founding}/></div>}
                    </td>
                    <td style={num}>{dollars(r.gross_cents)}</td>
                    <td style={{ ...num, color: 'var(--ink-soft)' }}>{dollars(r.sandbox_take_cents)}</td>
                    <td style={{ ...num, color: 'var(--paper)', fontWeight: 700 }}>{dollars(r.course_net_cents)}</td>
                    <td>
                      <StatusPill status={r.status}/>
                      {r.paid_at && (
                        <div style={{ fontSize: 10.5, color: 'var(--ink-faint)', fontFamily: 'var(--font-mono)', marginTop: 3 }}>
                          {String(r.paid_at).slice(0, 10)}
                        </div>
                      )}
                    </td>
                    <td style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--ink-muted)' }}>
                      {r.stripe_transfer_id || '—'}
                    </td>
                    <td style={{ textAlign: 'right' }}>
                      <RowActions row={r} onChange={change} disabled={busy === r.id}/>
                    </td>
                  </tr>
                );
              })}
              {!rows.length && (
                <tr>
                  <td colSpan={7} style={{ padding: 30, textAlign: 'center', color: 'var(--ink-muted)' }}>
                    {ledger.missing
                      ? 'The payouts table is missing. Run sql/admin-overhaul.sql in Supabase.'
                      : `Nothing built for ${period.label} yet. Build it from bookings to see what each course is owed.`}
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </div>

      {confirm && (
        <ConfirmDialog
          open
          title={`Mark ${(byId[confirm.row.course_id] || {}).short_name || 'this course'} paid?`}
          body={`This records that ${dollars(confirm.row.course_net_cents)} for ${period.label} has been sent. It doesn't move any money — Stripe isn't wired up yet.`}
          confirmLabel="Mark paid"
          onConfirm={() => apply(confirm.row, confirm.status)}
          onCancel={() => setConfirm(null)}/>
      )}
    </div>
  );
}

// ─── RevenuePanel ─────────────────────────────────────────────────────
function RevenuePanel({ onOpenCourse }) {
  const [net] = useNetwork();
  const courses = net ? net.courses : [];
  const hist = useRevenueHistory(courses, 12);
  const [payments, setPayments] = React.useState(undefined);

  React.useEffect(() => {
    let live = true;
    const now = new Date();
    const from = new Date(now.getFullYear(), now.getMonth() - 11, 1).toISOString().slice(0, 10);
    fetchPayments(from, now.toISOString().slice(0, 10))
      .then(p => { if (live) setPayments(p); })
      .catch(() => { if (live) setPayments(null); });
    return () => { live = false; };
  }, []);

  if (!net || !hist) return <PageSkeleton/>;

  const totalGross = hist.gross.reduce((a, b) => a + b, 0);
  const totalTake = hist.take.reduce((a, b) => a + b, 0);
  const cats = hist.keys.map(k => monthLabel(k));
  const maxGross = Math.max(1, ...hist.byCourse.map(r => r.gross));
  const liveCharges = Array.isArray(payments) && payments.length > 0;

  // Tier is a business question — is the founding rate costing us, and how
  // much of the network sits in each band.
  const byTier = ['pilot', 'growth', 'embed'].map(tier => {
    const inTier = hist.byCourse.filter(r => r.course.tier === tier);
    return {
      tier,
      courses: inTier.length,
      gross: inTier.reduce((n, r) => n + r.gross, 0),
      take: inTier.reduce((n, r) => n + r.take, 0),
    };
  }).filter(r => r.courses > 0);

  return (
    <div style={{ maxWidth: 1240, margin: '0 auto' }}>
      {hist.demo && <div style={{ marginBottom: 12 }}><DemoChip corner={false}/></div>}

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(168px, 1fr))', gap: 12 }}>
        {[
          { label: 'Booking Value · 12mo', value: money(totalGross) },
          { label: 'Sandbox Take · 12mo', value: money(totalTake), sub: totalGross ? `${Math.round((totalTake / totalGross) * 100)}% blended` : '—' },
          { label: 'Left With Courses', value: money(totalGross - totalTake) },
          { label: 'Contributing Courses', value: hist.byCourse.filter(r => r.gross > 0).length, sub: `of ${courses.length}` },
        ].map(k => (
          <div key={k.label} className="card" style={{ padding: '15px 16px' }}>
            <div className="eyebrow">{k.label}</div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 26, color: 'var(--paper)', marginTop: 3, fontVariantNumeric: 'tabular-nums' }}>{k.value}</div>
            {k.sub && <div style={{ fontSize: 11.5, color: 'var(--ink-muted)', marginTop: 2 }}>{k.sub}</div>}
          </div>
        ))}
      </div>

      <div className="card" style={{ padding: '14px 18px', marginTop: 14, borderLeft: `3px solid ${liveCharges ? 'var(--moss-light, #3E8A57)' : 'var(--cream)'}` }}>
        <div style={{ fontSize: 13, color: 'var(--ink-soft)', lineHeight: 1.55 }}>
          {liveCharges
            ? <><strong style={{ color: 'var(--paper)' }}>Reading real charges.</strong> {payments.length} payment{payments.length === 1 ? '' : 's'} on file, with the split recorded per charge.</>
            : <><strong style={{ color: 'var(--paper)' }}>Computed from bookings.</strong> No Stripe charges on file yet, so revenue is booking value and the take is each course&rsquo;s contracted split applied to it — not what was actually collected.</>}
        </div>
      </div>

      <div className="card fade-in" style={{ padding: 22, marginTop: 16 }}>
        <div className="eyebrow">Booking Value vs Sandbox Take · Last 12 Months</div>
        <div style={{ marginTop: 14, color: 'var(--paper)' }}>
          <InteractiveChart type="bar" categories={cats} format={money}
            series={[
              { key: 'gross', label: 'Booking value', color: 'var(--paper)', values: hist.gross },
              { key: 'take', label: 'Sandbox take', color: 'var(--moss-light, #3E8A57)', values: hist.take },
            ]}/>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(340px, 1fr))', gap: 16, marginTop: 16 }}>
        <div className="card" style={{ padding: 22 }}>
          <div className="eyebrow">Contribution By Course · 12 Months</div>
          <div style={{ marginTop: 14 }}>
            {hist.byCourse.map(r => (
              <HBar key={r.course.id} label={r.course.short_name || r.course.name}
                value={r.gross} max={maxGross} color="var(--paper)" track="rgba(234,226,206,0.16)"
                rightLabel={money(Math.round(r.gross))}/>
            ))}
            {!hist.byCourse.length && (
              <div style={{ fontSize: 13, color: 'var(--ink-muted)' }}>No revenue on record yet.</div>
            )}
          </div>
        </div>

        <div className="card" style={{ padding: 22 }}>
          <div className="eyebrow">By Partnership Tier</div>
          <div style={{ overflowX: 'auto', marginTop: 12 }}>
            <table className="table" style={{ minWidth: 300 }}>
              <thead>
                <tr>
                  <th>Tier</th>
                  <th style={{ textAlign: 'right' }}>Courses</th>
                  <th style={{ textAlign: 'right' }}>Booking value</th>
                  <th style={{ textAlign: 'right' }}>Our take</th>
                </tr>
              </thead>
              <tbody>
                {byTier.map(r => (
                  <tr key={r.tier}>
                    <td><TierPill tier={r.tier}/></td>
                    <td style={{ textAlign: 'right', fontFamily: 'var(--font-mono)' }}>{r.courses}</td>
                    <td style={{ textAlign: 'right', fontFamily: 'var(--font-mono)' }}>{money(Math.round(r.gross))}</td>
                    <td style={{ textAlign: 'right', fontFamily: 'var(--font-mono)', color: 'var(--ink-soft)' }}>
                      {money(Math.round(r.take))}
                      <span style={{ color: 'var(--ink-faint)' }}> · {r.gross ? Math.round((r.take / r.gross) * 100) : 0}%</span>
                    </td>
                  </tr>
                ))}
                {!byTier.length && (
                  <tr><td colSpan={4} style={{ color: 'var(--ink-muted)', padding: 18 }}>No courses yet.</td></tr>
                )}
              </tbody>
            </table>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-muted)', marginTop: 10, lineHeight: 1.5 }}>
            Embed carries the lowest split, so a healthy network shows this table getting less
            profitable per dollar as courses mature — that trade is the deal.
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { PayoutLedger, RevenuePanel, StatusPill, PeriodPicker });
