// forecast.jsx — 13-week cash flow forecast
// Chart on top, table below, click a week to drill into its line items.

const { useState: fState, useEffect: fEffect, useMemo, useRef } = React;

function ForecastScreen({ db, setDb, push }) {
  const f = db.forecast;
  const weeks = f.weeks;
  const [activeWeek, setActiveWeek] = fState(null);
  const [editingBalance, setEditingBalance] = fState(false);
  const [balanceDraft, setBalanceDraft] = fState(f.startingBalance.toFixed(2));
  const [thresholdDraft, setThresholdDraft] = fState(f.lowCashThreshold);

  // Recompute on starting balance / threshold changes
  fEffect(() => { setBalanceDraft(f.startingBalance.toFixed(2)); }, [f.startingBalance]);

  // Phase 4 wiring: editing the starting balance writes TWO settings —
  // starting_balance and starting_balance_confirmed_date (= today). The client
  // recompute stays as-is (harmless on the degenerate Phase 4 forecast); on a
  // failed write we roll back to the prior balance + confirmed date.
  const commitBalance = () => {
    const v = parseFloat(balanceDraft);
    if (isNaN(v)) { setEditingBalance(false); return; }
    setEditingBalance(false);
    const prevBalance = f.startingBalance;
    const prevConfirmed = f.startingConfirmed;
    setDb(prev => recomputeForecast(prev, { startingBalance: v, startingConfirmed: prev.today }));
    Promise.all([
      ViewerAPI.putSetting("starting_balance", v),
      ViewerAPI.putSetting("starting_balance_confirmed_date", db.today),
    ])
      .then(() => push("Starting balance updated · confirmed today"))
      .catch(err => {
        setDb(prev => recomputeForecast(prev, {
          startingBalance: prevBalance, startingConfirmed: prevConfirmed,
        }));
        setBalanceDraft(prevBalance.toFixed(2));
        push("Couldn't save balance: " + err.message);
      });
  };

  // Phase 4 wiring: low-cash threshold write on blur.
  const commitThreshold = () => {
    const v = parseFloat(thresholdDraft);
    if (isNaN(v)) { setThresholdDraft(f.lowCashThreshold); return; }
    const prevThreshold = f.lowCashThreshold;
    setDb(prev => recomputeForecast(prev, { lowCashThreshold: v }));
    ViewerAPI.putSetting("low_cash_threshold", v)
      .then(() => push("Low-cash threshold updated"))
      .catch(err => {
        setDb(prev => recomputeForecast(prev, { lowCashThreshold: prevThreshold }));
        setThresholdDraft(prevThreshold);
        push("Couldn't save threshold: " + err.message);
      });
  };

  const minEnd = Math.min(...weeks.map(w => w.ending));
  const maxEnd = Math.max(...weeks.map(w => w.ending), f.startingBalance);
  const lowest = weeks.reduce((acc, w) => w.ending < acc.ending ? w : acc, weeks[0]);
  const totalIn = weeks.reduce((s, w) => s + w.inflow, 0);
  const totalOut = weeks.reduce((s, w) => s + w.outflow, 0);
  const finalBal = weeks[weeks.length - 1].ending;

  return (
    <div className="fcst-wrap">
      <div className="grid-4">
        <KPICard
          label="Starting balance (wk 1)"
          value={
            editingBalance
              ? <input className="input" autoFocus
                  value={balanceDraft}
                  onChange={e => setBalanceDraft(e.target.value)}
                  onKeyDown={e => { if (e.key === "Enter") commitBalance(); if (e.key === "Escape") setEditingBalance(false); }}
                  onBlur={commitBalance}
                  style={{ width: "100%", fontSize: 20, fontWeight: 600, fontFamily: "Geist Mono, ui-monospace, monospace", padding: "4px 6px", height: 32 }}
                />
              : <span onClick={() => setEditingBalance(true)} style={{ cursor: "text", borderBottom: "1px dashed var(--border-strong)" }}>
                  {fmt(f.startingBalance)}
                </span>
          }
          sub={
            <span>
              <Icon name="pencil" size={10} /> &nbsp;Owner-confirmed · last set {fmtDate(f.startingConfirmed)}
            </span>
          }
        />
        <KPICard
          label="Projected 13-week inflow"
          value={fmtShort(totalIn)}
          sub={`${weeks.filter(w => w.inflow > 0).length} weeks with deposits`}
        />
        <KPICard
          label="Projected 13-week outflow"
          value={"−" + fmtShort(totalOut)}
          sub={`Recurring + payroll + supplier est.`}
        />
        <KPICard
          label="Ending balance · wk 13"
          value={fmtShort(finalBal)}
          sub={
            <span style={{ color: finalBal < f.lowCashThreshold ? "var(--negative)" : "var(--ink-3)" }}>
              {finalBal < f.lowCashThreshold ? "Below low-cash threshold" : `Above ${fmtShort(f.lowCashThreshold)} threshold`}
            </span>
          }
          accent
        />
      </div>

      {/* Chart card */}
      <div className="card">
        <header className="card-hd">
          <div className="row" style={{ gap: 10 }}>
            <h3>13-week ending balance</h3>
            <Pill kind="muted">Updated today</Pill>
            {lowest.ending < f.lowCashThreshold && (
              <Pill kind="neg" icon="alert">
                Dips to {fmtShort(lowest.ending)} in wk {lowest.idx}
              </Pill>
            )}
          </div>
          <div className="row" style={{ gap: 10 }}>
            <span className="tiny muted">Low-cash threshold</span>
            <input
              className="input"
              style={{ width: 90, height: 28, fontSize: 12 }}
              value={thresholdDraft}
              onChange={e => setThresholdDraft(e.target.value.replace(/[^0-9.]/g, ""))}
              onBlur={commitThreshold}
            />
          </div>
        </header>
        <div className="card-bd" style={{ paddingTop: 8 }}>
          <ForecastChart
            weeks={weeks}
            threshold={f.lowCashThreshold}
            startBal={f.startingBalance}
            activeWeek={activeWeek}
            onPick={setActiveWeek}
            minEnd={minEnd}
            maxEnd={maxEnd}
          />
        </div>
      </div>

      {/* Forecast table */}
      <ForecastTable
        weeks={weeks}
        threshold={f.lowCashThreshold}
        startBal={f.startingBalance}
        activeWeek={activeWeek}
        onPick={setActiveWeek}
      />

      {/* Legend */}
      <div className="row" style={{ gap: 16, flexWrap: "wrap", fontSize: 12, color: "var(--ink-2)", padding: "0 4px" }}>
        <span><Pill kind="paired" icon="dot">Known</Pill> &nbsp;Recurring + confirmed receivables</span>
        <span><Pill kind="muted" icon="dot">Estimated</Pill> &nbsp;Payroll & supplier run-rates (italic, prefixed ~)</span>
        <span><Pill kind="warn" icon="dot">Low-cash week</Pill> &nbsp;below {fmt(f.lowCashThreshold, false)}</span>
        <span><Pill kind="neg" icon="dot">Critical</Pill> &nbsp;negative ending balance</span>
      </div>

      {activeWeek && (
        <Modal
          title={`Week ${activeWeek.idx}: ${fmtDateLong(activeWeek.start)} – ${fmtDateLong(activeWeek.end)}`}
          onClose={() => setActiveWeek(null)}
          wide
          footer={<button className="btn" onClick={() => setActiveWeek(null)}>Close</button>}
        >
          <WeekDrillIn week={activeWeek} threshold={f.lowCashThreshold} />
        </Modal>
      )}
    </div>
  );
}

