// app.jsx — root component. Owns tab state, log form, user, and live data.

const { useEffect, useState, useCallback } = React;
const {
  T: TTT, TabBar, Sheet, H2, Muted, Card, SeverityBadge, Icons: ICN,
  HomeScreen, LogForm, HistoryScreen, PatternsScreen, CalendarScreen, ExportScreen, SettingsScreen, DayDrawer,
  ViewingAsBanner, ViewAsPicker,
  AuthApi, StoreApi, WeatherApi,
} = window;

function App() {
  const [tab, setTab] = useState('home');
  const [user, setUser] = useState(null);
  const [entries, setEntries] = useState([]);
  const [weather, setWeather] = useState(null);
  const [allergens, setAllergens] = useState(null);
  const [pendingSync, setPendingSync] = useState(0);

  const [logOpen, setLogOpen] = useState(false);
  const [logPrefill, setLogPrefill] = useState(null);
  const [toast, setToast] = useState(null);
  const [historyDetail, setHistoryDetail] = useState(null);
  const [dayDrawer, setDayDrawer] = useState(null);
  const [online, setOnline] = useState(navigator.onLine);

  // Share / view-as state. `viewAs` is null when viewing self.
  const [shares, setShares] = useState({ grantedToOthers: [], grantedToMe: [] });
  const [viewAs, setViewAs] = useState(null);            // null | { email, name }
  const [sharedEntries, setSharedEntries] = useState([]);
  const [sharesError, setSharesError] = useState(null);  // surfaced as toast when set

  // Boot: load user, entries, weather, pending
  useEffect(() => {
    (async () => {
      const u = await AuthApi.getCurrentUser();
      setUser(u);
      const es = await StoreApi.listEntries();
      setEntries(es);
      const pc = await StoreApi.pendingCount();
      setPendingSync(pc);
      // Weather + allergens in parallel; tolerant of failure.
      WeatherApi.fetchCurrentWeather().then(setWeather).catch(() => {});
      WeatherApi.fetchAirQuality().then(setAllergens).catch(() => {});

      // Load share state (best-effort; quiet on failure).
      StoreApi.listShares().then(setShares).catch(() => {});

      // Hydrate from server: merge entries where server version is newer.
      // Last updated_at wins per entry. Fire-and-forget; local data is already shown.
      if (navigator.onLine) {
        fetch('/api/entries', { credentials: 'include' })
          .then((r) => r.ok ? r.json() : null)
          .then(async (serverEntries) => {
            if (!Array.isArray(serverEntries) || serverEntries.length === 0) return;
            const local = await StoreApi.listEntries();
            const localById = Object.fromEntries(local.map((e) => [e.id, e]));
            let changed = false;
            for (const se of serverEntries) {
              const le = localById[se.id];
              if (!le || (se.updatedAt && le.updatedAt && se.updatedAt > le.updatedAt)) {
                await StoreApi.putEntryLocal(se);
                changed = true;
              }
            }
            if (changed) setEntries(await StoreApi.listEntries());
          })
          .catch(() => {/* server not yet wired up — local-only mode */});
      }

      // URL-trigger from PWA shortcut (?screen=log&now=1)
      const params = new URLSearchParams(location.search);
      if (params.get('screen') === 'log') openLog();
    })();
  }, []);

  // Online/offline
  useEffect(() => {
    const onOnline = () => { setOnline(true); StoreApi.drainPending().then(() => StoreApi.pendingCount().then(setPendingSync)); };
    const onOffline = () => setOnline(false);
    window.addEventListener('online', onOnline);
    window.addEventListener('offline', onOffline);
    return () => { window.removeEventListener('online', onOnline); window.removeEventListener('offline', onOffline); };
  }, []);

  // Service-worker → page broadcasts
  useEffect(() => {
    const handle = (e) => { if (e.data?.type === 'sync-trigger') StoreApi.drainPending().then(() => StoreApi.pendingCount().then(setPendingSync)); };
    navigator.serviceWorker?.addEventListener('message', handle);
    return () => navigator.serviceWorker?.removeEventListener('message', handle);
  }, []);

  const refreshEntries = useCallback(async () => {
    setEntries(await StoreApi.listEntries());
    setPendingSync(await StoreApi.pendingCount());
  }, []);

  const refreshShares = useCallback(async () => {
    try { setShares(await StoreApi.listShares()); } catch {}
  }, []);

  const loadSharedEntries = useCallback(async (target) => {
    try {
      const list = await StoreApi.fetchEntriesAs(target.email);
      setSharedEntries(list);
    } catch (err) {
      // Share revoked or never existed — bounce back to self with a toast.
      setViewAs(null);
      setSharedEntries([]);
      setSharesError(target.name || target.email);
      setTimeout(() => setSharesError(null), 4500);
      refreshShares();
    }
  }, [refreshShares]);

  const switchViewAs = useCallback((target) => {
    setViewAs(target);
    setSharedEntries([]);
    setTab('home');
    if (target) loadSharedEntries(target);
  }, [loadSharedEntries]);

  const openLog = useCallback((prefill = null) => {
    if (viewAs) return;  // viewers can't log
    setLogPrefill(prefill);
    setLogOpen(true);
  }, [viewAs]);

  const deleteEntry = useCallback(async (entry) => {
    await StoreApi.deleteEntry(entry.id);
    setHistoryDetail(null);
    await refreshEntries();
  }, [refreshEntries]);

  const saveLog = useCallback(async (payload) => {
    const askedCycle = payload.cycleDay != null;
    const entry = StoreApi.newEntry({ ...payload, userEmail: user?.email || null });
    await StoreApi.putEntry(entry);
    if (askedCycle) await StoreApi.setSetting('lastCycleDay', { day: payload.cycleDay, recordedAt: new Date().toISOString() });
    setLogOpen(false);
    setLogPrefill(null);
    await refreshEntries();
    setToast({ entry, when: Date.now() });
    setTimeout(() => setToast(null), 4500);
    setTab('home');
  }, [refreshEntries, user]);

  const displayEntries = viewAs ? sharedEntries : entries;
  const acceptedSharesIn = (shares.grantedToMe || []).filter((s) => s.status === 'accepted');
  const readOnly = !!viewAs;

  return (
    <div style={{ background: TTT.bg, minHeight: '100vh' }}>
      {/* Opaque shield covering the status bar so content doesn't show through when scrolled */}
      <div style={{ position: 'fixed', top: 0, left: 0, right: 0, height: 'env(safe-area-inset-top, 44px)', background: TTT.bg, zIndex: 35, pointerEvents: 'none' }}/>
      {!online && <OfflineBanner pending={pendingSync}/>}
      {viewAs && <ViewingAsBanner ownerName={viewAs.name} ownerEmail={viewAs.email} onSwitchBack={() => switchViewAs(null)}/>}
      {tab === 'home'     && <HomeScreen     user={viewAs ? { name: viewAs.name || viewAs.email, email: viewAs.email } : user} entries={displayEntries} weather={weather} allergens={allergens} onNavigate={setTab} onLog={() => openLog()} readOnly={readOnly}/>}
      {tab === 'history'  && <HistoryScreen  entries={displayEntries} onSelect={setHistoryDetail}/>}
      {tab === 'patterns' && <PatternsScreen entries={displayEntries}/>}
      {tab === 'calendar' && <CalendarScreen entries={displayEntries} onSelectDay={setDayDrawer}/>}
      {tab === 'export'   && <ExportScreen   entries={displayEntries} user={viewAs ? { name: viewAs.name || viewAs.email, email: viewAs.email } : user}/>}
      {tab === 'settings' && <SettingsScreen user={user} pendingSync={pendingSync} shares={shares} onShareChange={refreshShares} onSwitchViewAs={switchViewAs} viewAs={viewAs}/>}

      <TabBar active={tab === 'log' ? 'log' : tab} readOnly={readOnly} onChange={(id) => {
        if (id === 'log') openLog();
        else setTab(id);
      }}/>

      <CornerNav tab={tab} setTab={setTab}/>

      {acceptedSharesIn.length > 0 && (tab !== 'settings' && tab !== 'export') && (
        <div style={{ position: 'fixed', top: 'max(env(safe-area-inset-top), 44px)', left: 12, zIndex: 40 }}>
          <ViewAsPicker
            acceptedShares={acceptedSharesIn}
            currentViewAs={viewAs}
            onPick={switchViewAs}
          />
        </div>
      )}

      <LogForm
        open={logOpen}
        onClose={() => { setLogOpen(false); setLogPrefill(null); }}
        onSave={saveLog}
        prefill={logPrefill}
        weather={weather}
        allergens={allergens}
      />

      <HistoryDetail entry={historyDetail} onClose={() => setHistoryDetail(null)} onEdit={viewAs ? null : (e) => { setHistoryDetail(null); openLog(e); }} onDelete={viewAs ? null : deleteEntry}/>
      <DayDrawer open={!!dayDrawer} day={dayDrawer} onClose={() => setDayDrawer(null)}/>

      {toast && <SuccessToast entry={toast.entry} onClose={() => setToast(null)}/>}
      {sharesError && (
        <div style={{
          position: 'fixed', left: 14, right: 14,
          bottom: 'calc(env(safe-area-inset-bottom, 0px) + 100px)', zIndex: 90,
          background: TTT.ink, color: '#fff', borderRadius: 14, padding: '12px 14px',
          fontFamily: TTT.font, fontSize: 13, boxShadow: '0 10px 30px rgba(0,0,0,0.25)',
        }}>{sharesError}'s data is no longer shared with you.</div>
      )}
    </div>
  );
}

