import { AnimatePresence, motion } from "framer-motion";
import { Plus } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { flavourMeta, mergedFlavours } from "./data/flavours";
import { QUICK_ADDS, type QuickAddItem } from "./data/quickAdds";
import {
  THEME_MODE_STORAGE_KEY,
  THEME_STORAGE_KEY,
  getThemeById,
  getThemeTokens,
  normaliseThemeId,
  normaliseThemeMode,
  readStoredThemeId,
  readStoredThemeMode,
  type ThemeMode,
} from "./data/themes";
import { themeTokensToStyle } from "./lib/themeTokens";
import { account, appwriteConfig, Channel, client, pingAppwrite } from "./lib/appwrite";
import {
  appwriteErrorMessage,
  createEntries,
  createEntry,
  deleteEntry as deleteEntryDocument,
  listEntries,
  updateEntry,
} from "./lib/appwriteEntries";
import { BarcodeScannerModal } from "./components/BarcodeScannerModal";
import { ConfirmDialog } from "./components/ConfirmDialog";
import { EntryModal } from "./components/EntryModal";
import { ImportPreviewModal } from "./components/ImportPreviewModal";
import { LoadingScreen } from "./components/LoadingScreen";
import { MobileNav } from "./components/MobileNav";
import { OnboardingScreen } from "./components/OnboardingScreen";
import { ShellBackdrop } from "./components/ShellBackdrop";
import { Sidebar } from "./components/Sidebar";
import { StatusRail } from "./components/StatusRail";
import { TopBar } from "./components/TopBar";
import { AuthView } from "./components/views/AuthView";
import { LogbookView } from "./components/views/LogbookView";
import { OverviewView } from "./components/views/OverviewView";
import { SettingsView } from "./components/views/SettingsView";
import { TrendsView } from "./components/views/TrendsView";
import { buildDashboard, buildInsights } from "./lib/dashboard";
import { applyFilters, DEFAULT_FILTERS } from "./lib/filters";
import { VIEW_TRANSITION } from "./lib/motion";
import {
  evaluateLimits,
  limitStatusMessage,
  mergePrefsWithLimits,
  parseUserLimits,
} from "./lib/userLimits";
import { createExcelExport, downloadBlob, parseExcelImport } from "./lib/excel";
import { buildEntryDerived, groupByDay, groupByFlavour, groupByWeek, humanDateTime, makeId } from "./lib/metrics";
import { clearCachedEntries, readCachedEntries, writeCachedEntries } from "./lib/entryCache";
import { buildJsonImportPreview, exportPayload, parseImport } from "./lib/storage";
import { useToasts } from "./lib/toasts";
import type {
  AppView,
  AuthUser,
  EntryDraft,
  Filters,
  ImportPreview,
  ImportProgress,
  RedBullEntry,
  SetupStatus,
  UserLimits,
} from "./types";

type PendingLimitAction = {
  kind: "save" | "quick";
  draft: EntryDraft;
  editingId?: string;
  quickLabel?: string;
};

