// app.jsx — Root: navigation, sheets, persistence.
// Fullscreen mount (no iOS frame). Local-first: every state change is written
// to localStorage so data survives reloads and offline use.

const STORAGE_KEY = 'monthlyPlan.v1';
const DARK_KEY    = 'monthlyPlan.dark';

const LIGHT_TOKENS = {
  paper:   '#F4F0E8', card: '#FAF7F1', inset: '#EFEAE0',
  divider: 'rgba(33, 28, 22, 0.08)', hairline: 'rgba(33, 28, 22, 0.06)',
  ink: '#1F1B14', ink2: '#5A5247', ink3: '#8E867A', ink4: '#B5AFA3',
  sage: '#6F7B5F', sageDeep: '#4F5942', sageSoft: '#E2E5D6',
  ok: '#5E7A4F', warn: '#B07B2C', bad: '#A14438',
};
const DARK_TOKENS = {
  paper:   '#0E0D0B', card: '#1A1815', inset: '#141210',
  divider: 'rgba(255, 248, 232, 0.08)', hairline: 'rgba(255, 248, 232, 0.05)',
  ink: '#F4F0E8', ink2: '#B5AFA3', ink3: '#8E867A', ink4: '#5A5247',
  sage: '#9DAE8B', sageDeep: '#B8C7A6', sageSoft: '#2A2E22',
  ok: '#8FB07A', warn: '#D9A55A', bad: '#D27468',
};

function loadInitialState() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw) return normalizeState(JSON.parse(raw));
  } catch (_) {}
  return makeEmptyState();
}

function loadDark() {
  try { return localStorage.getItem(DARK_KEY) === 'true'; } catch (_) { return false; }
}

function App({ dark, setDark }) {
  const initial = React.useMemo(() => loadInitialState(), []);
  const store = useAppStore(initial);

  // Persist on every change (debounced — collapses bursts like multi-step edits).
  // Skip writes when the serialized blob hasn't actually changed (e.g. import of
  // identical data, slider rebalance to the same numbers) — saves sync IO.
  const lastWriteRef = React.useRef(null);
  React.useEffect(() => {
    const id = setTimeout(() => {
      try {
        const blob = JSON.stringify(store.state);
        if (blob === lastWriteRef.current) return;
        localStorage.setItem(STORAGE_KEY, blob);
        lastWriteRef.current = blob;
      } catch (_) {}
    }, 200);
    return () => clearTimeout(id);
  }, [store.state]);

  const [tab, setTab] = React.useState('home');
  const [moreScreen, setMoreScreen] = React.useState(null);
  const [historyYm, setHistoryYm] = React.useState(null);

  const [incomeSheet, setIncomeSheet] = React.useState({ open: false, initial: null });
  const [subSheet, setSubSheet] = React.useState({ open: false, initial: null });
  const [coffeeSaleSheet, setCoffeeSaleSheet] = React.useState({ open: false });
  const [coffeeExpenseSheet, setCoffeeExpenseSheet] = React.useState({ open: false });
  const [machineSheet, setMachineSheet] = React.useState({ open: false });

  const [flashId, setFlashId] = React.useState(null);
  const flash = (id) => {
    setFlashId(id);
    setTimeout(() => setFlashId(curr => (curr === id ? null : curr)), 1200);
  };

  const openAddIncome  = () => setIncomeSheet({ open: true, initial: null });
  const openEditIncome = (inc) => setIncomeSheet({ open: true, initial: inc });
  const closeIncomeSheet = () => setIncomeSheet({ open: false, initial: null });
  const saveIncome = (entry) => {
    if (entry.id) store.editIncome(entry.id, entry);
    else { const id = store.addIncome(entry); flash(id); }
    closeIncomeSheet();
  };

  const openAddSub  = () => setSubSheet({ open: true, initial: null });
  const openEditSub = (s) => setSubSheet({ open: true, initial: s });
  const closeSubSheet = () => setSubSheet({ open: false, initial: null });
  const saveSub = (entry) => {
    if (entry.id) store.editSub(entry.id, entry);
    else { const id = store.addSub(entry); flash(id); }
    closeSubSheet();
  };
  const deleteSubFromSheet = (id) => { store.deleteSub(id); closeSubSheet(); };

  const activeMachine =
    store.coffee.machines.find(m => m.id === store.coffee.activeMachineId)
    || store.coffee.machines[0]
    || null;

  const openAddCoffeeSale    = () => setCoffeeSaleSheet({ open: true });
  const openAddCoffeeExpense = () => setCoffeeExpenseSheet({ open: true });
  const openAddMachine       = () => setMachineSheet({ open: true });
  const saveCoffeeSale = (entry) => {
    if (activeMachine) { const id = store.addCoffeeSale({ machineId: activeMachine.id, ...entry }); flash(id); }
    setCoffeeSaleSheet({ open: false });
  };
  const saveCoffeeExpense = (entry) => {
    if (activeMachine) { const id = store.addCoffeeExpense({ machineId: activeMachine.id, ...entry }); flash(id); }
    setCoffeeExpenseSheet({ open: false });
  };
  const saveMachine = (entry) => {
    const id = store.addMachine(entry);
    flash(id);
    setMachineSheet({ open: false });
  };

  let view;
  if (historyYm) {
    view = <HistoryDetailScreen store={store} ym={historyYm} onBack={() => setHistoryYm(null)}/>;
  } else if (moreScreen === 'settings') {
    view = <SettingsScreen store={store} onBack={() => setMoreScreen(null)}/>;
  } else if (moreScreen === 'history') {
    view = <HistoryScreen store={store} onOpenMonth={(ym) => setHistoryYm(ym)}/>;
  } else if (moreScreen === 'subs') {
    view = <SubsScreen store={store} flashId={flashId} onAdd={openAddSub} onEdit={openEditSub} onBack={() => setMoreScreen(null)}/>;
  } else if (moreScreen === 'machines') {
    view = <ManageMachinesScreen store={store} onBack={() => setMoreScreen(null)} onOpenAddMachine={openAddMachine}/>;
  } else if (tab === 'home') {
    view = <HomeScreen store={store} flashId={flashId}
      onAddIncome={openAddIncome} onEditIncome={openEditIncome}
      onOpenSubs={() => setMoreScreen('subs')}
      dark={dark} onToggleTheme={() => setDark(!dark)}/>;
  } else if (tab === 'coffee') {
    view = <CoffeeScreen store={store}
      onAddExpense={openAddCoffeeExpense}
      onAddSale={openAddCoffeeSale}
      onManageMachines={() => setMoreScreen('machines')}/>;
  } else if (tab === 'cal') {
    view = <CalendarScreen store={store}/>;
  } else if (tab === 'stats') {
    view = <StatsScreen store={store}/>;
  } else if (tab === 'more') {
    view = <MoreScreen onNav={setMoreScreen}/>;
  }

  const onTabChange = (next) => {
    if (next !== 'more') { setMoreScreen(null); setHistoryYm(null); }
    setTab(next);
  };

  const showFab =
    !historyYm &&
    ((moreScreen === 'subs') ||
     (moreScreen === 'machines') ||
     // Hide FAB on empty home — empty card has its own '+ Add income' button,
     // and FAB would otherwise overlap the card pushed to the bottom.
     (!moreScreen && tab === 'home' && store.state.incomes.length > 0));
  const fabAction =
    moreScreen === 'subs'     ? openAddSub :
    moreScreen === 'machines' ? openAddMachine :
    openAddIncome;

  return (
    <div style={{
      width: '100%', height: '100%', position: 'relative',
      background: tokens.paper,
      fontFamily: tokens.sans,
      WebkitFontSmoothing: 'antialiased',
      overflow: 'hidden',
    }}>
      <div style={{
        position: 'absolute', top: 12, left: 0, right: 0, bottom: 0,
        overflowX: 'hidden',
      }}>
        <PullToRefresh
          enabled={tab === 'home' && !moreScreen && !historyYm}
          onRefresh={() => new Promise(r => setTimeout(r, 600))}
        >
          <div key={`${tab}|${moreScreen || ''}|${historyYm || ''}`} style={{ minHeight: '100%', height: tab === 'stats' ? '100%' : undefined }}>
            {view}
          </div>
        </PullToRefresh>
      </div>

      {showFab && <FAB onClick={fabAction}/>}
      <TabBar active={tab} onChange={onTabChange}/>

      <AddIncomeSheet open={incomeSheet.open} initial={incomeSheet.initial}
        onClose={closeIncomeSheet} onSave={saveIncome} globalSplit={store.state.splits}/>
      <AddSubSheet open={subSheet.open} initial={subSheet.initial}
        onClose={closeSubSheet} onSave={saveSub} onDelete={deleteSubFromSheet}/>

      <AddCoffeeSaleSheet
        open={coffeeSaleSheet.open}
        onClose={() => setCoffeeSaleSheet({ open: false })}
        onSave={saveCoffeeSale}
        machineName={(activeMachine && activeMachine.name) || 'machine'}
      />
      <AddCoffeeExpenseSheet
        open={coffeeExpenseSheet.open}
        onClose={() => setCoffeeExpenseSheet({ open: false })}
        onSave={saveCoffeeExpense}
        machineName={(activeMachine && activeMachine.name) || 'machine'}
      />
      <AddMachineSheet
        open={machineSheet.open}
        onClose={() => setMachineSheet({ open: false })}
        onSave={saveMachine}
      />
    </div>
  );
}