function OfflineBanner({ pending }) {
  return (
    <div style={{
      position: 'fixed', top: 0, left: 0, right: 0, zIndex: 200,
      padding: 'max(env(safe-area-inset-top), 8px) 14px 8px',
      background: TTT.ink, color: '#fff',
      fontFamily: TTT.font, fontSize: 12, fontWeight: 500,
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
    }}>
      <ICN.sync size={14}/>
      Offline — entries save locally{pending ? ` · ${pending} queued` : ''}
    </div>
  );
}

function CornerNav({ tab, setTab }) {
  if (tab === 'export' || tab === 'settings') return null;
  return (
    <div style={{
      position: 'fixed', top: 'max(env(safe-area-inset-top), 44px)', right: 12, zIndex: 40,
      display: 'flex', gap: 6,
    }}>
      <button onClick={() => setTab('export')} title="Export" style={cornerBtn}><ICN.share size={16}/></button>
      <button onClick={() => setTab('settings')} title="Settings" style={cornerBtn}><ICN.gear size={16}/></button>
    </div>
  );
}

const cornerBtn = {
  width: 36, height: 36, borderRadius: 999,
  background: 'rgba(255,255,255,0.85)', backdropFilter: 'blur(8px)',
  border: '1px solid rgba(16,20,24,0.06)', color: TTT.inkSoft,
  display: 'inline-flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
};

