// app.jsx — WEB app: state machine, photo compression, analyze call, demo fallback.

const PROFILE_KEY = 'chishale.profile';
const loadProfile = () => { try { return JSON.parse(localStorage.getItem(PROFILE_KEY)) || null; } catch { return null; } };

// resize + compress an image File to a jpeg data URL (max 1024px, q .72)
function compressImage(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = reject;
    reader.onload = () => {
      const img = new Image();
      img.onerror = reject;
      img.onload = () => {
        const max = 1024;
        const scale = Math.min(1, max / Math.max(img.width, img.height));
        const c = document.createElement('canvas');
        c.width = Math.round(img.width * scale);
        c.height = Math.round(img.height * scale);
        c.getContext('2d').drawImage(img, 0, 0, c.width, c.height);
        resolve(c.toDataURL('image/jpeg', 0.72));
      };
      img.src = reader.result;
    };
    reader.readAsDataURL(file);
  });
}

// fill any missing fields so screens never crash
function normalize(j, mode) {
  const fb = getCopy('幽默', mode);
  const out = { ...j };
  out.dish = out.dish || '这一餐';
  out.confidence = Math.round(out.confidence || 88);
  out.kcal = Math.round(out.kcal || 0);
  if (!Array.isArray(out.macros) || !out.macros.length) {
    out.macros = [
      { label: '碳水', grams: 0, pct: 33, key: 'carb' },
      { label: '蛋白质', grams: 0, pct: 33, key: 'protein' },
      { label: '脂肪', grams: 0, pct: 34, key: 'fat' },
    ];
  } else {
    const KM = { '碳水': 'carb', '蛋白质': 'protein', '蛋白': 'protein', '脂肪': 'fat' };
    out.macros = out.macros.map(m => ({ label: m.label, grams: Math.round(m.grams || 0), pct: Math.round(m.pct || 0), key: m.key || KM[m.label] || 'carb' }));
  }
  out.items = Array.isArray(out.items) ? out.items.map(it => ({ n: it.n || it.name || '', k: Math.round(it.k || it.kcal || 0) })).filter(it => it.n) : [];
  out.run = Math.round(out.run || Math.max(10, out.kcal / 9));
  out.steps = Math.round(out.steps || out.kcal * 13);
  out.verdict = (out.verdict && out.verdict.title) ? out.verdict : fb.verdict;
  out.tips = (Array.isArray(out.tips) && out.tips.length) ? out.tips : fb.tips;
  out.nextMeal = Array.isArray(out.nextMeal) && out.nextMeal.length ? out.nextMeal
    : (mode === '增肌' ? ['鸡胸 + 糙米饭', '一杯牛奶', '水煮蛋 2 颗'] : ['清炒时蔬', '水煮蛋', '无糖豆浆', '一份水果']);
  return out;
}

function demoData(mode) {
  return normalize({ ...SAMPLE }, mode);
}

async function analyzeFood(imageDataUrl, profile, mode, budget) {
  try {
    const resp = await fetch('/api/analyze', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ image: imageDataUrl, profile, mode, budget }),
    });
    if (!resp.ok) throw new Error('http ' + resp.status);
    const j = await resp.json();
    if (j.error) throw new Error(j.error);
    return { data: normalize(j, mode), demo: false };
  } catch (e) {
    // backend not connected (e.g. this preview) → show flow with sample data
    await new Promise(r => setTimeout(r, 1400));
    return { data: demoData(mode), demo: true };
  }
}

function App() {
  const theme = React.useMemo(() => buildTheme({}), []);
  const [screen, setScreen] = React.useState('home');
  const [profile, setProfile] = React.useState(() => loadProfile() || { gender: '男', height: 175, weight: 70 });
  const [mode, setMode] = React.useState('减脂');
  const [photoUrl, setPhotoUrl] = React.useState(null);
  const [result, setResult] = React.useState(null);
  const [demo, setDemo] = React.useState(false);
  const budget = computeBudget(profile, mode);

  React.useEffect(() => { try { localStorage.setItem(PROFILE_KEY, JSON.stringify(profile)); } catch {} }, [profile]);

  // reset scroll to top whenever the screen changes (so a new page never opens mid-scroll)
  React.useLayoutEffect(() => {
    window.scrollTo(0, 0);
    document.documentElement.scrollTop = 0;
    document.body.scrollTop = 0;
  }, [screen]);

  async function onPick(file) {
    let url;
    try { url = await compressImage(file); }
    catch { url = URL.createObjectURL(file); }
    setPhotoUrl(url);
    setScreen('analyzing');
    const { data, demo } = await analyzeFood(url, profile, mode, budget);
    setResult(data); setDemo(demo); setScreen('result');
  }

  return (
    <ThemeCtx.Provider value={theme}>
      <div className="cc-scroll" style={{ maxWidth: 460, margin: '0 auto', minHeight: '100dvh',
        background: theme.bg, position: 'relative',
        boxShadow: '0 0 60px rgba(43,34,24,0.12)' }}>
        {screen === 'home' && <HomeWeb profile={profile} setProfile={setProfile} mode={mode} setMode={setMode} onPick={onPick} />}
        {screen === 'analyzing' && <AnalyzingWeb photoUrl={photoUrl} />}
        {screen === 'result' && result && <ResultWeb data={result} mode={mode} budget={budget} photoUrl={photoUrl} demo={demo}
          onBack={() => setScreen('home')} onReshoot={() => setScreen('home')} onAdvice={() => setScreen('advice')} />}
        {screen === 'advice' && result && <AdviceWeb data={result} mode={mode} onBack={() => setScreen('result')} onLog={() => setScreen('home')} />}
      </div>
    </ThemeCtx.Provider>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
