/* eslint-disable */
// ===========================================================================
// Omnivore UI — Recap view (trends, day review, meal list, editor)
//
// Part of the in-browser (no-build) React app. These files are loaded in order
// as separate <script type="text/babel"> tags (see index.html); Babel compiles
// each as a classic global script, so top-level names declared in earlier files
// are visible here. Depends on ui/base.jsx, ui/components.jsx.
// ===========================================================================

// ── HISTORY ──────────────────────────────────────────────────────────────────
function MealRow({ rec, onChange, toast }) {
  const [open, setOpen] = useState(false);
  const [editing, setEditing] = useState(false);
  const m = rec.meal || {};
  const time = (rec.eaten_at || "").slice(11, 16);

  function del() { Store.deleteMeal(rec.id); toast("Deleted"); onChange(); }

  return (
    <div style={{ borderBottom: `1px solid ${C.line}` }}>
      <div onClick={() => setOpen(!open)}
        style={{ padding: "14px 4px", display: "flex", justifyContent: "space-between", alignItems: "center", cursor: "pointer", gap: 12 }}>
        <span style={{ fontSize: 15, color: C.text }}>{m.description || "(meal)"}</span>
        <span style={{ fontSize: 13, color: C.faint, whiteSpace: "nowrap" }}>{time}</span>
      </div>
      {open && (
        <div style={{ padding: "2px 4px 16px" }}>
          {editing ? (
            <MealEditor rec={rec} toast={toast} onCancel={() => setEditing(false)}
              onSaved={() => { setEditing(false); onChange(); }} />
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <MealDetail meal={m} />
              <div style={{ display: "flex", gap: 8 }}>
                <button onClick={() => setEditing(true)} style={pillBtn(false)}>Edit</button>
                <button onClick={del} style={pillBtn(false)}>Delete</button>
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ── RECAP ────────────────────────────────────────────────────────────────────
// A per-day look-back that defaults to today: the day's score, multi-day + per-
// metric trends, an AI "what went well / watch outs" reflection, and the meal
// list. Deliberately retrospective — the Today tab stays the live, act-now view
// (its worst-first metric rows live only there); Recap adds the trends and the
// narrative that Today doesn't show, so the two complement rather than overlap.

const WEEK = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
function shiftDay(day, n) {
  const [y, m, d] = day.split("-").map(Number);
  return localDay(new Date(y, m - 1, d + n));
}
function prettyDay(day) {
  const [y, m, d] = day.split("-").map(Number);
  const dt = new Date(y, m - 1, d);
  return `${WEEK[dt.getDay()]}, ${MON[m - 1]} ${d}`;
}
function whenFor(day, today) {
  return day === today ? new Date() : new Date(day + "T23:59:00");
}

// small caps section label (matches GoalsView's headings)
const SectionLabel = ({ children, right }) => (
  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", margin: "0 4px 8px" }}>
    <span style={{ fontSize: 12, color: C.faint, textTransform: "uppercase", letterSpacing: ".06em" }}>{children}</span>
    {right}
  </div>
);

// thin line sparkline for a metric's recent daily values
function Sparkline({ values, width = 78, height = 24, color = C.dim }) {
  const vals = values.map((v) => (v == null || Number.isNaN(v) ? 0 : v));
  const max = Math.max(1, ...vals);
  const n = vals.length;
  const X = (i) => (n <= 1 ? width / 2 : (i / (n - 1)) * (width - 3) + 1.5);
  const Y = (v) => height - 2 - (v / max) * (height - 4);
  const pts = vals.map((v, i) => `${X(i).toFixed(1)},${Y(v).toFixed(1)}`).join(" ");
  return (
    <svg width={width} height={height} style={{ display: "block", flexShrink: 0 }}>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
      {n > 0 && <circle cx={X(n - 1)} cy={Y(vals[n - 1])} r="2" fill={color} />}
    </svg>
  );
}

// per-day score bars, colored by tier; the selected day is full-opacity
function ScoreBars({ data, selectedDay, onPick }) {
  const H = 48;
  return (
    <div style={{ display: "flex", alignItems: "flex-end", gap: data.length > 14 ? 2 : 3, height: H }}>
      {data.map((d) => {
        const sel = d.day === selectedDay;
        const bh = d.score == null ? 3 : Math.max(4, (d.score / 100) * H);
        return (
          <div key={d.day} onClick={() => onPick(d.day)}
            title={`${prettyDay(d.day)}: ${d.score == null ? "—" : d.score}`}
            style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "flex-end", height: H, cursor: "pointer" }}>
            <div style={{ height: bh, background: d.score == null ? C.line : scoreColor(d.score),
              opacity: sel ? 1 : 0.45, borderRadius: 2, transition: "opacity .15s" }} />
          </div>
        );
      })}
    </div>
  );
}

// recent-vs-earlier direction for a metric's series; "good" is goal-aware
function trendDir(series, goalType) {
  const vals = series.filter((v) => v != null && !Number.isNaN(v));
  if (vals.length < 2) return { arrow: "→", good: null };
  const half = Math.max(1, Math.floor(vals.length / 2));
  const avg = (a) => a.reduce((s, x) => s + x, 0) / a.length;
  const e = avg(vals.slice(0, vals.length - half));
  const r = avg(vals.slice(vals.length - half));
  const diff = r - e;
  const eps = Math.max(1, Math.abs(e) * 0.05);
  if (Math.abs(diff) < eps) return { arrow: "→", good: null };
  const up = diff > 0;
  return { arrow: up ? "↑" : "↓", good: goalType === "below" ? !up : up };
}

// build a rule-based review from the scorer when the AI call can't run
function fallbackReview(scored) {
  return {
    wins: scored.doingWell.map((m) => ({ text: (m.phrasing && m.phrasing.good) || m.label, metric: m.id })),
    watchOuts: scored.needsAttention.map((m) => ({ text: (m.phrasing && m.phrasing.bad) || m.label, metric: m.id })),
    suggestion: "",
  };
}

// fetch + cache the AI day review; refreshes only when the day's meals change
function useDayReview(profile, day, recs, scored) {
  const [state, setState] = useState({ loading: false, review: null, error: null });
  const sig = recs.map((r) => r.id + ":" + (r.eaten_at || "")).join("|") + "|" + (scored.score == null ? "x" : scored.score);
  useEffect(() => {
    if (!scored.totals || !scored.totals.meals) {
      setState({ loading: false, review: { wins: [], watchOuts: [], suggestion: "" }, error: null });
      return;
    }
    let alive = true;
    setState((s) => ({ ...s, loading: true, error: null }));
    Engine.reviewDay({ profile, day, scored, records: recs }).then((r) => {
      if (!alive) return;
      if (r.ok) setState({ loading: false, review: r.review, error: null });
      else setState({ loading: false, review: null, error: r.error });
    });
    return () => { alive = false; };
  }, [day, sig]); // eslint-disable-line
  return state;
}

function ReviewPoint({ icon, color, text }) {
  return (
    <div style={{ display: "flex", gap: 10, padding: "6px 4px", alignItems: "baseline" }}>
      <span style={{ color, fontSize: 13, flexShrink: 0, width: 13 }}>{icon}</span>
      <span style={{ fontSize: 14, color: C.text, lineHeight: 1.45 }}>{text}</span>
    </div>
  );
}

function DayReview({ state, scored }) {
  if (state.loading) return <div style={{ fontSize: 13, color: C.faint, padding: "4px 4px 2px" }}>Reading your day…</div>;
  // On error, fall back to the deterministic buckets so the section is never empty.
  const review = state.review || fallbackReview(scored);
  const { wins, watchOuts, suggestion } = review;
  if (!wins.length && !watchOuts.length)
    return <div style={{ fontSize: 13, color: C.faint, padding: "4px 4px 2px" }}>Not enough logged to call it yet.</div>;
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {wins.length > 0 && (
        <div>
          <SectionLabel>What went well</SectionLabel>
          {wins.map((w, i) => <ReviewPoint key={i} icon="✓" color={C.good} text={w.text} />)}
        </div>
      )}
      {watchOuts.length > 0 && (
        <div>
          <SectionLabel>Watch outs</SectionLabel>
          {watchOuts.map((w, i) => <ReviewPoint key={i} icon="⚠" color={C.watch} text={w.text} />)}
        </div>
      )}
      {suggestion ? (
        <div style={{ display: "flex", gap: 10, padding: "2px 4px", alignItems: "baseline" }}>
          <span style={{ color: C.accent, fontSize: 13, flexShrink: 0, width: 13 }}>→</span>
          <span style={{ fontSize: 14, color: C.dim, lineHeight: 1.45, fontStyle: "italic" }}>{suggestion}</span>
        </div>
      ) : null}
    </div>
  );
}

// Remembers the selected Recap day across the key={rev} remount that fires when
// a meal is edited/deleted, so editing a past day doesn't snap back to today.
let recapDay = null;

function RecapView({ profile, toast, onChange }) {
  const log = Store.readLog();
  const today = localDay();
  const [day, setDayState] = useState(recapDay && recapDay <= today ? recapDay : today);
  const setDay = (d) => { recapDay = d; setDayState(d); };
  const [win, setWin] = useState(7); // trend window: 7 | 30 days

  const byDay = {};
  for (const rec of log) (byDay[dayOf(rec)] = byDay[dayOf(rec)] || []).push(rec);
  const firstDay = Object.keys(byDay).sort()[0] || today;

  // selected day (live score if it's today, end-of-day otherwise)
  const recs = (byDay[day] || []).slice().sort((a, b) => (a.eaten_at < b.eaten_at ? 1 : -1));
  const scored = Scoring.scoreDay(recs.map((r) => r.meal), profile, whenFor(day, today));

  // trend window ending on the selected day
  const windowData = [];
  for (let i = win - 1; i >= 0; i--) {
    const dk = shiftDay(day, -i);
    const dr = byDay[dk] || [];
    windowData.push({ day: dk, scored: Scoring.scoreDay(dr.map((r) => r.meal), profile, whenFor(dk, today)) });
  }
  const scoreSeries = windowData.map((w) => ({ day: w.day, score: w.scored.score }));
  const scoredDays = scoreSeries.filter((s) => s.score != null);
  const avg = scoredDays.length ? Math.round(scoredDays.reduce((s, x) => s + x.score, 0) / scoredDays.length) : null;

  const stars = Metrics.resolveStars(profile);
  const trendMetrics = Metrics.METRICS.map((m) => m.id).sort((a, b) => stars[b] - stars[a]).slice(0, 4);

  const review = useDayReview(profile, day, recs, scored);

  if (!log.length)
    return <div style={{ color: C.faint, textAlign: "center", padding: "60px 0", fontSize: 15 }}>No meals logged yet.</div>;

  const canPrev = day > firstDay;
  const canNext = day < today;
  const rel = dayLabel(day); // "Today" | "Yesterday" | raw date
  const navBtn = (enabled) => ({
    background: "none", border: "none", color: enabled ? C.dim : C.line, fontSize: 22,
    cursor: enabled ? "pointer" : "default", padding: "0 8px", lineHeight: 1,
  });
  const winBtn = (n) => ({
    background: win === n ? C.card : "transparent", border: `1px solid ${win === n ? C.line : "transparent"}`,
    color: win === n ? C.text : C.faint, borderRadius: 8, padding: "2px 9px", fontSize: 12, cursor: "pointer",
  });

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 26 }}>
      {/* day stepper */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <button onClick={() => canPrev && setDay(shiftDay(day, -1))} style={navBtn(canPrev)}>‹</button>
        <div style={{ textAlign: "center" }}>
          <div style={{ fontSize: 16, color: C.text, fontWeight: 600 }}>{prettyDay(day)}</div>
          {(rel === "Today" || rel === "Yesterday") &&
            <div style={{ fontSize: 12, color: C.faint, marginTop: 1 }}>{rel}</div>}
        </div>
        <button onClick={() => canNext && setDay(shiftDay(day, 1))} style={navBtn(canNext)}>›</button>
      </div>

      {/* day score */}
      <div style={{ textAlign: "center", padding: "6px 0 2px" }}>
        <div style={{ fontSize: 72, fontWeight: 300, fontFamily: SERIF, color: scoreColor(scored.score), lineHeight: 1 }}>
          {scored.score == null ? "—" : scored.score}
        </div>
        <div style={{ fontSize: 13, color: C.faint, marginTop: 2 }}>
          {day === today ? "today’s score so far · live on Today" : "day score"}
        </div>
      </div>

      {/* multi-day trend */}
      <div>
        <SectionLabel right={
          <div style={{ display: "flex", gap: 4 }}>
            <button onClick={() => setWin(7)} style={winBtn(7)}>7d</button>
            <button onClick={() => setWin(30)} style={winBtn(30)}>30d</button>
          </div>
        }>Score · last {win} days</SectionLabel>
        <ScoreBars data={scoreSeries} selectedDay={day} onPick={setDay} />
        {avg != null && <div style={{ fontSize: 12, color: C.faint, marginTop: 6, padding: "0 4px" }}>average {avg}</div>}
      </div>

      {/* per-metric trends */}
      <div>
        <SectionLabel>Trends</SectionLabel>
        {trendMetrics.map((id) => {
          const m = Metrics.BY_ID[id];
          const series = windowData.map((w) => {
            const mm = w.scored.metrics.find((x) => x.id === id);
            return mm ? mm.value : 0;
          });
          const dir = trendDir(series, m.goalType);
          const col = dir.good == null ? C.faint : dir.good ? C.good : C.over;
          return (
            <div key={id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "9px 4px", borderBottom: `1px solid ${C.line}` }}>
              <span style={{ flex: 1, fontSize: 14, color: C.text }}>{m.label}</span>
              <Sparkline values={series} color={dir.good == null ? C.dim : col} />
              <span style={{ fontSize: 14, color: col, width: 16, textAlign: "right" }}>{dir.arrow}</span>
            </div>
          );
        })}
      </div>

      {/* AI reflection */}
      <DayReview state={review} scored={scored} />

      {/* meals */}
      <div>
        <SectionLabel>Meals</SectionLabel>
        {recs.length ? (
          recs.map((rec) => <MealRow key={rec.id} rec={rec} onChange={onChange} toast={toast} />)
        ) : (
          <div style={{ fontSize: 14, color: C.faint, padding: "8px 4px" }}>No meals logged on this day.</div>
        )}
      </div>
    </div>
  );
}