// Recompute helper (recomputes ending balances and below/negative flags).
function recomputeForecast(prev, patch) {
  const f = { ...prev.forecast, ...patch };
  let bal = f.startingBalance;
  const newWeeks = f.weeks.map(w => {
    const starting = bal;
    const ending = starting + w.net;
    bal = ending;
    return {
      ...w,
      starting,
      ending,
      below: ending < f.lowCashThreshold,
      negative: ending < 0,
    };
  });
  return { ...prev, forecast: { ...f, weeks: newWeeks } };
}

// ────────────────────────────────────────────────────────────────────────────
// Chart (custom SVG)
// ────────────────────────────────────────────────────────────────────────────
function ForecastChart({ weeks, threshold, startBal, activeWeek, onPick, minEnd, maxEnd }) {
  const W = 920, H = 220;
  const padL = 50, padR = 14, padT = 14, padB = 36;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;

  // Y domain: from min(0, minEnd) - buffer to max + buffer
  const yLo = Math.min(0, minEnd) - 500;
  const yHi = Math.max(maxEnd, threshold + 1000) + 500;
  const yScale = v => padT + innerH - ((v - yLo) / (yHi - yLo)) * innerH;
  const barW = innerW / weeks.length * 0.72;
  const xCenter = i => padL + innerW * ((i + 0.5) / weeks.length);

  // Gridline values
  const gridVals = useMemo(() => {
    const step = Math.max(1000, Math.round((yHi - yLo) / 5 / 1000) * 1000);
    const arr = [];
    let v = Math.ceil(yLo / step) * step;
    for (; v <= yHi; v += step) arr.push(v);
    return arr;
  }, [yHi, yLo]);

  // Line path: starting balance -> each week's ending balance
  const points = [
    [padL, yScale(startBal)],
    ...weeks.map((w, i) => [xCenter(i), yScale(w.ending)]),
  ];
  const linePath = points.map((p, i) => (i ? "L" : "M") + p[0] + " " + p[1]).join(" ");
  // Area fill under line, down to baseline (threshold)
  const baselineY = yScale(0);
  const areaPath = linePath + ` L ${points[points.length-1][0]} ${baselineY} L ${padL} ${baselineY} Z`;

  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" role="img" aria-label="13-week ending balance">
      {/* Grid lines */}
      {gridVals.map(v => (
        <g key={v}>
          <line x1={padL} x2={W - padR} y1={yScale(v)} y2={yScale(v)} stroke="var(--border)" strokeDasharray={v === 0 ? "" : "2 3"} />
          <text x={padL - 8} y={yScale(v) + 3.5} fontSize="10" fill="var(--ink-3)" textAnchor="end" fontFamily="Geist Mono, monospace">
            {fmtShort(v)}
          </text>
        </g>
      ))}
      {/* Threshold line */}
      <line x1={padL} x2={W - padR} y1={yScale(threshold)} y2={yScale(threshold)} stroke="var(--warning)" strokeWidth="1.2" strokeDasharray="4 3" />
      <text x={W - padR - 4} y={yScale(threshold) - 4} fontSize="10" fill="var(--warning)" textAnchor="end" fontWeight="500">
        Low-cash threshold {fmtShort(threshold)}
      </text>

      {/* Bars: ending balance per week */}
      {weeks.map((w, i) => {
        const cx = xCenter(i);
        const y0 = yScale(0);
        const yEnd = yScale(w.ending);
        const y = Math.min(y0, yEnd);
        const h = Math.abs(y0 - yEnd);
        let color = "color-mix(in srgb, var(--accent) 18%, transparent)";
        let stroke = "color-mix(in srgb, var(--accent) 60%, transparent)";
        if (w.negative)      { color = "color-mix(in srgb, var(--negative) 30%, transparent)"; stroke = "var(--negative)"; }
        else if (w.below)    { color = "color-mix(in srgb, var(--warning) 30%, transparent)"; stroke = "var(--warning)"; }
        if (activeWeek && activeWeek.idx === w.idx) {
          color = "var(--accent-soft)";
          stroke = "var(--accent)";
        }
        return (
          <g key={w.idx} onClick={() => onPick(w)} style={{ cursor: "default" }}>
            <rect x={cx - barW/2} y={y} width={barW} height={Math.max(h, 1.5)} fill={color} stroke={stroke} strokeWidth="1" rx="2" />
            <rect x={cx - barW/2 - 4} y={padT} width={barW + 8} height={innerH} fill="transparent" />
          </g>
        );
      })}

      {/* Line + dots */}
      <path d={linePath} fill="none" stroke="var(--accent)" strokeWidth="1.6" />
      {points.slice(1).map((p, i) => {
        const w = weeks[i];
        let dotFill = "var(--accent)";
        if (w.negative)   dotFill = "var(--negative)";
        else if (w.below) dotFill = "var(--warning)";
        return (
          <circle key={i} cx={p[0]} cy={p[1]} r={activeWeek && activeWeek.idx === w.idx ? 4.5 : 3} fill={dotFill} stroke="var(--surface)" strokeWidth="1.5" />
        );
      })}

      {/* X axis labels */}
      {weeks.map((w, i) => (
        <g key={"x"+w.idx}>
          <text x={xCenter(i)} y={H - padB + 16} fontSize="10" fill="var(--ink-3)" textAnchor="middle" fontFamily="Geist Mono, monospace">
            W{w.idx}
          </text>
          <text x={xCenter(i)} y={H - padB + 28} fontSize="9" fill="var(--ink-4)" textAnchor="middle">
            {w.label}
          </text>
        </g>
      ))}
    </svg>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Table
// ────────────────────────────────────────────────────────────────────────────
function ForecastTable({ weeks, threshold, startBal, activeWeek, onPick }) {
  const tdClass = (w) => "col-week" +
    (w.negative ? " crit" : w.below ? " warn" : "") +
    (activeWeek && activeWeek.idx === w.idx ? " active" : "");

  return (
    <div className="fcst-table-wrap">
      <table className="fcst">
        <thead>
          <tr>
            <th>Week of</th>
            {weeks.map(w => (
              <th key={w.idx}
                  className={tdClass(w)}
                  onClick={() => onPick(w)}
                  style={{ cursor: "default" }}>
                <div style={{ fontSize: 9, opacity: .8 }}>W{w.idx}</div>
                <div>{w.label}</div>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          <tr className="section-hd"><td>Cash position</td>{weeks.map(w => <td key={w.idx} className={tdClass(w)}></td>)}</tr>
          <tr>
            <td>Starting balance</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w) + " conf-known"}>{fmt(w.starting, false)}</td>
            ))}
          </tr>

          <tr className="section-hd"><td>Inflows · known</td>{weeks.map(w => <td key={w.idx} className={tdClass(w)}></td>)}</tr>
          <tr>
            <td>Jobber receivables</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w) + " conf-known"}>
                {w.inflowJobber ? "+" + fmt(w.inflowJobber, false).replace("$","$") : ""}
              </td>
            ))}
          </tr>
          <tr>
            <td>Wix store payouts</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w) + " conf-known"}>
                {w.inflowWix ? "+" + fmt(w.inflowWix, false) : ""}
              </td>
            ))}
          </tr>

          <tr className="section-hd"><td>Outflows · known</td>{weeks.map(w => <td key={w.idx} className={tdClass(w)}></td>)}</tr>
          <tr>
            <td>Recurring (rent, bills, loan)</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w) + " conf-known"}>
                {w.outRecurring ? "−" + fmt(w.outRecurring, false) : ""}
              </td>
            ))}
          </tr>

          <tr className="section-hd"><td>Outflows · estimated</td>{weeks.map(w => <td key={w.idx} className={tdClass(w)}></td>)}</tr>
          <tr>
            <td>Payroll (from helper hours)</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w) + " conf-est"}>
                {w.outPayrollEst ? "−" + fmt(w.outPayrollEst, false) : ""}
              </td>
            ))}
          </tr>
          <tr>
            <td>Supplier run-rate (8-wk avg)</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w) + " conf-est"}>
                {w.outSupplierEst ? "−" + fmt(w.outSupplierEst, false) : ""}
              </td>
            ))}
          </tr>

          <tr className="total">
            <td>Net change</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w)} style={{ color: w.net < 0 ? "var(--negative)" : "var(--positive)" }}>
                {(w.net >= 0 ? "+" : "−") + fmt(Math.abs(w.net), false)}
              </td>
            ))}
          </tr>
          <tr className="total">
            <td>Ending balance</td>
            {weeks.map(w => (
              <td key={w.idx} className={tdClass(w)}>
                <strong>{fmt(w.ending, false)}</strong>
              </td>
            ))}
          </tr>
        </tbody>
      </table>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Week drill-in
