// src/App.tsx
import React, { useState, useCallback, useEffect } from 'react';
import { Routes, Route, useParams, useNavigate } from 'react-router-dom'; 
import { Toaster } from 'react-hot-toast';
import { FlyerDisplay } from './features/flyer/FlyerDisplay';
import { ExtractedDataTable } from './features/flyer/ExtractedDataTable';
import { AnalysisPanel } from './features/flyer/AnalysisPanel';
import { PriceChart } from './features/charts/PriceChart';
import * as pdfjsLib from 'pdfjs-dist';
import { ErrorDisplay } from './components/ErrorDisplay';
import { Header } from './components/Header';
import { logger } from './services/logger'; 
import * as aiApiClient from './services/aiApiClient';
import type { FlyerItem, Flyer, MasterGroceryItem, DealItem, ProcessingStage, StageStatus, Profile, ShoppingList, ShoppingListItem, User, UserProfile } from './types';
import { BulkImporter } from './features/flyer/BulkImporter';
import { PriceHistoryChart } from './features/charts/PriceHistoryChart';
import * as apiClient from './services/apiClient'; 
import { FlyerList } from './features/flyer/FlyerList';
import { recordProcessingTime, getAverageProcessingTime } from './utils/processingTimer';
import { ProfileManager } from './pages/admin/components/ProfileManager';
import { ShoppingListComponent } from './features/shopping/ShoppingList';
import { FlyerUploader } from './features/flyer/FlyerUploader';
import { VoiceAssistant } from './features/voice-assistant/VoiceAssistant';
import { AdminPage } from './pages/admin/AdminPage';
import { AdminRoute } from './components/AdminRoute';
import { CorrectionsPage } from './pages/admin/CorrectionsPage';
import { ActivityLog, ActivityLogClickHandler } from './pages/admin/ActivityLog';
import { WatchedItemsList } from './features/shopping/WatchedItemsList';
import { AdminStatsPage } from './pages/admin/AdminStatsPage';
import { ResetPasswordPage } from './pages/ResetPasswordPage';
import { AnonymousUserBanner } from './pages/admin/components/AnonymousUserBanner';
import { VoiceLabPage } from './pages/VoiceLabPage';
import { WhatsNewModal } from './components/WhatsNewModal';
import { FlyerCorrectionTool } from './components/FlyerCorrectionTool';
import { QuestionMarkCircleIcon } from './components/icons/QuestionMarkCircleIcon';
import Leaderboard from './components/Leaderboard';

/**
 * Defines the possible authentication states for a user session.
 * - `SIGNED_OUT`: No user is active. The session is fresh or the user has explicitly signed out.
 * - `ANONYMOUS`: The user has started interacting with the app (e.g., by uploading a flyer)
 *   but has not logged in or created an account. This state allows for temporary,
 *   session-based data access without full authentication. This is a planned feature.
 * - `AUTHENTICATED`: The user has successfully logged in, and their identity is confirmed
 *   via a valid JWT.
 */
type AuthStatus = 'SIGNED_OUT' | 'ANONYMOUS' | 'AUTHENTICATED';

// pdf.js worker configuration
// This is crucial for allowing pdf.js to process PDFs in a separate thread, preventing the UI from freezing.
// We need to explicitly tell pdf.js where to load its worker script from.
// By importing pdfjs-dist, we can host the worker locally, which is more reliable.
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.mjs', import.meta.url).toString();

