import React, { useCallback, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { sendMessage } from 'webext-bridge/popup';

import { useApp } from '@/entrypoints/popup/context/AppContext';
import { useDb } from '@/entrypoints/popup/context/DbContext';
import { useLoading } from '@/entrypoints/popup/context/LoadingContext';
import useCurrentTabMatching from '@/entrypoints/popup/hooks/useCurrentTabMatching';
import { consumePendingRedirectUrl } from '@/entrypoints/popup/hooks/useVaultLockRedirect';
import { useVaultSync } from '@/entrypoints/popup/hooks/useVaultSync';

import { storage } from '#imports';

const LAST_VISITED_PAGE_KEY = 'session:lastVisitedPage';
const LAST_VISITED_TIME_KEY = 'session:lastVisitedTime';
const NAVIGATION_HISTORY_KEY = 'session:navigationHistory';
const LAST_TAB_URL_KEY = 'session:lastTabUrl';
const PAGE_MEMORY_DURATION = 120 * 1000; // 2 minutes in milliseconds

type NavigationHistoryEntry = {
  pathname: string;
  search: string;
  hash: string;
};

/**
 * Initialize component that handles initial application setup, authentication checks,
 * vault synchronization, and state restoration.
 */
const Reinitialize: React.FC = () => {
  const navigate = useNavigate();
  const { setIsInitialLoading } = useLoading();
  const { syncVault } = useVaultSync();
  const { matchCurrentTab } = useCurrentTabMatching();
  const hasInitialized = useRef(false);

  // Auth and DB state
  const { isInitialized: authInitialized, isLoggedIn } = useApp();
  const { dbInitialized, dbAvailable, refreshSyncState, hasPendingMigrations } = useDb();

  // Derived state
  const isFullyInitialized = authInitialized && dbInitialized;
  const requiresAuth = isFullyInitialized && (!isLoggedIn || !dbAvailable);

  /**
   * Get the expected navigation path based on URL matching result.
   */
  const getMatchedPath = useCallback((matchResult: { items: { Id: string }[]; domain: string } | null): string => {
    if (matchResult && matchResult.items.length === 1) {
      return `/items/${matchResult.items[0].Id}`;
    } else if (matchResult && matchResult.items.length > 1) {
      /*
       * For multiple matches, we navigate to /items with search param,
       * but the saved lastPage won't have the param, so we just check against /items
       */
      return '/items';
    } else {
      return '/items';
    }
  }, []);

  /**
   * Navigate based on URL matching for the current tab.
   */
  const navigateWithUrlMatching = useCallback(async (matchResult: { items: { Id: string }[]; domain: string } | null): Promise<void> => {
    if (matchResult && matchResult.items.length === 1) {
      // Single match - navigate to items first, then to the item (for back button support)
      navigate('/items', { replace: true });
      navigate(`/items/${matchResult.items[0].Id}`, { replace: false });
    } else if (matchResult && matchResult.items.length > 1) {
      // Multiple matches - navigate to items list with domain search to help user find the right one
      navigate(`/items?search=${encodeURIComponent(matchResult.domain)}`, { replace: true });
    } else {
      // No matches or matching failed - navigate to items page without search (don't prefill search when there are no matches)
      navigate('/items', { replace: true });
    }
  }, [navigate]);

  /**
   * Restore the last visited page and navigation history if it was visited within the memory duration.
   * Compares with URL matching result - if user navigated away from matched page, restore their navigation.
   */
  const restoreLastPage = useCallback(async (): Promise<void> => {
    // First, run URL matching to see what we would auto-navigate to
    const matchResult = await matchCurrentTab();
    const matchedPath = getMatchedPath(matchResult);

    const [lastPage, lastVisitTime, savedHistory, lastTabUrl] = await Promise.all([
      storage.getItem(LAST_VISITED_PAGE_KEY) as Promise<string>,
      storage.getItem(LAST_VISITED_TIME_KEY) as Promise<number>,
      storage.getItem(NAVIGATION_HISTORY_KEY) as Promise<NavigationHistoryEntry[]>,
      storage.getItem(LAST_TAB_URL_KEY) as Promise<string>,
    ]);

    // Check if user switched to a different tab (different URL)
    const currentTabUrl = matchResult?.currentUrl;
    const hasTabChanged = currentTabUrl && lastTabUrl && currentTabUrl !== lastTabUrl;

    if (lastPage && lastVisitTime) {
      const timeSinceLastVisit = Date.now() - lastVisitTime;
      if (timeSinceLastVisit <= PAGE_MEMORY_DURATION) {
        /*
         * Check if user navigated away from the auto-matched page to a specific different page.
         * Use fresh URL matching if:
         * - Tab URL has changed (user switched tabs)
         * - lastPage matches what URL matching would show AND has no search query (user stayed on auto-matched page)
         * - lastPage is /items with no search query (default index page - treat as "home" state)
         *
         * Restore user's navigation only if they navigated to a specific different page like:
         * - Settings, add/edit forms, a different item, folder view, search queries, etc.
         */
        // Check if it's the default index page (no search query or other params)
        const lastHistoryEntry = savedHistory?.[savedHistory.length - 1];
        const hasSearchQuery = lastHistoryEntry?.search && lastHistoryEntry.search.length > 0;
        const isOnMatchedPage = lastPage === matchedPath && !hasSearchQuery;
        const isOnDefaultIndexPage = lastPage === '/items' && !hasSearchQuery;
        const shouldUseFreshMatch = hasTabChanged || isOnMatchedPage || isOnDefaultIndexPage;

        if (!shouldUseFreshMatch) {
          // Restore user's navigation since they navigated away from auto-matched page
          if (savedHistory?.length > 1) {
            // Navigate to the base route first
            const firstEntry = savedHistory[0];
            const firstPath = firstEntry.pathname + (firstEntry.search || '');
            navigate(firstPath, { replace: true });
            // Then navigate to the final destination with search params
            const finalPath = lastPage + (lastHistoryEntry?.search || '');
            navigate(finalPath, { replace: false });
          } else {
            // Simple navigation for non-nested routes
            const fullPath = lastPage + (lastHistoryEntry?.search || '');
            navigate(fullPath, { replace: true });
          }
          return;
        }
      }
    }

    // Clear stored navigation data since we're using fresh URL matching
    await Promise.all([
      storage.removeItem(LAST_VISITED_PAGE_KEY),
      storage.removeItem(LAST_VISITED_TIME_KEY),
      storage.removeItem(NAVIGATION_HISTORY_KEY),
      sendMessage('CLEAR_PERSISTED_FORM_VALUES', null, 'background'),
    ]);

    // Save current tab URL for future tab-switch detection
    if (currentTabUrl) {
      await storage.setItem(LAST_TAB_URL_KEY, currentTabUrl);
    }

    // Navigate based on URL matching
    await navigateWithUrlMatching(matchResult);
  }, [navigate, matchCurrentTab, getMatchedPath, navigateWithUrlMatching]);

  useEffect(() => {
    /**
     * Handle initialization and redirect logic
     */
    const handleInitialization = async (): Promise<void> => {
      // Check for inline unlock mode
      const urlParams = new URLSearchParams(window.location.search);
      const inlineUnlock = urlParams.get('mode') === 'inline_unlock';

      // Check for pending redirect URL in storage (set by useVaultLockRedirect hook)
      const pendingRedirectUrl = await consumePendingRedirectUrl();

      if (!isFullyInitialized) {
        return;
      }
      // Prevent multiple vault syncs (only run sync once)
      const shouldRunSync = !hasInitialized.current;

      if (requiresAuth) {
        setIsInitialLoading(false);

        // Determine which auth page to show
        if (!isLoggedIn) {
          navigate('/login', { replace: true });
        } else if (!dbAvailable) {
          navigate('/unlock', { replace: true });
        }
      } else if (shouldRunSync) {
        // Only perform vault sync once during initialization
        hasInitialized.current = true;

        // Check for pending migrations before navigating
        if (await hasPendingMigrations()) {
          setIsInitialLoading(false);
          navigate('/upgrade', { replace: true });
          return;
        }

        /*
         * Navigate immediately using local vault - don't block on sync.
         * This ensures the UI is responsive even if server is slow.
         */
        setIsInitialLoading(false);
        if (inlineUnlock) {
          navigate('/unlock-success', { replace: true });
        } else if (pendingRedirectUrl) {
          navigate(pendingRedirectUrl, { replace: true });
        } else {
          await restoreLastPage();
        }

        /*
         * Run sync in background. If server has newer vault, useVaultSync will:
         * 1. Download and merge (if needed)
         * 2. Call dbContext.loadDatabase() which updates sqliteClient
         * 3. ItemsList reacts to sqliteClient changes and auto-refreshes
         *
         * Note: onSuccess triggers refreshSyncState to ensure any UI components
         * watching sync state will re-render with the updated vault data.
         */
        syncVault({
          /**
           * Handle successful sync - refresh sync state to trigger UI updates.
           * @param _hasNewVault Whether a new vault was downloaded
           */
          onSuccess: async (_hasNewVault) => {
            await refreshSyncState();
          },
          /**
           * Handle upgrade required - redirect to upgrade page.
           */
          onUpgradeRequired: () => {
            navigate('/upgrade', { replace: true });
          },
          /**
           * Handle sync errors silently - user already has local vault.
           * @param error Error message
           */
          onError: (error) => {
            console.error('Background vault sync error:', error);
          }
        });
      } else {
        // User is logged in and db is available, navigate to appropriate page
        setIsInitialLoading(false);
        restoreLastPage();
      }
    };

    handleInitialization();
  }, [isFullyInitialized, requiresAuth, isLoggedIn, dbAvailable, navigate, setIsInitialLoading, syncVault, restoreLastPage, refreshSyncState, hasPendingMigrations]);

  // This component doesn't render anything visible - it just handles initialization
  return null;
};

export default Reinitialize;