function App() {
  const [themeId, setThemeId] = useState(() => readStoredThemeId());
  const [themeMode, setThemeMode] = useState<ThemeMode>(() => readStoredThemeMode());
  const [systemDark, setSystemDark] = useState(
    () => typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches,
  );
  const activeTheme = useMemo(() => getThemeById(themeId), [themeId]);
  const resolvedMode: "light" | "dark" = themeMode === "system" ? (systemDark ? "dark" : "light") : themeMode;
  const shellStyle = useMemo(() => themeTokensToStyle(getThemeTokens(themeId, resolvedMode)), [themeId, resolvedMode]);
  const [user, setUser] = useState<AuthUser | null>(null);
  const [authLoading, setAuthLoading] = useState(true);
  const [authError, setAuthError] = useState("");
  const [setupStatus, setSetupStatus] = useState<SetupStatus>({
    state: "checking",
    message: "Pinging Appwrite...",
  });
  const [entries, setEntries] = useState<RedBullEntry[]>([]);
  const [filters, setFilters] = useState<Filters>(DEFAULT_FILTERS);
  const [activeView, setActiveView] = useState<AppView>("overview");
  const [isEntryModalOpen, setIsEntryModalOpen] = useState(false);
  const [entryInitialDraft, setEntryInitialDraft] = useState<EntryDraft | null>(null);
  const [editingEntry, setEditingEntry] = useState<RedBullEntry | null>(null);
  const [isBarcodeScannerOpen, setIsBarcodeScannerOpen] = useState(false);
  const [isResetOpen, setIsResetOpen] = useState(false);
  const [notice, setNotice] = useState("Appwrite session pending.");
  const [dataLoading, setDataLoading] = useState(false);
  const [busyAction, setBusyAction] = useState<string | null>(null);
  const [syncError, setSyncError] = useState("");
  const [importPreview, setImportPreview] = useState<ImportPreview | null>(null);
  const [importProgress, setImportProgress] = useState<ImportProgress | null>(null);
  const [userLimits, setUserLimits] = useState<UserLimits>({});
  const [limitConfirmOpen, setLimitConfirmOpen] = useState(false);
  const [limitConfirmMessage, setLimitConfirmMessage] = useState("");
  const [pendingLimitAction, setPendingLimitAction] = useState<PendingLimitAction | null>(null);
  const [setupOpen, setSetupOpen] = useState(false);
  const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);
  const { pushToast } = useToasts();
  const excelFileInputRef = useRef<HTMLInputElement>(null);
  const jsonFileInputRef = useRef<HTMLInputElement>(null);
  const importInFlightRef = useRef(false);
  const refreshDebounceRef = useRef<number | null>(null);
  const cacheHydratedRef = useRef(false);

  useEffect(() => {
    localStorage.setItem(THEME_STORAGE_KEY, normaliseThemeId(themeId));
  }, [themeId]);

  useEffect(() => {
    localStorage.setItem(THEME_MODE_STORAGE_KEY, themeMode);
  }, [themeMode]);

  useEffect(() => {
    if (typeof window === "undefined") return undefined;
    const media = window.matchMedia("(prefers-color-scheme: dark)");
    const handleChange = (event: MediaQueryListEvent) => setSystemDark(event.matches);
    setSystemDark(media.matches);
    media.addEventListener("change", handleChange);
    return () => media.removeEventListener("change", handleChange);
  }, []);

  const refreshEntries = useCallback(
    async (userId: string, showLoader = true) => {
      if (showLoader) setDataLoading(true);
      setSyncError("");
      try {
        const remoteEntries = await listEntries(userId);
        setEntries(sortEntries(remoteEntries));
        writeCachedEntries(userId, remoteEntries);
        cacheHydratedRef.current = false;
        setNotice(`Synced ${remoteEntries.length} Appwrite entr${remoteEntries.length === 1 ? "y" : "ies"}.`);
      } catch (error) {
        if (cacheHydratedRef.current) {
          // Cached entries are on screen, so one info toast replaces the generic error toast.
          pushToast({ tone: "info", message: "Couldn't reach the server — showing saved data" });
        } else {
          const message = appwriteErrorMessage(error);
          setSyncError(message);
          setNotice("Appwrite sync failed.");
        }
      } finally {
        if (showLoader) setDataLoading(false);
      }
    },
    [pushToast],
  );

  useEffect(() => {
    let mounted = true;

    async function bootstrap() {
      setAuthLoading(true);
      setAuthError("");
      try {
        await pingAppwrite();
        if (!mounted) return;
        setSetupStatus({ state: "ok", message: "Appwrite ping succeeded." });
      } catch (error) {
        if (!mounted) return;
        setSetupStatus({
          state: "error",
          message: error instanceof Error ? error.message : "Appwrite ping failed.",
        });
      }

      try {
        const currentUser = await account.get();
        if (!mounted) return;
        setUser(currentUser);
        setUserLimits(parseUserLimits(currentUser.prefs));
        if (typeof currentUser.prefs.themeId === "string" && currentUser.prefs.themeId) {
          setThemeId(normaliseThemeId(currentUser.prefs.themeId));
        }
        if (typeof currentUser.prefs.themeMode === "string" && currentUser.prefs.themeMode) {
          setThemeMode(normaliseThemeMode(currentUser.prefs.themeMode));
        }
        setNotice(`Signed in as ${currentUser.email || currentUser.name || "Appwrite user"}.`);
        if (!currentUser.prefs.onboarded) {
          setSetupOpen(true);
        }
      } catch {
        if (!mounted) return;
        setUser(null);
        setNotice("Sign in to sync entries across devices.");
      } finally {
        if (mounted) setAuthLoading(false);
      }
    }

    void bootstrap();
    return () => {
      mounted = false;
    };
  }, []);

  useEffect(() => {
    if (!user) {
      cacheHydratedRef.current = false;
      setEntries([]);
      return;
    }
    const cachedEntries = readCachedEntries(user.$id);
    cacheHydratedRef.current = cachedEntries !== null;
    if (cachedEntries) {
      // Render the last synced entries immediately; the refresh below replaces them.
      setEntries(sortEntries(cachedEntries));
    }
    void refreshEntries(user.$id);
  }, [refreshEntries, user]);

  useEffect(() => {
    if (!user) return undefined;

    // Trailing debounce: collapse bursts of row events (e.g. bulk imports) into one refetch.
    const scheduleRefresh = () => {
      if (refreshDebounceRef.current !== null) {
        window.clearTimeout(refreshDebounceRef.current);
      }
      refreshDebounceRef.current = window.setTimeout(() => {
        refreshDebounceRef.current = null;
        void refreshEntries(user.$id, false);
      }, 500);
    };

    const unsubscribe = client.subscribe<Record<string, unknown>>(
      Channel.tablesdb(appwriteConfig.databaseId).table(appwriteConfig.collectionId).row(),
      (event) => {
        // Bulk imports update local state directly; their row events need no refetch.
        if (importInFlightRef.current) return;
        if (event.payload?.userId === user.$id) scheduleRefresh();
      },
    );

    return () => {
      if (refreshDebounceRef.current !== null) {
        window.clearTimeout(refreshDebounceRef.current);
        refreshDebounceRef.current = null;
      }
      unsubscribe();
    };
  }, [refreshEntries, user]);

  useEffect(() => {
    if (notice) pushToast({ tone: "success", message: notice });
  }, [notice, pushToast]);

  useEffect(() => {
    if (syncError) pushToast({ tone: "error", message: syncError });
  }, [syncError, pushToast]);

  const allFlavours = useMemo(
    () => mergedFlavours(entries.map((entry) => entry.flavour)),
    [entries],
  );
  const entryDerived = useMemo(() => buildEntryDerived(entries), [entries]);
  const entriesInView = useMemo(
    () => sortEntries(applyFilters(entries, filters)),
    [entries, filters],
  );
  const summary = useMemo(() => buildDashboard(entries), [entries]);
  const limitCheck = useMemo(() => evaluateLimits(userLimits, entries), [userLimits, entries]);
  const chartData = useMemo(() => groupByDay(entriesInView), [entriesInView]);
  const weekData = useMemo(() => groupByWeek(entriesInView), [entriesInView]);
  const flavourData = useMemo(() => groupByFlavour(entriesInView), [entriesInView]);
  const insights = useMemo(() => buildInsights(entries), [entries]);
  const recentEntries = useMemo(() => entries.slice(0, 5), [entries]);
  const pendingDeleteEntry = pendingDeleteId ? entries.find((entry) => entry.id === pendingDeleteId) ?? null : null;
  const deletingId = busyAction?.startsWith("delete-") ? busyAction.slice("delete-".length) : null;

  async function login(email: string, password: string) {
    setBusyAction("auth");
    setAuthError("");
    try {
      await account.createEmailPasswordSession({ email, password });
      const currentUser = await account.get();
      setUser(currentUser);
      setUserLimits(parseUserLimits(currentUser.prefs));
      if (typeof currentUser.prefs.themeId === "string" && currentUser.prefs.themeId) {
        setThemeId(normaliseThemeId(currentUser.prefs.themeId));
      }
      if (typeof currentUser.prefs.themeMode === "string" && currentUser.prefs.themeMode) {
        setThemeMode(normaliseThemeMode(currentUser.prefs.themeMode));
      }
      setNotice(`Signed in as ${currentUser.email}.`);
      if (!currentUser.prefs.onboarded) {
        setSetupOpen(true);
      }
    } catch (error) {
      setAuthError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
    }
  }

  async function signup(name: string, email: string, password: string) {
    setBusyAction("auth");
    setAuthError("");
    try {
      await account.create({
        userId: makeId(),
        email,
        password,
        name: name.trim() || undefined,
      });
      await account.createEmailPasswordSession({ email, password });
      const currentUser = await account.get();
      setUser(currentUser);
      setNotice(`Welcome, ${currentUser.name || currentUser.email}.`);
      setSetupOpen(true);
    } catch (error) {
      setAuthError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
    }
  }

  async function logout() {
    setBusyAction("logout");
    setSyncError("");
    try {
      await account.deleteSession({ sessionId: "current" });
      if (user) clearCachedEntries(user.$id);
      setUser(null);
      setEntries([]);
      setNotice("Logged out.");
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
    }
  }

  function openNewEntry() {
    setEditingEntry(null);
    setEntryInitialDraft(null);
    setIsEntryModalOpen(true);
  }

  function openBarcodeScanner() {
    setIsBarcodeScannerOpen(true);
  }

  function addBarcodeDraft(draft: EntryDraft) {
    setIsBarcodeScannerOpen(false);
    saveDraftWithLimitCheck(draft);
  }

  function editBarcodeDraft(draft: EntryDraft) {
    setIsBarcodeScannerOpen(false);
    setEditingEntry(null);
    setEntryInitialDraft(draft);
    setIsEntryModalOpen(true);
  }

  function logEntryAgain(entry: RedBullEntry) {
    setEditingEntry(null);
    setEntryInitialDraft({
      cans: entry.cans,
      flavour: entry.flavour,
      flavourAccent: entry.flavourAccent,
      sizeMl: entry.sizeMl,
      pricePerCan: entry.pricePerCan,
      dateTime: new Date().toISOString(),
      notes: entry.notes ?? "",
      store: entry.store ?? "",
      sugarFree: entry.sugarFree,
      caffeineMgPerCan: entry.caffeineMgPerCan,
      source: entry.source,
    });
    setIsEntryModalOpen(true);
  }

  async function saveUserLimits(next: UserLimits) {
    if (!user) return;
    setBusyAction("save-limits");
    setSyncError("");
    try {
      const prefs = mergePrefsWithLimits(user.prefs, next);
      await account.updatePrefs(prefs);
      const currentUser = await account.get();
      setUser(currentUser);
      setUserLimits(parseUserLimits(currentUser.prefs));
      setNotice("Daily limits saved to your account.");
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
    }
  }

  async function saveOnboarding(limits: UserLimits, onboardingThemeId: string): Promise<boolean> {
    if (!user) return false;
    setBusyAction("save-onboarding");
    setSyncError("");
    try {
      const limitsPrefs = mergePrefsWithLimits(user.prefs, limits);
      const nextPrefs = {
        ...limitsPrefs,
        themeId: onboardingThemeId,
        themeMode,
        onboarded: true,
      };
      await account.updatePrefs(nextPrefs);
      const currentUser = await account.get();
      setUser(currentUser);
      setUserLimits(parseUserLimits(currentUser.prefs));
      setThemeId(onboardingThemeId);
      setSetupOpen(false);
      setNotice("Setup saved.");
      return true;
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
      return false;
    } finally {
      setBusyAction(null);
    }
  }

  async function saveDraft(action: PendingLimitAction) {
    if (!user) return;
    const loadingKey = action.kind === "quick" ? `quick-${action.quickLabel ?? "add"}` : "save-entry";
    setBusyAction(loadingKey);
    setSyncError("");
    try {
      const editing = action.editingId ? entries.find((entry) => entry.id === action.editingId) : null;
      const saved = editing
        ? await updateEntry(user.$id, editing.id, { ...action.draft, source: editing.source })
        : await createEntry(user.$id, { ...action.draft, source: action.draft.source ?? "manual" });
      setEntries((current) =>
        sortEntries(editing ? current.map((entry) => (entry.id === saved.id ? saved : entry)) : [saved, ...current]),
      );
      setNotice(editing ? "Entry updated in Appwrite." : "Entry saved to Appwrite.");
      setEditingEntry(null);
      setEntryInitialDraft(null);
      setIsEntryModalOpen(false);
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
      setLimitConfirmOpen(false);
      setPendingLimitAction(null);
      setLimitConfirmMessage("");
    }
  }

  function saveDraftWithLimitCheck(draft: EntryDraft, editingId?: string) {
    const check = evaluateLimits(userLimits, entries, { draft, excludeEntryId: editingId });
    if (check.violations.length) {
      setPendingLimitAction({ kind: "save", draft, editingId });
      setLimitConfirmMessage(limitStatusMessage(check.violations, check, userLimits));
      setLimitConfirmOpen(true);
      return;
    }
    void saveDraft({ kind: "save", draft, editingId });
  }

  async function saveEntryDraft(draft: EntryDraft) {
    if (!user) return;
    saveDraftWithLimitCheck(draft, editingEntry?.id);
  }

  async function quickAdd(item: QuickAddItem) {
    if (!user) return;
    const meta = flavourMeta(item.flavour);
    const draft: EntryDraft = {
      cans: 1,
      flavour: item.flavour,
      flavourAccent: meta.accent,
      sizeMl: item.sizeMl,
      pricePerCan: item.pricePerCan,
      dateTime: new Date().toISOString(),
      sugarFree: Boolean(meta.sugarFree),
      notes: "Quick add",
      store: "",
      source: "quick-add",
    };

    const check = evaluateLimits(userLimits, entries, { draft });
    if (check.violations.length) {
      setPendingLimitAction({ kind: "quick", draft, quickLabel: item.label });
      setLimitConfirmMessage(limitStatusMessage(check.violations, check, userLimits));
      setLimitConfirmOpen(true);
      return;
    }

    void saveDraft({ kind: "quick", draft, quickLabel: item.label });
  }

  function confirmLimitOverride() {
    if (!pendingLimitAction) return;
    void saveDraft(pendingLimitAction);
  }

  async function deleteEntry(id: string) {
    setBusyAction(`delete-${id}`);
    setSyncError("");
    try {
      await deleteEntryDocument(id);
      setEntries((current) => current.filter((entry) => entry.id !== id));
      setPendingDeleteId(null);
      setNotice("Entry deleted from Appwrite.");
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
    }
  }

  async function resetAll() {
    setBusyAction("reset");
    setSyncError("");
    try {
      await Promise.all(entries.map((entry) => deleteEntryDocument(entry.id)));
      setEntries([]);
      setFilters(DEFAULT_FILTERS);
      setIsResetOpen(false);
      setNotice("All Appwrite entries deleted.");
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
    } finally {
      setBusyAction(null);
    }
  }

  async function exportExcel() {
    setBusyAction("excel-export");
    setSyncError("");
    try {
      const blob = await createExcelExport(entries);
      downloadBlob(blob, `red-bull-intake-${new Date().toISOString().slice(0, 10)}.xlsx`);
      setNotice("Excel workbook exported.");
    } catch (error) {
      setSyncError(error instanceof Error ? error.message : "Excel export failed.");
    } finally {
      setBusyAction(null);
    }
  }

  async function importExcel(file: File | undefined) {
    if (!file) return;
    setBusyAction("excel-import");
    setSyncError("");
    try {
      const preview = await parseExcelImport(file, entries);
      setImportPreview(preview);
      setNotice(`${preview.rows.length} Excel row${preview.rows.length === 1 ? "" : "s"} parsed for review.`);
    } catch (error) {
      setSyncError(error instanceof Error ? error.message : "Excel import failed.");
    } finally {
      if (excelFileInputRef.current) excelFileInputRef.current.value = "";
      setBusyAction(null);
    }
  }

  async function confirmImportPreview() {
    if (!user || !importPreview) return;
    const drafts = importPreview.rows
      .filter((row) => row.entry && !row.errors.length && !row.duplicate)
      .map((row) => row.entry as EntryDraft);

    if (!drafts.length) {
      setNotice("No valid new rows to import.");
      return;
    }

    setBusyAction("confirm-import");
    setSyncError("");
    importInFlightRef.current = true;
    setImportProgress({ done: 0, total: drafts.length });
    try {
      const saved = await createEntries(user.$id, drafts, (done, total) => setImportProgress({ done, total }));
      setEntries((current) => sortEntries([...saved, ...current]));
      setImportPreview(null);
      setNotice(`${saved.length} row${saved.length === 1 ? "" : "s"} saved to Appwrite.`);
    } catch (error) {
      setSyncError(appwriteErrorMessage(error));
    } finally {
      importInFlightRef.current = false;
      setImportProgress(null);
      setBusyAction(null);
    }
  }

  function exportJson() {
    const blob = new Blob([exportPayload(entries)], { type: "application/json" });
    downloadBlob(blob, `red-bull-intake-${new Date().toISOString().slice(0, 10)}.json`);
    setNotice("JSON backup exported.");
  }

  async function importJson(file: File | undefined) {
    if (!file || !user) return;
    setBusyAction("json-import");
    setSyncError("");
    try {
      const drafts = parseImport(await file.text());
      const preview = buildJsonImportPreview(drafts, entries, file.name);
      setImportPreview(preview);
      setNotice(`${preview.rows.length} JSON row${preview.rows.length === 1 ? "" : "s"} parsed for review.`);
    } catch (error) {
      setSyncError(error instanceof Error ? error.message : "JSON import failed.");
    } finally {
      if (jsonFileInputRef.current) jsonFileInputRef.current.value = "";
      setBusyAction(null);
    }
  }

  if (authLoading) {
    return <LoadingScreen setupStatus={setupStatus} shellStyle={shellStyle} themeId={themeId} resolvedMode={resolvedMode} />;
  }

  if (!user) {
    return (
      <AuthView
        authError={authError}
        busy={busyAction === "auth"}
        setupStatus={setupStatus}
        shellStyle={shellStyle}
        themeId={themeId}
        resolvedMode={resolvedMode}
        onLogin={login}
        onSignup={signup}
      />
    );
  }

  return (
    <div
      className="app-shell min-h-screen overflow-x-hidden"
      data-theme={themeId}
      data-mode={resolvedMode}
      style={shellStyle}
    >
      {setupOpen && user && (
        <OnboardingScreen
          userName={user.name || undefined}
          activeThemeId={themeId}
          onThemeChange={setThemeId}
          onSave={saveOnboarding}
          onClose={() => setSetupOpen(false)}
          initialLimits={userLimits}
        />
      )}
      <input
        ref={excelFileInputRef}
        className="hidden"
        type="file"
        accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        onChange={(event) => void importExcel(event.currentTarget.files?.[0])}
      />
      <input
        ref={jsonFileInputRef}
        className="hidden"
        type="file"
        accept="application/json"
        onChange={(event) => void importJson(event.currentTarget.files?.[0])}
      />

      <ShellBackdrop />

      <div className="app-layout">
        <Sidebar
          activeView={activeView}
          dataLoading={dataLoading}
          setupStatus={setupStatus}
          user={user}
          onAdd={openNewEntry}
          onScan={openBarcodeScanner}
          onChange={setActiveView}
          onOpenSettings={() => setActiveView("settings")}
        />

        <div className="app-content">
          <TopBar
            activeView={activeView}
            busyAction={busyAction}
            onAdd={openNewEntry}
            onScan={openBarcodeScanner}
            className={activeView === "overview" ? "top-app-bar--overview" : ""}
          />

          <StatusRail busyAction={busyAction} setupStatus={setupStatus} />

          <AnimatePresence mode="wait">
            <motion.main
              key={activeView}
              initial={{ opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -8 }}
              transition={VIEW_TRANSITION}
              className="app-view"
            >
              {activeView === "overview" && (
                <OverviewView
                  summary={summary}
                  entries={entries}
                  insights={insights}
                  quickAdds={QUICK_ADDS}
                  recentEntries={recentEntries}
                  chartData={chartData}
                  flavourData={flavourData}
                  user={user}
                  userLimits={userLimits}
                  limitCheck={limitCheck}
                  loading={dataLoading}
                  onQuickAdd={(item) => void quickAdd(item)}
                  onAdd={openNewEntry}
                  onScan={openBarcodeScanner}
                  onOpenLogbook={() => setActiveView("logbook")}
                  onOpenSettings={() => setActiveView("settings")}
                />
              )}

              {activeView === "logbook" && (
                <LogbookView
                  entries={entriesInView}
                  totalEntries={entries.length}
                  entryDerived={entryDerived}
                  filters={filters}
                  flavours={allFlavours}
                  loading={dataLoading}
                  deletingId={deletingId}
                  onFilterChange={setFilters}
                  onAdd={openNewEntry}
                  onEdit={(entry) => {
                    setEditingEntry(entry);
                    setIsEntryModalOpen(true);
                  }}
                  onDelete={(id) => setPendingDeleteId(id)}
                  onLogAgain={logEntryAgain}
                />
              )}

              {activeView === "trends" && (
                <TrendsView
                  chartData={chartData}
                  weekData={weekData}
                  flavourData={flavourData}
                  insights={insights}
                  entries={entriesInView}
                  filters={filters}
                  flavours={allFlavours}
                  userLimits={userLimits}
                  loading={dataLoading}
                  onFilterChange={setFilters}
                  onSaveLimits={(next) => void saveUserLimits(next)}
                />
              )}

              {activeView === "settings" && (
                <SettingsView
                  activeTheme={activeTheme}
                  summary={summary}
                  dataLoading={dataLoading}
                  entries={entries}
                  setupStatus={setupStatus}
                  themeId={themeId}
                  themeMode={themeMode}
                  user={user}
                  userLimits={userLimits}
                  limitCheck={limitCheck}
                  busyAction={busyAction}
                  onExportExcel={() => void exportExcel()}
                  onImportExcel={() => excelFileInputRef.current?.click()}
                  onExportJson={exportJson}
                  onImportJson={() => jsonFileInputRef.current?.click()}
                  onLogout={() => void logout()}
                  onReset={() => setIsResetOpen(true)}
                  onThemeChange={setThemeId}
                  onThemeModeChange={setThemeMode}
                  onSaveLimits={(next) => void saveUserLimits(next)}
                  onRerunOnboarding={() => setSetupOpen(true)}
                />
              )}
            </motion.main>
          </AnimatePresence>
        </div>
      </div>

      <MobileNav activeView={activeView} onChange={setActiveView} />

      {activeView === "overview" && (
        <button className="fab-add" type="button" onClick={openNewEntry} aria-label="Add intake">
          <Plus size={26} aria-hidden="true" />
        </button>
      )}

      <EntryModal
        entry={editingEntry}
        initialDraft={entryInitialDraft}
        flavours={allFlavours}
        open={isEntryModalOpen}
        saving={busyAction === "save-entry"}
        onClose={() => {
          setIsEntryModalOpen(false);
          setEditingEntry(null);
          setEntryInitialDraft(null);
        }}
        onSave={(draft) => void saveEntryDraft(draft)}
      />

      <BarcodeScannerModal
        busy={busyAction === "save-entry"}
        flavours={allFlavours}
        open={isBarcodeScannerOpen}
        userId={user.$id}
        onAddNow={addBarcodeDraft}
        onClose={() => setIsBarcodeScannerOpen(false)}
        onEditBeforeAdding={editBarcodeDraft}
      />

      <ImportPreviewModal
        busy={busyAction === "confirm-import"}
        preview={importPreview}
        importProgress={importProgress}
        onClose={() => setImportPreview(null)}
        onConfirm={() => void confirmImportPreview()}
      />

      <ConfirmDialog
        busy={busyAction === "reset"}
        open={isResetOpen}
        title="Delete all Appwrite entries?"
        body="This removes every intake entry owned by your current Appwrite user. Export first if you want a backup."
        confirmLabel="Delete all"
        onCancel={() => setIsResetOpen(false)}
        onConfirm={() => void resetAll()}
      />

      <ConfirmDialog
        busy={pendingDeleteId !== null && busyAction === `delete-${pendingDeleteId}`}
        open={pendingDeleteId !== null}
        title="Delete this entry?"
        body={
          pendingDeleteEntry
            ? `${pendingDeleteEntry.flavour} · ${pendingDeleteEntry.cans} can${pendingDeleteEntry.cans === 1 ? "" : "s"} · ${pendingDeleteEntry.sizeMl}ml · ${humanDateTime(pendingDeleteEntry.dateTime)}`
            : "This entry will be permanently removed."
        }
        confirmLabel="Delete"
        onCancel={() => setPendingDeleteId(null)}
        onConfirm={() => {
          if (pendingDeleteId) void deleteEntry(pendingDeleteId);
        }}
      />

      <ConfirmDialog
        busy={Boolean(busyAction && pendingLimitAction)}
        open={limitConfirmOpen}
        title="Over your limit?"
        body={limitConfirmMessage || "This intake goes past one of your daily limits."}
        confirmLabel="Log anyway"
        tone="warning"
        onCancel={() => {
          setLimitConfirmOpen(false);
          setPendingLimitAction(null);
          setLimitConfirmMessage("");
        }}
        onConfirm={confirmLimitOverride}
      />
    </div>
  );
}

function sortEntries(entries: RedBullEntry[]) {
  return [...entries].sort((left, right) => new Date(right.dateTime).getTime() - new Date(left.dateTime).getTime());
}

export default App;