// ── meal editor (paper theme; AI quick-edit + manual grams) ──────────────────
const mkRows = (meal) =>
  (meal.components || []).map((c) => ({ grams: String((c.quantity && c.quantity.grams) || ""), removed: false }));

function MealEditor({ rec, onSaved, onCancel, toast }) {
  const [meal, setMeal] = useState(rec.meal || {});
  const [desc, setDesc] = useState((rec.meal || {}).description || "");
  const [when, setWhen] = useState((rec.eaten_at || "").slice(0, 16));
  const [rows, setRows] = useState(() => mkRows(rec.meal || {}));
  const [ai, setAi] = useState("");
  const [busy, setBusy] = useState(false);
  const comps = meal.components || [];

  const setRow = (i, patch) => setRows((rs) => rs.map((r, j) => (j === i ? { ...r, ...patch } : r)));
  const rowItems = () => rows.map((r) => (r.removed ? { removed: true } : { grams: Number(r.grams) }));
  const applyManual = () =>
    Engine.reviseMeal(meal, { description: desc.trim() || meal.description, items: rowItems() });

  async function askAi() {
    const text = ai.trim();
    if (!text || busy) return;
    setBusy(true);
    const r = await Engine.aiEditMeal(applyManual(), text);
    setBusy(false);
    if (!r.ok) { toast(r.error || "Edit failed"); return; }
    setMeal(r.entry); setDesc(r.entry.description || ""); setRows(mkRows(r.entry)); setAi("");
    toast("AI updated it — review & save");
  }
  function save() {
    if (rowItems().every((it) => it.removed)) { toast("Keep at least one ingredient"); return; }
    Store.updateMeal(rec.id, { meal: applyManual(), eaten_at: when ? when + ":00" : rec.eaten_at });
    toast("Saved ✓"); onSaved();
  }

  const lbl = { fontSize: 12, color: C.faint, marginBottom: 5, display: "block" };
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <div>
        <label style={lbl}>Edit with AI</label>
        <div style={{ display: "flex", gap: 8 }}>
          <input type="text" value={ai} onChange={(e) => setAi(e.target.value)}
            onKeyDown={(e) => { if (e.key === "Enter") askAi(); }}
            placeholder="e.g. double the rice, drop the bun" style={{ ...inputStyle, flex: 1 }} />
          <button onClick={askAi} disabled={busy}
            style={{ ...pillBtn(true), opacity: busy ? 0.5 : 1, padding: "0 16px" }}>{busy ? "…" : "Apply"}</button>
        </div>
        {busy && <div style={{ fontSize: 12, color: C.faint, marginTop: 6 }}>AI is rebuilding the meal…</div>}
      </div>
      <div>
        <label style={lbl}>Description</label>
        <input type="text" value={desc} onChange={(e) => setDesc(e.target.value)} style={inputStyle} />
      </div>
      <div>
        <label style={lbl}>When</label>
        <input type="datetime-local" value={when} onChange={(e) => setWhen(e.target.value)} style={{ ...inputStyle, width: "auto" }} />
      </div>
      <div>
        <label style={lbl}>Ingredients (grams)</label>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {comps.map((c, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 14 }}>
              <span style={{ flex: 1, color: rows[i].removed ? C.faint : C.text, textDecoration: rows[i].removed ? "line-through" : "none" }}>{c.name}</span>
              <input type="number" value={rows[i].grams} disabled={rows[i].removed}
                onChange={(e) => setRow(i, { grams: e.target.value })}
                style={{ ...inputStyle, width: 76, textAlign: "right", padding: "7px 9px", opacity: rows[i].removed ? 0.4 : 1 }} />
              <span style={{ color: C.faint }}>g</span>
              <button onClick={() => setRow(i, { removed: !rows[i].removed })} style={{ ...pillBtn(false), padding: "5px 10px", fontSize: 12 }}>
                {rows[i].removed ? "undo" : "remove"}
              </button>
            </div>
          ))}
        </div>
      </div>
      <div style={{ display: "flex", gap: 8 }}>
        <button onClick={save} style={{ ...pillBtn(true), flex: 1, padding: "10px 0", borderRadius: 12 }}>Save changes</button>
        <button onClick={onCancel} style={{ ...pillBtn(false), flex: 1, padding: "10px 0", borderRadius: 12 }}>Cancel</button>
      </div>
    </div>
  );
}
