/* eslint-disable */
// ===========================================================================
// Omnivore UI — base (palette, helpers, shared styles)
//
// 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. Load FIRST: everything below depends on these names.
// ===========================================================================

const { useState, useRef, useCallback, useEffect } = React;

const Scoring = window.OmniScoring;
const Metrics = window.OmniMetrics;
const Store = window.OmniStore;
const Engine = window.OmniEngine;
const Auth = window.OmniAuth;
const Data = window.OmniData;

// ── palette / type ─────────────────────────────────────────────────────────
const C = {
  bg: "#f7f5f1",      // warm paper
  card: "#f1eee8",    // barely-there raised tone
  line: "#e2ddd3",    // hairline
  text: "#1c1a17",    // warm ink
  dim: "#7c766c",     // secondary
  faint: "#a8a298",   // tertiary / placeholder
  good: "#5b8f6f",    // sage — also the accent
  watch: "#bd8a4a",   // clay
  over: "#b5604f",    // faded terracotta
  accent: "#5b8f6f",
};
const SANS = "'Hanken Grotesk', ui-sans-serif, system-ui, sans-serif";
const SERIF = "'Fraunces', Georgia, serif";

const FontLink = () => (
  <style>{`
    @import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400&family=Hanken+Grotesk:wght@400;500;600&display=swap');
    * { -webkit-tap-highlight-color: transparent; box-sizing: border-box; }
    /* Lock the whole document: no page scroll, no rubber-band. Only the inner
       body of the app scrolls (it sets overscroll-behavior: contain). */
    html, body, #root { margin: 0; height: 100%; }
    html, body { overflow: hidden; overscroll-behavior: none; position: fixed; width: 100%; }
    textarea, input, button { font-family: inherit; }
    ::placeholder { color: ${C.faint}; }
    *::-webkit-scrollbar { width: 0; height: 0; }

    /* Responsive shell. On a phone the frame fills the screen edge-to-edge; on a
       tablet / desktop it becomes a centered phone-shaped card (≈9:19.5). */
    #omni-frame {
      width: 100vw;
      height: 100vh;       /* fallback */
      height: 100dvh;
      border-radius: 0;
      box-shadow: none;
    }
    @media (min-width: 480px) {
      #omni-frame {
        width: 412px;
        height: min(calc(100dvh - 40px), 900px);
        border-radius: 30px;
        box-shadow: 0 14px 50px rgba(60, 50, 35, 0.22);
      }
    }
  `}</style>
);

// ── platform detection (for install instructions) ───────────────────────────
const UA = (typeof navigator !== "undefined" && navigator.userAgent) || "";
const IS_IOS =
  /iphone|ipad|ipod/i.test(UA) ||
  (typeof navigator !== "undefined" && navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
const IS_ANDROID = /android/i.test(UA);

// ── small helpers ───────────────────────────────────────────────────────────
function fmt(v) {
  if (v == null || Number.isNaN(v)) return "—";
  return Math.abs(v) >= 100 ? Math.round(v) : Math.round(v * 10) / 10;
}
const p2 = (n) => String(n).padStart(2, "0");
function nowForInput() {
  const d = new Date();
  return `${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())}T${p2(d.getHours())}:${p2(d.getMinutes())}`;
}
function localDay(d = new Date()) {
  return `${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())}`;
}
const dayOf = (rec) => (rec.eaten_at || rec.logged_at || "").slice(0, 10);
const clampPct = (x) => Math.max(0, Math.min(100, x));
const dot = (s) => (s === "good" ? C.good : s === "watch" ? C.watch : C.over);

function dayLabel(day) {
  if (day === localDay()) return "Today";
  const y = new Date(); y.setDate(y.getDate() - 1);
  if (day === localDay(y)) return "Yesterday";
  return day;
}

// score → status tier for the big number + day sentence
function scoreColor(score) {
  if (score == null) return C.faint;
  if (score >= 75) return C.good;
  if (score >= 50) return C.watch;
  return C.over;
}
function dayLine(score) {
  if (score == null) return "Log a meal to start your day.";
  if (score >= 85) return "On a good day.";
  if (score >= 75) return "Looking solid.";
  if (score >= 60) return "A couple things to watch.";
  if (score >= 45) return "A few things to fix.";
  return "Let’s turn it around.";
}

// metric subscore → three-tier dot
function metricTier(m) {
  if (m.subscore >= 75) return "good";
  if (m.subscore >= 50) return "watch";
  return "over";
}
// "88 / 130g" style value/target string
const ushort = (u) => (u && u.startsWith("%") ? "%" : u === "g" ? "g" : "");
function metricValue(m) {
  const us = ushort(m.unit);
  const v = fmt(m.value);
  if (m.goalType === "range") {
    const max = m.target && typeof m.target === "object" ? fmt(m.target.max) : fmt(m.target);
    if (m.unit === "kcal") return `${v}`;
    return `${v} / ${max}${us}`;
  }
  const t = fmt(m.target);
  return us === "%" ? `${v} / ${t}%` : `${v} / ${t}${us}`;
}
// one-line explanation shown in the info sheet
const METRIC_INFO = {
  protein: "Total grams of protein today vs. your target. Supports muscle and keeps you full.",
  fiber: "Dietary fiber from whole plants. Helps digestion and satiety.",
  plant: "Grams of distinct plant foods — vegetables, fruit, legumes, nuts, seeds, whole grains.",
  healthy_fat: "Unsaturated fats from fish, nuts, seeds and oils. Aim to stay within the range.",
  upf: "Share of your calories from ultra-processed foods. Lower is better.",
  sugar: "Total sugars today vs. your daily ceiling.",
  added_sugar: "Sugars added during processing, not those natural to whole foods.",
  sat_fat: "Saturated fat today vs. your daily ceiling.",
  calories: "Total energy today vs. your target range.",
};
const metricInfo = (m) => METRIC_INFO[m.id] || (m.phrasing && m.phrasing.good) || m.label;

// shared input look
const inputStyle = {
  background: C.card, border: `1px solid ${C.line}`, borderRadius: 12, color: C.text,
  padding: "11px 13px", fontSize: 15, boxSizing: "border-box", outline: "none", width: "100%",
};
const pillBtn = (active) => ({
  background: active ? C.accent : C.card, border: `1px solid ${active ? C.accent : C.line}`,
  color: active ? "#fff" : C.text, borderRadius: 16, padding: "7px 13px", fontSize: 13, cursor: "pointer",
});

// Swipe-down-to-dismiss for bottom sheets. Spread `handlers` onto the grab
// area(s); apply `style` to the sliding panel. Releasing past ~90px closes.
function useDragToClose(onClose) {
  const [dy, setDy] = useState(0);
  const [dragging, setDragging] = useState(false);
  const startY = useRef(null);
  const onTouchStart = (e) => { startY.current = e.touches[0].clientY; setDragging(true); };
  const onTouchMove = (e) => {
    if (startY.current == null) return;
    const d = e.touches[0].clientY - startY.current;
    setDy(d > 0 ? d : 0);
  };
  const onTouchEnd = () => {
    const d = dy; startY.current = null; setDragging(false);
    if (d > 90) onClose(); else setDy(0);
  };
  const handlers = { onTouchStart, onTouchMove, onTouchEnd };
  const style = { transform: dy ? `translateY(${dy}px)` : "none", transition: dragging ? "none" : "transform .25s ease" };
  return { handlers, style, dragging };
}