// ────────────────────────────────────────────────────────────────────────────
function WeekDrillIn({ week, threshold }) {
  return (
    <div className="stack" style={{ gap: 18 }}>
      <div className="grid-3">
        <KPICard label="Starting" value={fmt(week.starting, false)} sub="From prior week" />
        <KPICard label="Net change" value={(week.net >= 0 ? "+" : "−") + fmt(Math.abs(week.net), false)}
          sub={week.net < 0 ? "Cash burning" : "Cash building"} />
        <KPICard label="Ending"
          value={fmt(week.ending, false)}
          sub={week.negative ? "Negative — overdraft risk" : week.below ? `Below ${fmt(threshold, false)} threshold` : "Above threshold"}
          accent
        />
      </div>

      <div className="grid-2">
        <Card title="Inflows" meta={`+${fmt(week.inflow, false)}`}>
          {week.receivablesHits.length === 0
            ? <div className="muted small">No expected receivables this week.</div>
            : (
              <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 8 }}>
                {week.receivablesHits.map(r => (
                  <li key={r.id} className="row between" style={{ borderBottom: "1px solid var(--border)", paddingBottom: 8 }}>
                    <div>
                      <div style={{ fontWeight: 500 }}>{r.customer}</div>
                      <div className="tiny muted">{r.id} · {r.source.toUpperCase()} · invoiced {fmtDate(r.invoiceDate)}</div>
                    </div>
                    <div className="num" style={{ color: "var(--positive)" }}>+{fmt(r.amount, false)}</div>
                  </li>
                ))}
              </ul>
            )
          }
        </Card>

        <Card title="Outflows" meta={`−${fmt(week.outflow, false)}`}>
          <div className="stack" style={{ gap: 14 }}>
            <div>
              <div className="label-up" style={{ marginBottom: 6 }}>Known (recurring)</div>
              {week.recurringHits.length === 0
                ? <div className="muted small">None due this week.</div>
                : (
                  <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 4 }}>
                    {week.recurringHits.map((it, i) => (
                      <li key={i} className="row between small">
                        <span>{it.name} <span className="tiny muted">· due {fmtDate(it.due)}</span></span>
                        <span className="num" style={{ color: "var(--negative)" }}>−{fmt(it.amount, false)}</span>
                      </li>
                    ))}
                  </ul>
                )
              }
            </div>
            <div>
              <div className="label-up" style={{ marginBottom: 6 }}>Estimated</div>
              <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 4 }}>
                <li className="row between small">
                  <span><em>Payroll (helper hours est.)</em></span>
                  <span className="num conf-est">−{fmt(week.outPayrollEst, false)}</span>
                </li>
                <li className="row between small">
                  <span><em>Supplier spend (run-rate)</em></span>
                  <span className="num conf-est">−{fmt(week.outSupplierEst, false)}</span>
                </li>
              </ul>
            </div>
          </div>
        </Card>
      </div>

      {(week.below || week.negative) && (
        <div className="row" style={{
          background: week.negative ? "var(--negative-soft)" : "var(--warning-soft)",
          color: week.negative ? "var(--negative)" : "var(--warning)",
          padding: "10px 14px", borderRadius: 8, gap: 10,
          border: "1px solid",
          borderColor: week.negative ? "color-mix(in srgb, var(--negative) 30%, transparent)" : "color-mix(in srgb, var(--warning) 30%, transparent)",
        }}>
          <Icon name="alert" />
          <div style={{ fontSize: 13 }}>
            <strong>{week.negative ? "Cash projected negative this week." : "Projected ending below low-cash threshold."}</strong>
            &nbsp; Consider deferring a non-essential outflow or accelerating Jobber receipt collection.
          </div>
        </div>
      )}
    </div>
  );
}

window.ForecastScreen = ForecastScreen;