function Boot() {
  const [dark, setDarkRaw] = React.useState(loadDark);
  const setDark = (v) => {
    setDarkRaw(v);
    try { localStorage.setItem(DARK_KEY, String(!!v)); } catch (_) {}
  };

  // Mutate the shared `tokens` object synchronously during render so children
  // read the right palette on the same pass (useEffect would commit too late).
  Object.assign(tokens, dark ? DARK_TOKENS : LIGHT_TOKENS);

  React.useEffect(() => {
    const bg = dark ? '#0E0D0B' : '#F4F0E8';
    document.documentElement.style.background = bg;
    document.body.style.background = bg;
    document.documentElement.style.colorScheme = dark ? 'dark' : 'light';
    const meta = document.getElementById('theme-color');
    if (meta) meta.setAttribute('content', bg);
  }, [dark]);

  const [reveal, setReveal] = React.useState(null);
  const prevDarkRef = React.useRef(dark);
  React.useEffect(() => {
    if (prevDarkRef.current !== dark) {
      const oldColor = prevDarkRef.current ? '#0E0D0B' : '#F4F0E8';
      const id = Math.random().toString(36).slice(2);
      setReveal({ id, color: oldColor });
      const timer = setTimeout(() => {
        setReveal(curr => (curr && curr.id === id ? null : curr));
      }, 540);
      prevDarkRef.current = dark;
      return () => clearTimeout(timer);
    }
  }, [dark]);

  return (
    <div className="device-content" style={{
      width: '100%', height: '100%', position: 'relative',
      background: dark ? '#0E0D0B' : '#F4F0E8', overflow: 'hidden',
      '--rx': '88%', '--ry': '4%',
    }}>
      <PinGate>
        <App dark={dark} setDark={setDark}/>
      </PinGate>
      {reveal && (
        <div key={reveal.id} className="theme-reveal" style={{ background: reveal.color }}/>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('device-mount')).render(<Boot/>);