function App() {
  const [user, setUser] = useState<User | null>(null); // Moved user state to the top

    // --- Data Fetching ---
  const [flyers, setFlyers] = useState<Flyer[]>([]);
  const [masterItems, setMasterItems] = useState<MasterGroceryItem[]>([]);
  const [watchedItems, setWatchedItems] = useState<MasterGroceryItem[]>([]);
  const [shoppingLists, setShoppingLists] = useState<ShoppingList[]>([]); // This was a duplicate, fixed.

  const refetchFlyers = useCallback(async () => {
    try {
      const flyersRes = await apiClient.fetchFlyers();
      setFlyers(await flyersRes.json());
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    }
  }, []);

  useEffect(() => {
    const fetchData = async () => {
        const [masterItemsRes, watchedItemsRes, shoppingListsRes] = await Promise.all([
            apiClient.fetchMasterItems(),
            user ? apiClient.fetchWatchedItems() : Promise.resolve(new Response(JSON.stringify([]))),
            user ? apiClient.fetchShoppingLists() : Promise.resolve(new Response(JSON.stringify([]))),
        ]);
        setMasterItems(await masterItemsRes.json());
        setWatchedItems(await watchedItemsRes.json());
        setShoppingLists(await shoppingListsRes.json());
    };
    refetchFlyers(); // Initial fetch
    fetchData();
  }, [user, refetchFlyers]);

  const [selectedFlyer, setSelectedFlyer] = useState<Flyer | null>(null);
  const [flyerItems, setFlyerItems] = useState<FlyerItem[]>([]);
  // Local state for watched items and shopping lists to allow for optimistic updates
  const [localWatchedItems, setLocalWatchedItems] = useState<MasterGroceryItem[]>([]);
  const [localShoppingLists, setLocalShoppingLists] = useState<ShoppingList[]>([]);

  const [activeDeals, setActiveDeals] = useState<DealItem[]>([]);
  const [activeDealsLoading, setActiveDealsLoading] = useState(false);
  const [totalActiveItems, setTotalActiveItems] = useState(0);
  const [error, setError] = useState<string | null>(null);

  const [importSummary, setImportSummary] = useState<{
    processed: string[];
    skipped: string[];
    errors: { fileName: string; message: string }[];
  } | null>(null);

  const [isDarkMode, setIsDarkMode] = useState(false);
  const [unitSystem, setUnitSystem] = useState<'metric' | 'imperial'>('imperial');
  const [profile, setProfile] = useState<Profile | null>(null);
  const [authStatus, setAuthStatus] = useState<AuthStatus>('SIGNED_OUT');
  const [isProfileManagerOpen, setIsProfileManagerOpen] = useState(false); // This will now control the login modal as well
  const [isWhatsNewOpen, setIsWhatsNewOpen] = useState(false);
  const [isVoiceAssistantOpen, setIsVoiceAssistantOpen] = useState(false);
  const [isCorrectionToolOpen, setIsCorrectionToolOpen] = useState(false);

  const handleDataExtractedFromCorrection = (type: 'store_name' | 'dates', value: string) => {
    if (!selectedFlyer) return;

    // This is a simplified update. A real implementation would involve
    // making another API call to update the flyer record in the database.
    // For now, we just update the local state for immediate visual feedback.
    const updatedFlyer = { ...selectedFlyer };
    if (type === 'store_name') {
      updatedFlyer.store = { ...updatedFlyer.store!, name: value };
    } else if (type === 'dates') {
      // A more robust solution would parse the date string properly.
    }
    setSelectedFlyer(updatedFlyer);
  };

  const [estimatedTime, setEstimatedTime] = useState(0);
  
  const [activeListId, setActiveListId] = useState<number | null>(null);

  // --- State Synchronization and Error Handling ---

  useEffect(() => {
    setLocalWatchedItems(watchedItems);
  }, [watchedItems]);

  useEffect(() => {
    setLocalShoppingLists(shoppingLists);
  }, [shoppingLists]);

  // Effect to set initial theme based on user profile, local storage, or system preference
  useEffect(() => {
    if (profile && profile.preferences?.darkMode !== undefined) {
        // Preference from DB
        const dbDarkMode = profile.preferences.darkMode;
        setIsDarkMode(dbDarkMode);
        document.documentElement.classList.toggle('dark', dbDarkMode);
    } else {
        // Fallback to local storage or system preference
        const savedMode = localStorage.getItem('darkMode');
        const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
        const initialDarkMode = savedMode !== null ? savedMode === 'true' : prefersDark;
        setIsDarkMode(initialDarkMode);
        document.documentElement.classList.toggle('dark', initialDarkMode);
    }
  }, [profile]);

  // Effect to set initial unit system based on user profile or local storage
  useEffect(() => {
    if (profile && profile.preferences?.unitSystem) {
        setUnitSystem(profile.preferences.unitSystem);
    } else {
        const savedSystem = localStorage.getItem('unitSystem') as 'metric' | 'imperial' | null;
        if (savedSystem) {
            setUnitSystem(savedSystem);
        }
    }
  }, [profile]);

  // This is the login handler that will be passed to the ProfileManager component.
  const handleLoginSuccess = async (loggedInUser: User, token: string) => {
    setError(null);
    // Immediately store the token so subsequent API calls in this function are authenticated.
    localStorage.setItem('authToken', token);

    try {
      // Fetch all essential user data *before* setting the final authenticated state.
      // This ensures the app doesn't enter an inconsistent state if one of these calls fails.
      const [profileResponse, watchedResponse] = await Promise.all([
        apiClient.getAuthenticatedUserProfile(),
        apiClient.fetchWatchedItems(), // We still fetch here to get immediate data after login
      ]);

      const userProfile = await profileResponse.json();
      const watchedData = await watchedResponse.json();

      // Now that all data is successfully fetched, update the application state.
      setUser(loggedInUser); // Set user first
      setProfile(userProfile);
      setAuthStatus('AUTHENTICATED');
      setLocalWatchedItems(watchedData);

      // The fetchShoppingLists function will be triggered by the useEffect below
      // now that the user state has been set.

      logger.info('Login and data fetch successful', { user: loggedInUser });
    } catch (e) {
      const errorMessage = e instanceof Error ? e.message : String(e);
      logger.error('Failed to fetch user data after login. Rolling back.', { error: errorMessage });
      setError(`Login succeeded, but failed to fetch your data: ${errorMessage}`);
      handleSignOut(); // Log the user out to prevent an inconsistent state.
    }
  };

  // Effect to check for an existing token on initial app load.
  useEffect(() => {
    const checkAuthToken = async () => {
      const token = localStorage.getItem('authToken');
      if (token) {
        logger.info('Found auth token in local storage. Validating...');
        try {
          const response = await apiClient.getAuthenticatedUserProfile();
          const userProfile = await response.json();
          // The user object is nested within the UserProfile object.
          setUser(userProfile.user);
          setProfile(userProfile);
          setAuthStatus('AUTHENTICATED');
          logger.info('Token validated successfully.', { user: userProfile.user });
        } catch (e) {
          logger.warn('Auth token validation failed. Clearing token.', { error: e });
          localStorage.removeItem('authToken');
          setUser(null);
          setAuthStatus('SIGNED_OUT');
        }
      } else {
        logger.info('No auth token found. User is signed out.');
        setAuthStatus('SIGNED_OUT');
      }
    };
    checkAuthToken();
  }, []); // Runs only once on mount. Intentionally empty.

  // Effect to handle the token from Google OAuth redirect
  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const googleToken = urlParams.get('googleAuthToken');

    if (googleToken) {
      logger.info('Received Google Auth token from URL. Authenticating...');
      // The token is already a valid access token from our server.
      // We can use it to fetch the user profile and complete the login flow.
      localStorage.setItem('authToken', googleToken);
      apiClient.getAuthenticatedUserProfile().then(response => response.json())
        .then((userProfile: UserProfile) => {
          handleLoginSuccess(userProfile.user, googleToken);
        })
        .catch(err => logger.error('Failed to log in with Google token', { error: err }));
      
      // Clean the token from the URL
      window.history.replaceState({}, document.title, "/");
    }

    const githubToken = urlParams.get('githubAuthToken');
    if (githubToken) {
      logger.info('Received GitHub Auth token from URL. Authenticating...');
      // The token is already a valid access token from our server.
      // We can use it to fetch the user profile and complete the login flow.
      localStorage.setItem('authToken', githubToken);
      apiClient.getAuthenticatedUserProfile().then(response => response.json()) // This returns a UserProfile
        .then((userProfile: UserProfile) => {
          handleLoginSuccess(userProfile.user, githubToken);
        })
        .catch(err => {
          logger.error('Failed to log in with GitHub token', { error: err });
          // Optionally, redirect to a page with an error message
          // navigate('/login?error=github_auth_failed');
        });
      
      // Clean the token from the URL
      window.history.replaceState({}, document.title, "/");
    }
  }, [handleLoginSuccess]);


  const resetState = useCallback(() => {
    setSelectedFlyer(null);
    setFlyerItems([]);
  }, []);

  const handleFlyerSelect = useCallback(async (flyer: Flyer) => {
    setSelectedFlyer(flyer);
    setError(null);
    setFlyerItems([]); // Clear previous items

    try {
        const response = await apiClient.fetchFlyerItems(flyer.flyer_id);
        const items = await response.json();
        setFlyerItems(items);
    } catch (e) {
        const errorMessage = e instanceof Error ? e.message : String(e);
        setError(errorMessage);
    }
  }, []);

  useEffect(() => {
    if (!selectedFlyer && flyers.length > 0) {
      handleFlyerSelect(flyers[0]);
    }
  }, [flyers, selectedFlyer, handleFlyerSelect]);

  // New effect to handle routing to a specific flyer ID from the URL
  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const flyerIdFromUrl = urlParams.get('flyerId'); // Or parse from path if using /flyers/:id

    if (flyerIdFromUrl && flyers.length > 0) {
      const flyerId = parseInt(flyerIdFromUrl, 10);
      const flyerToSelect = flyers.find(f => f.flyer_id === flyerId);
      if (flyerToSelect && flyerToSelect.flyer_id !== selectedFlyer?.flyer_id) {
        handleFlyerSelect(flyerToSelect);
      }
    }
  }, [flyers, handleFlyerSelect, selectedFlyer]);


  useEffect(() => {
    const findActiveDeals = async () => {
      if (flyers.length === 0 || localWatchedItems.length === 0) {
        setActiveDeals([]);
        return;
      }
      
      setActiveDealsLoading(true);

      try {
        const today = new Date();
        today.setHours(0, 0, 0, 0);

        const validFlyers = flyers.filter((flyer) => {
            if (!flyer.valid_from || !flyer.valid_to) return false;
            try {
                const from = new Date(`${flyer.valid_from}T00:00:00`);
                const to = new Date(`${flyer.valid_to}T00:00:00`);
                return today >= from && today <= to;
            } catch (e) {
                logger.error("Error parsing flyer date", { error: e });
                return false;
            }
        });

        if (validFlyers.length === 0) {
          setActiveDeals([]);
          return;
        }

        const validFlyerIds = validFlyers.map(f => f.flyer_id);
        const response = await apiClient.fetchFlyerItemsForFlyers(validFlyerIds);
        const allItems: FlyerItem[] = await response.json();

        const watchedItemIds = new Set(localWatchedItems.map((item: MasterGroceryItem) => item.master_grocery_item_id));
        const dealItemsRaw = allItems.filter(item =>
            item.master_item_id && watchedItemIds.has(item.master_item_id)
        ); // This seems correct as it's comparing with master_item_id

        const flyerIdToStoreName = new Map(validFlyers.map((f: Flyer) => [f.flyer_id, f.store?.name || 'Unknown Store']));
        
        const deals: DealItem[] = dealItemsRaw.map(item => ({
            item: item.item,
            price_display: item.price_display,
            price_in_cents: item.price_in_cents,
            quantity: item.quantity,
            storeName: (flyerIdToStoreName.get(item.flyer_id!) || 'Unknown Store') as string,
            master_item_name: item.master_item_name,
            unit_price: item.unit_price,
        }));

        setActiveDeals(deals);
      } catch (e) {
          const errorMessage = e instanceof Error ? e.message : String(e);
          setError(`Could not fetch active deals: ${errorMessage}`);
      } finally {
        setActiveDealsLoading(false);
      }
    };

    findActiveDeals();
  }, [flyers, localWatchedItems]);

  useEffect(() => {
    const calculateTotalActiveItems = async () => {
        if (flyers.length === 0) {
            setTotalActiveItems(0);
            return;
        }

        try {
            const today = new Date();
            today.setHours(0, 0, 0, 0);

            const validFlyers = flyers.filter((flyer) => {
                if (!flyer.valid_from || !flyer.valid_to) return false;
                try {
                    const from = new Date(`${flyer.valid_from}T00:00:00`);
                    const to = new Date(`${flyer.valid_to}T00:00:00`);
                    return today >= from && today <= to;
                } catch (e) {
                    logger.error("Error parsing flyer date", { error: e });
                    return false;
                }
            });

            if (validFlyers.length === 0) {
                setTotalActiveItems(0);
                return;
            }

            const validFlyerIds = validFlyers.map(f => f.flyer_id);
            const response = await apiClient.countFlyerItemsForFlyers(validFlyerIds);
            const { count: totalCount } = await response.json();
            setTotalActiveItems(totalCount);
        } catch (e) {
            const errorMessage = e instanceof Error ? e.message : String(e);
            logger.error("Failed to calculate total active items:", { error: errorMessage });
            setTotalActiveItems(0);
        }
    };

    calculateTotalActiveItems();
  }, [flyers]);

  const handleAddWatchedItem = useCallback(async (itemName: string, category: string) => {
    if (!user) return;
    try {
        const updatedOrNewItem = await (await apiClient.addWatchedItem(itemName, category)).json();
        setLocalWatchedItems((prevItems: MasterGroceryItem[]) => {
            // Check if the item already exists in the state by its correct ID property.
            const itemExists = prevItems.some((item: MasterGroceryItem) => item.master_grocery_item_id === updatedOrNewItem.master_grocery_item_id);
            if (!itemExists) {
                const newItems = [...prevItems, updatedOrNewItem]; // This was correct, but the check above was wrong.
                return newItems.sort((a,b) => a.name.localeCompare(b.name));
            }
            return prevItems; // Item already existed in list
        });
    } catch (e) {
        const errorMessage = e instanceof Error ? e.message : String(e);
        setError(`Could not add watched item: ${errorMessage}`);
        // Re-fetch to sync state on error
    }
  }, [user]);
  
  const handleRemoveWatchedItem = useCallback(async (masterItemId: number) => {
    if (!user) return;
    try {
      const response = await apiClient.removeWatchedItem(masterItemId); // API call is correct
      if (!response.ok) throw new Error('Failed to remove item');
      setLocalWatchedItems(prevItems => prevItems.filter((item: MasterGroceryItem) => item.master_grocery_item_id !== masterItemId)); // State update must use correct property
    } catch (e) {
      const errorMessage = e instanceof Error ? e.message : String(e);
      setError(`Could not remove watched item: ${errorMessage}`);
    }
  }, [user]);

  // --- Shopping List Handlers ---
  const handleCreateList = useCallback(async (name: string) => {
    if (!user) return;
    try {
      const response = await apiClient.createShoppingList(name);
      const newList = await response.json();
      setShoppingLists(prev => [...prev, newList]);
      setActiveListId(newList.shopping_list_id);
    } catch (e) {
      const errorMessage = e instanceof Error ? e.message : String(e);
      setError(`Could not create list: ${errorMessage}`);
    }
  }, [user]); // Changed dependency from `session` to `user`
  const handleDeleteList = useCallback(async (listId: number) => {
    if (!user) return;
    try {
      const response = await apiClient.deleteShoppingList(listId);
      if (!response.ok) throw new Error('Failed to delete list');
      const newLists = localShoppingLists.filter(l => l.shopping_list_id !== listId);
      setLocalShoppingLists(newLists);
      if (activeListId === listId) {
        setActiveListId(newLists.length > 0 ? newLists[0].shopping_list_id : null);
      }
    } catch (e) {
      const errorMessage = e instanceof Error ? e.message : String(e);
      setError(`Could not delete list: ${errorMessage}`);
    }
  }, [user, localShoppingLists, activeListId]);

  const handleAddShoppingListItem = useCallback(async (listId: number, item: { masterItemId?: number, customItemName?: string }) => {
    if (!user) return;
    try {
        const response = await apiClient.addShoppingListItem(listId, item);
        const newItem = await response.json();
        setShoppingLists(prevLists => prevLists.map(list => {
            if (list.shopping_list_id === listId) {
                // Avoid adding duplicates to the state if it's already there
                // Check if the item already exists in the list by its correct ID property.
                const itemExists = list.items.some(i => i.shopping_list_item_id === newItem.shopping_list_item_id);
                if (itemExists) return list;
                return { ...list, items: [...list.items, newItem] };
            }
            return list;
        }));
    } catch (e) {
        const errorMessage = e instanceof Error ? e.message : String(e);
        setError(`Could not add item to list: ${errorMessage}`);
    }
  }, [user, activeListId]); // Added activeListId to dependencies

  const handleUpdateShoppingListItem = useCallback(async (itemId: number, updates: Partial<ShoppingListItem>) => {
    if (!user || !activeListId) return;
    try {
        const response = await apiClient.updateShoppingListItem(itemId, updates);
        const updatedItem = await response.json();
        setShoppingLists(prevLists => prevLists.map(list => {
            if (list.shopping_list_id === activeListId) {
                return { ...list, items: list.items.map(i => i.shopping_list_item_id === itemId ? updatedItem : i) };
            }
            return list;
        }));
    } catch (e) {
        const errorMessage = e instanceof Error ? e.message : String(e);
        setError(`Could not update list item: ${errorMessage}`);
    }
  }, [user, activeListId]); // Changed dependency from `session` to `user`

  const handleRemoveShoppingListItem = useCallback(async (itemId: number) => {
    if (!user || !activeListId) return;
    try {
        const response = await apiClient.removeShoppingListItem(itemId);
        if (!response.ok) throw new Error('Failed to remove item');
        setLocalShoppingLists(prevLists => prevLists.map(list => {
            if (list.shopping_list_id === activeListId) {
                return { ...list, items: list.items.filter(i => i.shopping_list_item_id !== itemId) };
            }
            return list;
        }));
    } catch (e) {
        const errorMessage = e instanceof Error ? e.message : String(e);
        setError(`Could not remove list item: ${errorMessage}`);
    }
  }, [user, activeListId]); // Changed dependency from `session` to `user`

  const handleSignOut = () => {
    localStorage.removeItem('authToken'); // Remove the JWT token
    setUser(null); // Clear the user state
    setProfile(null); // Clear the profile state
    setAuthStatus('SIGNED_OUT'); // Update auth status
  };

  const handleActivityLogClick: ActivityLogClickHandler = (log) => {
    // Thanks to the discriminated union, if the action is 'list_shared', TypeScript knows 'details.shopping_list_id' is a number.
    if (log.action === 'list_shared') {
      const listId = log.details.shopping_list_id;
      if (localShoppingLists.some(list => list.shopping_list_id === listId)) {
        setActiveListId(listId);
      }
    }
    // Future functionality for other clickable log types can be added here.
    // For example, clicking a recipe could open a recipe detail modal.
  };


  const hasData = flyerItems.length > 0;

    // Read the application version injected at build time.
  // This will only be available in the production build, not during local development.
  const appVersion = import.meta.env.VITE_APP_VERSION;
  const commitMessage = import.meta.env.VITE_APP_COMMIT_MESSAGE;
  const commitUrl = import.meta.env.VITE_APP_COMMIT_URL;
  useEffect(() => {
    if (appVersion) {
      logger.info(`Application version: ${appVersion}`);
      const lastSeenVersion = localStorage.getItem('lastSeenVersion');
      // If the current version is new, show the "What's New" modal.
      if (appVersion !== lastSeenVersion) {
        setIsWhatsNewOpen(true);
        localStorage.setItem('lastSeenVersion', appVersion);
      }
    }
  }, [appVersion]);

  return (
    <div className="bg-gray-100 dark:bg-gray-950 min-h-screen font-sans text-gray-800 dark:text-gray-200">
      {/* Toaster component for displaying notifications. It's placed at the top level. */}
      <Toaster position="top-center" reverseOrder={false} />
      {/* Add CSS variables for toast theming based on dark mode */}
      <style>{`
        :root {
          --toast-bg: ${isDarkMode ? '#4B5563' : '#FFFFFF'};
          --toast-color: ${isDarkMode ? '#F9FAFB' : '#1F2937'};
        }
      `}</style>


      <Header 
        isDarkMode={isDarkMode} // Still pass for display, but toggling happens in ProfileManager
        // toggleDarkMode removed
        unitSystem={unitSystem} // Still pass for display
        // toggleUnitSystem removed
        profile={profile}
        authStatus={authStatus}
        user={user}
        onOpenProfile={() => setIsProfileManagerOpen(true)}
        onOpenVoiceAssistant={() => setIsVoiceAssistantOpen(true)}
        onSignOut={handleSignOut}
      />

      {/* The ProfileManager is now always available to be opened, handling both login and profile management. */}
      {isProfileManagerOpen && (
        <ProfileManager
            isOpen={isProfileManagerOpen}
            onClose={() => setIsProfileManagerOpen(false)}
            user={user}
            authStatus={authStatus}
            profile={profile}
            onProfileUpdate={setProfile}
            onLoginSuccess={handleLoginSuccess}
            onSignOut={handleSignOut} // Pass the signOut handler
        />
      )}
      {user && (
          <VoiceAssistant 
              isOpen={isVoiceAssistantOpen}
              onClose={() => setIsVoiceAssistantOpen(false)}
          />
      )}

      {/* "What's New" modal, shown automatically on new versions */}
      {appVersion && commitMessage && (
        <WhatsNewModal
          isOpen={isWhatsNewOpen}
          onClose={() => setIsWhatsNewOpen(false)}
          version={appVersion}
          commitMessage={commitMessage}
        />
      )}

      {selectedFlyer && (
        <FlyerCorrectionTool
          isOpen={isCorrectionToolOpen}
          onClose={() => setIsCorrectionToolOpen(false)}
          imageUrl={selectedFlyer.image_url}
          onDataExtracted={handleDataExtractedFromCorrection}
        />
      )}

      <Routes>
        <Route path="/flyers/:flyerId" element={<HomePage />} />
        <Route path="/" element={
          <main className="max-w-screen-2xl mx-auto py-4 px-2.5 sm:py-6 lg:py-8">
            {/* This banner will only appear for users who have interacted with the app but are not logged in. */}
            {authStatus === 'ANONYMOUS' && (
              <div className="max-w-5xl mx-auto mb-6 px-4 lg:px-0">
                <AnonymousUserBanner onOpenProfile={() => setIsProfileManagerOpen(true)} />
              </div>
            )}
            <div className="grid grid-cols-1 lg:grid-cols-4 gap-8 items-start">
              
              <div className="lg:col-span-1 flex flex-col space-y-6">
                <FlyerList flyers={flyers} onFlyerSelect={handleFlyerSelect} selectedFlyerId={selectedFlyer?.flyer_id || null} />
                <FlyerUploader onProcessingComplete={refetchFlyers} />
              </div>
    
              <div className="lg:col-span-2 flex flex-col space-y-6">
                {error && <ErrorDisplay message={error} />} {/* This was a duplicate, fixed. */}
                
                {selectedFlyer ? (
                  <>
                    <FlyerDisplay
                      imageUrl={selectedFlyer.image_url}
                      store={selectedFlyer.store}
                      validFrom={selectedFlyer.valid_from}
                      validTo={selectedFlyer.valid_to}
                      storeAddress={selectedFlyer.store_address}
                      onOpenCorrectionTool={() => setIsCorrectionToolOpen(true)}
                    />
                    {hasData && (
                      <>
                        <ExtractedDataTable 
                          items={flyerItems} 
                          totalActiveItems={totalActiveItems}
                          watchedItems={localWatchedItems}
                          masterItems={masterItems}
                          unitSystem={unitSystem}
                          user={user}
                          onAddItem={handleAddWatchedItem}
                          shoppingLists={localShoppingLists}
                          activeListId={activeListId}
                          onAddItemToList={(masterItemId: number) => handleAddShoppingListItem(activeListId!, { masterItemId })}                        />
                        <AnalysisPanel flyerItems={flyerItems} store={selectedFlyer.store} />
                      </>
                    )}
                  </>
                ) : (
                  <div className="text-center p-8 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 h-full flex flex-col justify-center min-h-[400px]">
                    <h2 className="text-xl font-semibold text-gray-700 dark:text-gray-200">Welcome to Flyer Crawler!</h2>
                    <p className="mt-2 text-gray-500 dark:text-gray-400">Upload a new grocery flyer to begin, or select a previously processed flyer from the list on the left.</p>
                  </div>
                )}
              </div>
              
              <div className="lg:col-span-1 flex-col space-y-6">
                {(
                    <>
                        <ShoppingListComponent 
                            user={user}
                            lists={localShoppingLists}
                            activeListId={activeListId}
                            onSelectList={setActiveListId}
                            onCreateList={handleCreateList}
                            onDeleteList={handleDeleteList} // This was a duplicate, fixed.
                            onAddItem={(item: { masterItemId?: number; customItemName?: string }) => handleAddShoppingListItem(activeListId!, item)}
                            onUpdateItem={handleUpdateShoppingListItem}
                            onRemoveItem={handleRemoveShoppingListItem}
                        />
                        <WatchedItemsList 
                            items={localWatchedItems}
                            onAddItem={handleAddWatchedItem}
                            onRemoveItem={handleRemoveWatchedItem}
                            user={user}
                            activeListId={activeListId}
                            onAddItemToList={(masterItemId) => handleAddShoppingListItem(activeListId!, { masterItemId })}
                        />
                        <PriceChart 
                            deals={activeDeals} 
                            isLoading={activeDealsLoading}
                            unitSystem={unitSystem}
                            user={user}
                        />
                        <PriceHistoryChart watchedItems={localWatchedItems} /> 
                        <Leaderboard />
                        <ActivityLog user={user} onLogClick={handleActivityLogClick} />
                    </>
                )}
              </div>
            </div>
          </main>
        } />
        <Route element={<AdminRoute profile={profile} />}>
          <Route path="/admin" element={<AdminPage />} />
          <Route path="/admin/corrections" element={<CorrectionsPage />} />
          <Route path="/admin/stats" element={<AdminStatsPage />} />
          <Route path="/admin/voice-lab" element={<VoiceLabPage />} />
        </Route>
        <Route path="/reset-password/:token" element={<ResetPasswordPage />} />
      </Routes>

      {/* Display the build version number at the bottom-left of the screen */}
      {appVersion && (
        <div className="fixed bottom-2 left-3 z-50 flex items-center space-x-2">
          <a
            href={commitUrl || '#'}
            target="_blank"
            rel="noopener noreferrer"
            title="View commit details on Gitea"
            className="text-xs text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-950 px-2 py-1 rounded hover:text-brand-primary dark:hover:text-brand-primary transition-colors"
          >
            Version: {appVersion}
          </a>
          <button onClick={() => setIsWhatsNewOpen(true)} title="Show what's new in this version">
            <QuestionMarkCircleIcon className="w-5 h-5 text-gray-400 dark:text-gray-600 hover:text-brand-primary dark:hover:text-brand-primary transition-colors" />
          </button>
        </div>
      )}
    </div>
  );
}

/**
 * A wrapper component to handle the logic for the /flyers/:flyerId route.
 * It extracts the flyerId from the URL and triggers the selection in the parent App component.
 */
const HomePage: React.FC = () => {
  const { flyerId } = useParams<{ flyerId: string }>();
  const navigate = useNavigate();

  useEffect(() => {
    // This component's purpose is to set the selected flyer based on the URL.
    // The actual rendering is handled by the main App component's state.
    // After mounting, we can navigate back to the root path, as the selection
    // will have been triggered by the main App's useEffect hook that watches the URL.
    // This is a common pattern for using URL params to drive state in a parent component.
    if (flyerId) {
      // The main App component will see this URL and select the flyer.
      // We can then navigate to the root to clean up the URL, while the selection remains.
      // A small timeout can ensure the parent component has time to react.
      setTimeout(() => navigate('/', { replace: true }), 100);
    }
  }, [flyerId, navigate]);

  // This component doesn't render anything itself; it's just a controller.
  return null;
};

export default App;