function SuccessToast({ entry, onClose }) {
  return (
    <div onClick={onClose} style={{
      position: 'fixed', left: 14, right: 14,
      bottom: 'calc(env(safe-area-inset-bottom, 0px) + 100px)', zIndex: 90,
      background: TTT.ink, color: '#fff', borderRadius: 14, padding: '12px 14px',
      display: 'flex', alignItems: 'center', gap: 10,
      boxShadow: '0 10px 30px rgba(0,0,0,0.25)',
      fontFamily: TTT.font, cursor: 'pointer',
    }}>
      <div style={{ width: 28, height: 28, borderRadius: 999, background: '#3F8050', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <ICN.check size={16} strokeWidth={2.6}/>
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14, fontWeight: 600 }}>Headache logged</div>
        <div style={{ fontSize: 11.5, opacity: 0.7, marginTop: 1 }}>
          {new Date(entry.occurredAt).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })} · {entry.severity}
          {!navigator.onLine ? ' · saved locally' : ''}
        </div>
      </div>
      <ICN.x size={14} style={{ opacity: 0.5 }}/>
    </div>
  );
}

// Read-only detail drawer for a single history entry.
function HistoryDetail({ entry, onClose, onEdit, onDelete }) {
  const [confirmDelete, setConfirmDelete] = React.useState(false);
  if (!entry) return null;
  const hasEnv = entry.weather || (entry.allergens && entry.allergens.pollenLabel && entry.allergens.pollenLabel !== '—');
  return (
    <Sheet open={true} onClose={() => { setConfirmDelete(false); onClose(); }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 14 }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <H2 style={{ fontSize: 22 }}>{new Date(entry.occurredAt).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}</H2>
            <SeverityBadge level={entry.severity} size="md"/>
          </div>
          <Muted size={12} style={{ marginTop: 2 }}>{new Date(entry.occurredAt).toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' })}</Muted>
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          {onDelete && !confirmDelete && (
            <button onClick={() => setConfirmDelete(true)} style={{ background: TTT.surfaceAlt, border: '1px solid ' + TTT.hairline, borderRadius: 8, padding: '6px 10px', fontSize: 12, color: TTT.inkSoft, fontFamily: TTT.font, fontWeight: 500, cursor: 'pointer' }}>Delete</button>
          )}
          {onDelete && confirmDelete && (
            <button onClick={() => onDelete(entry)} style={{ background: '#FEE8E8', border: '1px solid #F5B8B8', borderRadius: 8, padding: '6px 10px', fontSize: 12, color: '#A03A3A', fontFamily: TTT.font, fontWeight: 600, cursor: 'pointer' }}>Confirm delete</button>
          )}
          {onEdit && !confirmDelete && (
            <button onClick={() => onEdit(entry)} style={{ background: TTT.surfaceAlt, border: '1px solid ' + TTT.hairline, borderRadius: 8, padding: '6px 10px', fontSize: 12, color: TTT.inkSoft, fontFamily: TTT.font, fontWeight: 500, cursor: 'pointer' }}>Edit</button>
          )}
          <button onClick={() => { setConfirmDelete(false); onClose(); }} style={{ width: 36, height: 36, borderRadius: 999, background: TTT.surface, border: '1px solid ' + TTT.hairline, color: TTT.inkSoft, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }}><ICN.x size={14}/></button>
        </div>
      </div>

      <DetailRow label="Location" value={(entry.locations || []).join(', ') || '—'}/>
      <DetailRow label="Activity" value={entry.activity || '—'}/>
      <DetailRow label="Notes"    value={entry.notes || '—'}/>

      {hasEnv && (
        <DetailBlock label="Environment">
          {entry.weather && (
            <Muted size={13}>{entry.weather.temp}° · {entry.weather.condition}{entry.weather.humidity != null ? ` · ${Math.round(entry.weather.humidity)}% humidity` : ''}</Muted>
          )}
          {entry.allergens && entry.allergens.pollenLabel && entry.allergens.pollenLabel !== '—' && (
            <Muted size={13} style={{ marginTop: entry.weather ? 4 : 0 }}>
              {entry.allergens.pollenLabel} pollen{entry.allergens.mainPollen?.length ? ` (${entry.allergens.mainPollen.join(', ')})` : ''}
              {entry.allergens.aqi != null ? ` · AQI ${Math.round(entry.allergens.aqi)}` : ''}
            </Muted>
          )}
        </DetailBlock>
      )}

      {(entry.medicine?.taken || entry.allergy?.active || entry.onPeriod) && (
        <DetailBlock label="Response">
          {entry.medicine?.taken && <Muted size={13} style={{ marginBottom: 4 }}><ICN.pill size={12} style={{ verticalAlign: 'text-top' }}/> {entry.medicine.what || 'Medicine'} · {entry.medicine.relief || 'no relief noted'}</Muted>}
          {entry.allergy?.active && <Muted size={13} style={{ marginBottom: 4 }}><ICN.leaf size={12} style={{ verticalAlign: 'text-top' }}/> Allergy: {entry.allergy.symptoms || 'symptoms not specified'}</Muted>}
          {entry.onPeriod && <Muted size={13}><ICN.heart size={12} style={{ verticalAlign: 'text-top' }}/> On period</Muted>}
        </DetailBlock>
      )}
    </Sheet>
  );
}

function DetailRow({ label, value }) {
  return (
    <div style={{ padding: '8px 0', borderBottom: '1px solid ' + TTT.hairline, display: 'flex', gap: 12 }}>
      <div style={{ width: 84, fontSize: 11, fontWeight: 600, letterSpacing: 0.4, textTransform: 'uppercase', color: TTT.muted, paddingTop: 3, flexShrink: 0 }}>{label}</div>
      <div style={{ flex: 1, fontSize: 13.5, color: TTT.ink, lineHeight: 1.45 }}>{value}</div>
    </div>
  );
}

function DetailBlock({ label, children }) {
  return (
    <div style={{ marginTop: 12, padding: 12, background: TTT.surfaceAlt, borderRadius: 12 }}>
      <div style={{ fontSize: 10.5, fontWeight: 600, letterSpacing: 0.5, textTransform: 'uppercase', color: TTT.muted, marginBottom: 6 }}>{label}</div>
      {children}
    </div>
  );
}

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