import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { useNavigation, useRouter, useLocalSearchParams } from 'expo-router';
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { StyleSheet, Text, Platform, Animated, TextInput, TouchableOpacity, View, RefreshControl } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import Toast from 'react-native-toast-message';

import type { Folder } from '@/utils/db/repositories/FolderRepository';
import type { CredentialSortOrder } from '@/utils/db/repositories/SettingsRepository';
import type { Item, ItemType } from '@/utils/dist/core/models/vault';
import { getFieldValue, FieldKey, ItemTypes } from '@/utils/dist/core/models/vault';
import emitter from '@/utils/EventEmitter';
import { HapticsUtility } from '@/utils/HapticsUtility';
import { applyTypeFilter, isItemTypeFilter, type ItemFilterType } from '@/utils/ItemFilters';
import { VaultAuthenticationError } from '@/utils/types/errors/VaultAuthenticationError';

import { useColors } from '@/hooks/useColorScheme';
import { useMinDurationLoading } from '@/hooks/useMinDurationLoading';
import { useNavigationDebounce } from '@/hooks/useNavigationDebounce';
import { useVaultMutate } from '@/hooks/useVaultMutate';
import { useVaultSync } from '@/hooks/useVaultSync';

import Logo from '@/assets/images/logo.svg';
import { ConfirmDialog } from '@/components/common/ConfirmDialog';
import { FolderModal } from '@/components/folders/FolderModal';
import { FolderPill, type FolderWithCount } from '@/components/folders/FolderPill';
import { ItemCard } from '@/components/items/ItemCard';
import { ItemFilterMenu } from '@/components/items/ItemFilterMenu';
import { SortMenu } from '@/components/items/SortMenu';
import { ThemedContainer } from '@/components/themed/ThemedContainer';
import { ThemedText } from '@/components/themed/ThemedText';
import { ThemedView } from '@/components/themed/ThemedView';
import { AndroidHeader } from '@/components/ui/AndroidHeader';
import { CollapsibleHeader } from '@/components/ui/CollapsibleHeader';
import { RobustPressable } from '@/components/ui/RobustPressable';
import { SkeletonLoader } from '@/components/ui/SkeletonLoader';
import { useApp } from '@/context/AppContext';
import { useDb } from '@/context/DbContext';
import { LocalPreferencesService } from '@/services/LocalPreferencesService';

/**
 * Item type filter option configuration.
 */
type ItemTypeOption = {
  type: ItemType;
  titleKey: string;
  iconName: keyof typeof MaterialIcons.glyphMap;
};

/**
 * Available item type filter options with icons.
 */
const ITEM_TYPE_OPTIONS: ItemTypeOption[] = [
  { type: ItemTypes.Login, titleKey: 'itemTypes.login.title', iconName: 'key' },
  { type: ItemTypes.Alias, titleKey: 'itemTypes.alias.title', iconName: 'person' },
  { type: ItemTypes.CreditCard, titleKey: 'itemTypes.creditCard.title', iconName: 'credit-card' },
  { type: ItemTypes.Note, titleKey: 'itemTypes.note.title', iconName: 'description' },
];

/**
 * Items screen - main vault items list.
 */
export default function ItemsScreen(): React.ReactNode {
  const { syncVault } = useVaultSync();
  const { t } = useTranslation();
  const colors = useColors();
  const navigate = useNavigationDebounce();
  const [scrollY] = useState(() => new Animated.Value(0));
  const navigation = useNavigation();
  const insets = useSafeAreaInsets();
  const flatListRef = useRef<Animated.FlatList<Item | null>>(null);
  const [isTabFocused, setIsTabFocused] = useState(false);
  const router = useRouter();
  const { itemUrl } = useLocalSearchParams<{ itemUrl?: string }>();
  const [itemsList, setItemsList] = useState<Item[]>([]);
  const [folders, setFolders] = useState<Folder[]>([]);
  const [isLoadingItems, setIsLoadingItems] = useMinDurationLoading(false, 200);
  const [refreshing, setRefreshing] = useMinDurationLoading(false, 200);
  const { executeVaultMutation } = useVaultMutate();
  const [showFolderModal, setShowFolderModal] = useState(false);

  // Search and filter state
  const [searchQuery, setSearchQuery] = useState('');
  const [filterType, setFilterType] = useState<ItemFilterType>('all');
  const [showFilterMenu, setShowFilterMenu] = useState(false);
  const [sortOrder, setSortOrder] = useState<CredentialSortOrder>('OldestFirst');
  const [showSortMenu, setShowSortMenu] = useState(false);
  const [showFolderItems, setShowFolderItems] = useState(true);

  // Recently deleted count state
  const [recentlyDeletedCount, setRecentlyDeletedCount] = useState(0);

  // Alert dialog state
  const [alertConfig, setAlertConfig] = useState<{ title: string; message: string } | null>(null);

  /**
   * Hide the alert dialog.
   */
  const hideAlert = useCallback((): void => {
    setAlertConfig(null);
  }, []);

  const authContext = useApp();
  const dbContext = useDb();

  const isAuthenticated = authContext.isLoggedIn;
  const isDatabaseAvailable = dbContext.dbAvailable;

  /*
   * Handle deep link itemUrl parameter - populate search field.
   */
  useEffect(() => {
    if (itemUrl) {
      const decodedUrl = decodeURIComponent(itemUrl);
      // eslint-disable-next-line react-hooks/set-state-in-effect -- syncing search query with external deep-link parameter
      setSearchQuery(decodedUrl);
    }
  }, [itemUrl]);

  /*
   * Save search query to local storage whenever it changes.
   * This allows restoring it when navigating back from item details.
   */
  useEffect(() => {
    LocalPreferencesService.setLastSearchQuery(searchQuery);
  }, [searchQuery]);

  /*
   * Restore search query when screen gains focus (e.g., coming back from item details).
   */
  useEffect(() => {
    const unsubscribeFocus = navigation.addListener('focus', async () => {
      /*
       * Only restore search query if we don't have a deep link.
       * Deep links should take precedence.
       */
      if (!itemUrl) {
        const savedQuery = await LocalPreferencesService.getLastSearchQuery();
        if (savedQuery) {
          setSearchQuery(savedQuery);
        }
      }
    });

    return unsubscribeFocus;
  }, [navigation, itemUrl]);

  // Load saved showFolderItems preference from LocalPreferencesService
  useEffect(() => {
    /**
     * Load the show folders preference from LocalPreferencesService.
     */
    const loadShowFoldersPreference = async (): Promise<void> => {
      try {
        const stored = await LocalPreferencesService.getShowFolders();
        setShowFolderItems(stored);
      } catch {
        // Ignore storage errors, use default value
      }
    };
    loadShowFoldersPreference();
  }, []);

  /**
   * Get folders with item counts for display.
   * Counts respect the active type/feature filter so the folder badge always matches
   * the number of items the user will see when navigating into the folder.
   */
  const foldersWithCounts = useMemo((): FolderWithCount[] => {
    // Don't show folders when searching
    if (searchQuery) {
      return [];
    }

    // Apply the active type/feature filter to the items used for counting.
    const itemsForCount = applyTypeFilter(itemsList, filterType);

    /**
     * Count items per folder (including items in subfolders recursively).
     * @param folderId - The folder ID to count items for
     * @returns Total count of items in the folder and all subfolders
     */
    const getRecursiveItemCount = (folderId: string): number => {
      // Get items directly in this folder
      const directItems = itemsForCount.filter((item: Item) => item.FolderId === folderId);

      // Get all child folders
      const childFolders = folders.filter(f => f.ParentFolderId === folderId);

      // Recursively count items in child folders
      const childItemCount = childFolders.reduce((count, child) => {
        return count + getRecursiveItemCount(child.Id);
      }, 0);

      return directItems.length + childItemCount;
    };

    // Return only root-level folders (no parent) with recursive counts, sorted alphabetically
    return folders
      .filter(folder => !folder.ParentFolderId) // Only root-level folders
      .map(folder => ({
        id: folder.Id,
        name: folder.Name,
        itemCount: getRecursiveItemCount(folder.Id)
      }))
      .sort((a, b) => a.name.localeCompare(b.name));
  }, [folders, itemsList, searchQuery, filterType]);

  /**
   * Get the title based on the active filter.
   */
  const getFilterTitle = useCallback((): string => {
    switch (filterType) {
      case 'passkeys':
        return t('items.filters.passkeys');
      case 'attachments':
        return t('common.attachments');
      case 'totp':
        return t('items.filters.totp');
      case 'all':
        return t('items.title');
      default:
        if (isItemTypeFilter(filterType)) {
          const itemTypeOption = ITEM_TYPE_OPTIONS.find(opt => opt.type === filterType);
          if (itemTypeOption) {
            return t(itemTypeOption.titleKey);
          }
        }
        return t('items.title');
    }
  }, [filterType, t]);

  /**
   * Check if all items are in folders (no items at root level but items exist in folders).
   * This is used to show a helpful message when the user has imported credentials that were all in folders.
   */
  const hasItemsInFoldersOnly = useMemo(() => {
    return itemsList.length > 0 && itemsList.every((item: Item) => item.FolderId !== null);
  }, [itemsList]);

  /**
   * Filter items by folder, type, and search query.
   */
  const filteredItems = useMemo(() => {
    /*
     * When showing folders (checkbox ON): only show root items (exclude items in folders)
     * When not showing folders (checkbox OFF): show all items flat
     */
    const folderScoped = !searchQuery && showFolderItems
      ? itemsList.filter(item => !item.FolderId)
      : itemsList;

    const typeFiltered = applyTypeFilter(folderScoped, filterType);

    const searchLower = searchQuery.toLowerCase().trim();
    if (!searchLower) {
      return typeFiltered;
    }

    const searchWords = searchLower.split(/\s+/).filter(word => word.length > 0);

    return typeFiltered.filter(item => {
      const searchableFields = [
        item.Name?.toLowerCase() || '',
        getFieldValue(item, FieldKey.LoginUsername)?.toLowerCase() || '',
        getFieldValue(item, FieldKey.LoginEmail)?.toLowerCase() || '',
        getFieldValue(item, FieldKey.LoginUrl)?.toLowerCase() || '',
        getFieldValue(item, FieldKey.NotesContent)?.toLowerCase() || '',
      ];

      return searchWords.every(word =>
        searchableFields.some(field => field.includes(word))
      );
    });
  }, [itemsList, searchQuery, filterType, showFolderItems]);

  /**
   * Sort the filtered items based on the current sort order.
   */
  const sortedItems = useMemo(() => {
    const itemsCopy = [...filteredItems];
    switch (sortOrder) {
      case 'NewestFirst':
        return itemsCopy.sort((a, b) =>
          new Date(b.CreatedAt || 0).getTime() - new Date(a.CreatedAt || 0).getTime()
        );
      case 'Alphabetical':
        return itemsCopy.sort((a, b) =>
          (a.Name || '').localeCompare(b.Name || '')
        );
      case 'OldestFirst':
      default:
        return itemsCopy.sort((a, b) =>
          new Date(a.CreatedAt || 0).getTime() - new Date(b.CreatedAt || 0).getTime()
        );
    }
  }, [filteredItems, sortOrder]);

  /**
   * Load items (credentials), folders, and recently deleted count.
   */
  const loadItems = useCallback(async (): Promise<void> => {
    try {
      const [items, loadedFolders, deletedCount, savedSortOrder] = await Promise.all([
        dbContext.sqliteClient!.items.getAll(),
        dbContext.sqliteClient!.folders.getAll(),
        dbContext.sqliteClient!.items.getRecentlyDeletedCount(),
        dbContext.sqliteClient!.settings.getCredentialsSortOrder()
      ]);
      setItemsList(items);
      setFolders(loadedFolders);
      setRecentlyDeletedCount(deletedCount);
      setSortOrder(savedSortOrder);
      setIsLoadingItems(false);
    } catch (err) {
      console.error('Error loading items:', err);
      const errorMessage = err instanceof Error ? err.message : String(err);
      const errorStack = err instanceof Error ? err.stack ?? '' : '';
      router.replace({
        pathname: '/vault-error',
        params: { errorMessage, errorStack, errorSource: 'items' },
      });
    }
  }, [dbContext.sqliteClient, setIsLoadingItems, router]);

  useEffect(() => {
    const unsubscribeFocus = navigation.addListener('focus', () => {
      setIsTabFocused(true);
    });

    const unsubscribeBlur = navigation.addListener('blur', () => {
      setIsTabFocused(false);
    });

    const tabPressSub = emitter.addListener('tabPress', (routeName: string) => {
      if (routeName === 'items' && isTabFocused) {
        // Reset search and scroll to top when tapping the tab again
        setSearchQuery('');
        flatListRef.current?.scrollToOffset({ offset: 0, animated: true });
      }
    });

    // Add listener for item/credential changes
    const itemChangedSub = emitter.addListener('credentialChanged', async () => {
      await loadItems();
    });

    return (): void => {
      tabPressSub.remove();
      itemChangedSub.remove();
      unsubscribeFocus();
      unsubscribeBlur();
    };
  }, [isTabFocused, loadItems, navigation, setRefreshing]);

  /**
   * Handle pull-to-refresh.
   */
  const onRefresh = useCallback(async () => {
    HapticsUtility.impact();

    setRefreshing(true);
    setIsLoadingItems(true);

    // Always attempt sync, even when offline - this allows recovery when connection is restored
    try {
      await syncVault({
        /**
         * On success.
         */
        onSuccess: async (hasNewVault) => {
          await loadItems();
          await dbContext.refreshSyncState(); // Clear offline state if we were offline
          setIsLoadingItems(false);
          setRefreshing(false);
          setTimeout(() => {
            Toast.show({
              type: 'success',
              text1: hasNewVault ? t('items.vaultSyncedSuccessfully') : t('items.vaultUpToDate'),
              position: 'top',
              visibilityTime: 1200,
            });
          }, 200);
        },
        /**
         * On offline - just update state, ServerSyncIndicator shows offline status.
         */
        onOffline: async () => {
          setRefreshing(false);
          setIsLoadingItems(false);
          await dbContext.setIsOffline(true);
          await dbContext.refreshSyncState();
        },
        /**
         * On error.
         */
        onError: async (error) => {
          console.error('Error syncing vault:', error);
          setRefreshing(false);
          setIsLoadingItems(false);

          // Show generic error message to user, detailed error is logged above
          setAlertConfig({ title: t('common.error'), message: t('common.errors.unknownError') });
        },
        /**
         * On upgrade required.
         */
        onUpgradeRequired: (): void => {
          router.replace('/upgrade');
        },
      });
    } catch (err) {
      console.error('Error refreshing items:', err);
      setRefreshing(false);
      setIsLoadingItems(false);

      if (!(err instanceof VaultAuthenticationError)) {
        Toast.show({
          type: 'error',
          text1: t('items.vaultSyncFailed'),
          text2: t('common.errors.unknownError'),
        });
      }
    }
  }, [syncVault, loadItems, setIsLoadingItems, setRefreshing, dbContext, router, t]);

  useEffect(() => {
    if (!isAuthenticated || !isDatabaseAvailable) {
      return;
    }

    setIsLoadingItems(true);
    // eslint-disable-next-line react-hooks/set-state-in-effect -- loadItems is async; setState only fires after data is fetched
    loadItems();
  }, [isAuthenticated, isDatabaseAvailable, loadItems, setIsLoadingItems]);

  /**
   * Track previous syncing state to detect when sync completes.
   */
  const wasSyncingRef = useRef(dbContext.isSyncing);

  /**
   * Reload items when background sync completes (isSyncing goes from true to false).
   * This ensures newly synced data is displayed without requiring manual pull-to-refresh.
   */
  useEffect(() => {
    const wasSyncing = wasSyncingRef.current;
    wasSyncingRef.current = dbContext.isSyncing;

    // Only reload when sync just completed (was syncing, now not syncing)
    if (wasSyncing && !dbContext.isSyncing && isAuthenticated && isDatabaseAvailable) {
      loadItems();
    }
  }, [dbContext.isSyncing, isAuthenticated, isDatabaseAvailable, loadItems]);

  // Set header for Android
  useEffect(() => {
    navigation.setOptions({
      /**
       * Define custom header which is shown on Android. iOS displays the custom CollapsibleHeader component instead.
       */
      headerTitle: (): React.ReactNode => {
        if (Platform.OS === 'android') {
          return (
            <AndroidHeader
              title={getFilterTitle()}
              subtitle={`(${filteredItems.length})`}
              onTitlePress={() => setShowFilterMenu(prev => !prev)}
              isDropdownOpen={showFilterMenu}
            />
          );
        }
        return <Text>{t('items.title')}</Text>;
      },
      /**
       * Sort button in the header (Android only, iOS uses inline button).
       */
      headerRight: (): React.ReactNode => {
        if (Platform.OS === 'android') {
          return (
            <TouchableOpacity
              style={{ padding: 8, marginRight: 8 }}
              onPress={() => setShowSortMenu(prev => !prev)}
            >
              <MaterialIcons name="sort" size={24} color={colors.text} />
            </TouchableOpacity>
          );
        }
        return null;
      },
    });
  }, [navigation, t, getFilterTitle, filteredItems.length, showFilterMenu, colors.text]);

  /**
   * Delete an item (move to trash).
   * Non-blocking: saves locally and syncs in background via ServerSyncIndicator.
   */
  const onItemDelete = useCallback(async (itemId: string): Promise<void> => {
    await executeVaultMutation(async () => {
      await dbContext.sqliteClient!.items.trash(itemId);
    });

    // Reload items to reflect the deletion
    await loadItems();
  }, [dbContext.sqliteClient, executeVaultMutation, loadItems]);

  /**
   * Navigate to a folder, preserving the active type/feature filter so the folder view
   * shows the same subset of items reflected in the folder's badge count.
   */
  const handleFolderClick = useCallback((folderId: string) => {
    navigate(() => {
      router.push(`/(tabs)/items/folder/${folderId}?filter=${encodeURIComponent(filterType)}` as '/(tabs)/items/folder/[id]');
    });
  }, [router, navigate, filterType]);

  /**
   * Create a new folder.
   */
  const handleCreateFolder = useCallback(async (folderName: string) => {
    await executeVaultMutation(async () => {
      await dbContext.sqliteClient!.folders.create(folderName, null);
    });
    await loadItems();
  }, [dbContext.sqliteClient, executeVaultMutation, loadItems]);

  /**
   * Handle FAB press - navigate to add item screen.
   * If there's a search query, pass it as itemUrl (if URL) or itemName (if not).
   */
  const handleAddItem = useCallback(() => {
    navigate(() => {
      const trimmedQuery = searchQuery.trim();
      if (trimmedQuery) {
        const isUrl = /^https?:\/\//i.test(trimmedQuery);
        if (isUrl) {
          router.push(`/(tabs)/items/add-edit?itemUrl=${encodeURIComponent(trimmedQuery)}`);
        } else {
          router.push(`/(tabs)/items/add-edit?itemName=${encodeURIComponent(trimmedQuery)}`);
        }
      } else {
        router.push('/(tabs)/items/add-edit');
      }
      HapticsUtility.impact();
    });
  }, [router, searchQuery, navigate]);

  const styles = StyleSheet.create({
    container: {
      paddingHorizontal: 0,
    },
    stepContainer: {
      flex: 1,
      gap: 8,
    },
    contentContainer: {
      paddingBottom: Platform.OS === 'ios' ? insets.bottom + 60 : 10,
      paddingHorizontal: 14,
      paddingTop: Platform.OS === 'ios' ? 42 : 8,
    },
    // Header row styles
    headerRow: {
      alignItems: 'center',
      flexDirection: 'row',
      justifyContent: 'space-between',
      marginBottom: 16,
    },
    // Filter button styles
    filterButton: {
      alignItems: 'center',
      flexDirection: 'row',
      flex: 1,
      gap: 8,
    },
    sortButton: {
      padding: 8,
    },
    filterButtonText: {
      color: colors.text,
      fontSize: 28,
      fontWeight: 'bold',
      lineHeight: 34,
    },
    filterCount: {
      color: colors.textMuted,
      fontSize: 20,
      lineHeight: 28,
    },
    // Folder pills styles
    folderPillsContainer: {
      flexDirection: 'row',
      flexWrap: 'wrap',
      gap: 8,
      marginBottom: 16,
    },
    newFolderButton: {
      alignItems: 'center',
      backgroundColor: colors.accentBackground,
      borderColor: colors.accentBorder,
      borderRadius: 20,
      borderStyle: 'dashed',
      borderWidth: 1,
      flexDirection: 'row',
      gap: 6,
      paddingHorizontal: 12,
      paddingVertical: 8,
    },
    newFolderButtonText: {
      color: colors.textMuted,
      fontSize: 14,
    },
    // Search styles
    searchContainer: {
      position: 'relative',
      marginTop: 12,
    },
    searchIcon: {
      left: 12,
      position: 'absolute',
      top: 11,
      zIndex: 1,
    },
    searchInput: {
      backgroundColor: colors.accentBackground,
      borderRadius: 8,
      color: colors.text,
      fontSize: 16,
      height: 40,
      lineHeight: 20,
      marginBottom: 16,
      paddingLeft: 40,
      paddingRight: Platform.OS === 'android' ? 40 : 12,
    },
    clearButton: {
      padding: 4,
      position: 'absolute',
      right: 8,
      top: 4,
    },
    clearButtonText: {
      color: colors.textMuted,
      fontSize: 20,
    },
    // Empty state styles
    emptyContainer: {
      alignItems: 'center',
      marginTop: 24,
    },
    emptyText: {
      color: colors.textMuted,
      fontSize: 16,
      opacity: 0.7,
      textAlign: 'center',
    },
    clearButtonsContainer: {
      flexDirection: 'row',
      flexWrap: 'wrap',
      gap: 8,
      justifyContent: 'center',
      marginTop: 16,
    },
    clearSearchButton: {
      alignItems: 'center',
      backgroundColor: colors.accentBackground,
      borderRadius: 8,
      flexDirection: 'row',
      gap: 6,
      paddingHorizontal: 12,
      paddingVertical: 8,
    },
    clearSearchButtonText: {
      color: colors.text,
      fontSize: 14,
    },
    clearFilterButton: {
      alignItems: 'center',
      backgroundColor: colors.primary + '20',
      borderRadius: 8,
      flexDirection: 'row',
      gap: 6,
      paddingHorizontal: 12,
      paddingVertical: 8,
    },
    clearFilterButtonText: {
      color: colors.primary,
      fontSize: 14,
    },
    // Footer clear buttons styles (at bottom of list)
    footerClearContainer: {
      alignItems: 'center',
      flexDirection: 'row',
      flexWrap: 'wrap',
      gap: 8,
      justifyContent: 'center',
      marginTop: 16,
      paddingTop: 16,
    },
    // FAB styles
    fab: {
      alignItems: 'center',
      backgroundColor: colors.primary,
      borderRadius: 28,
      bottom: Platform.OS === 'ios' ? insets.bottom + 60 : 16,
      elevation: 4,
      height: 56,
      justifyContent: 'center',
      position: 'absolute',
      right: 16,
      shadowColor: colors.black,
      shadowOffset: {
        width: 0,
        height: 2,
      },
      shadowOpacity: 0.25,
      shadowRadius: 3.84,
      width: 56,
      zIndex: 1000,
    },
    fabIcon: {
      color: colors.primarySurfaceText,
      fontSize: 24,
    },
  });

  /**
   * Render the list header with filter button, folders, and search.
   */
  const renderListHeader = (): React.ReactNode => {
    return (
      <ThemedView>
        {/* Large header with logo (iOS only) */}
        {Platform.OS === 'ios' && (
          <View style={styles.headerRow}>
            <TouchableOpacity
              style={styles.filterButton}
              onPress={() => setShowFilterMenu(!showFilterMenu)}
            >
              <Logo width={40} height={40} />
              <ThemedText style={styles.filterButtonText}>
                {getFilterTitle()}
              </ThemedText>
              <ThemedText style={styles.filterCount}>
                ({filteredItems.length})
              </ThemedText>
              <MaterialIcons
                name={showFilterMenu ? "keyboard-arrow-up" : "keyboard-arrow-down"}
                size={28}
                color={colors.text}
              />
            </TouchableOpacity>
            {/* Sort button */}
            <TouchableOpacity
              style={styles.sortButton}
              onPress={() => setShowSortMenu(!showSortMenu)}
            >
              <MaterialIcons
                name="sort"
                size={24}
                color={colors.textMuted}
              />
            </TouchableOpacity>
          </View>
        )}

        {/* Search input */}
        <ThemedView style={styles.searchContainer}>
          <MaterialIcons
            name="search"
            size={20}
            color={colors.textMuted}
            style={styles.searchIcon}
          />
          <TextInput
            style={styles.searchInput}
            placeholder={t('items.searchPlaceholder')}
            placeholderTextColor={colors.textMuted}
            value={searchQuery}
            autoCorrect={false}
            autoCapitalize="none"
            multiline={false}
            numberOfLines={1}
            onChangeText={setSearchQuery}
            clearButtonMode={Platform.OS === 'ios' ? 'while-editing' : 'never'}
            testID="search-input"
          />
          {Platform.OS === 'android' && searchQuery.length > 0 && (
            <TouchableOpacity
              style={styles.clearButton}
              onPress={() => setSearchQuery('')}
              testID="clear-search-button"
            >
              <ThemedText style={styles.clearButtonText}>×</ThemedText>
            </TouchableOpacity>
          )}
        </ThemedView>

        {/* Folder pills (shown below search when not searching and showing folder items) */}
        {!searchQuery && showFolderItems && (
          <View style={styles.folderPillsContainer}>
            {foldersWithCounts.map((folder) => (
              <FolderPill
                key={folder.id}
                folder={folder}
                onPress={() => handleFolderClick(folder.id)}
              />
            ))}
            <TouchableOpacity
              style={styles.newFolderButton}
              onPress={() => setShowFolderModal(true)}
            >
              <MaterialIcons name="create-new-folder" size={16} color={colors.textMuted} />
              <Text style={styles.newFolderButtonText}>{t('items.folders.newFolder')}</Text>
            </TouchableOpacity>
          </View>
        )}
      </ThemedView>
    );
  };

  /**
   * Render empty state.
   */
  const renderEmptyComponent = (): React.ReactNode => {
    if (isLoadingItems) {
      return null;
    }

    /**
     * Determine the appropriate message based on search and filter state.
     */
    const getMessage = (): string => {
      // Both search and filter active
      if (searchQuery && filterType !== 'all') {
        return t('items.noMatchingItemsWithFilter', { filter: getFilterTitle(), search: searchQuery });
      }
      // Only search active
      if (searchQuery) {
        return t('items.noMatchingItemsSearch', { search: searchQuery });
      }
      // Only filter active (no search)
      if (filterType !== 'all') {
        return t('items.noMatchingItems');
      }
      // All items are in folders - show helpful message
      if (hasItemsInFoldersOnly) {
        return t('items.allItemsInFolders');
      }
      // No search, no filter - truly empty vault
      return t('items.noItemsFound');
    };

    const showClearButtons = searchQuery || filterType !== 'all';

    return (
      <View style={styles.emptyContainer}>
        <Text style={styles.emptyText}>{getMessage()}</Text>

        {/* Clear search/filter buttons */}
        {showClearButtons && (
          <View style={styles.clearButtonsContainer}>
            {searchQuery && (
              <TouchableOpacity
                style={styles.clearSearchButton}
                onPress={() => setSearchQuery('')}
              >
                <MaterialIcons name="close" size={16} color={colors.text} />
                <Text style={styles.clearSearchButtonText}>{t('items.clearSearch')}</Text>
              </TouchableOpacity>
            )}
            {filterType !== 'all' && (
              <TouchableOpacity
                style={styles.clearFilterButton}
                onPress={() => setFilterType('all')}
              >
                <MaterialIcons name="close" size={16} color={colors.primary} />
                <Text style={styles.clearFilterButtonText}>{t('items.clearFilter')}</Text>
              </TouchableOpacity>
            )}
          </View>
        )}
      </View>
    );
  };

  /**
   * Render list footer with clear filter/search buttons.
   * Only shown when there are items and a filter or search is active.
   */
  const renderListFooter = (): React.ReactNode => {
    // Don't show footer if loading, no items, or no active filter/search
    if (isLoadingItems || filteredItems.length === 0 || (filterType === 'all' && !searchQuery)) {
      return null;
    }

    return (
      <View style={styles.footerClearContainer}>
        {searchQuery && (
          <TouchableOpacity
            style={styles.clearSearchButton}
            onPress={() => setSearchQuery('')}
          >
            <MaterialIcons name="close" size={16} color={colors.text} />
            <Text style={styles.clearSearchButtonText}>{t('items.clearSearch')}</Text>
          </TouchableOpacity>
        )}
        {filterType !== 'all' && (
          <TouchableOpacity
            style={styles.clearFilterButton}
            onPress={() => setFilterType('all')}
          >
            <MaterialIcons name="close" size={16} color={colors.primary} />
            <Text style={styles.clearFilterButtonText}>{t('items.clearFilter')}</Text>
          </TouchableOpacity>
        )}
      </View>
    );
  };

  return (
    <ThemedContainer style={styles.container} testID="items-screen">
      <CollapsibleHeader
        title={getFilterTitle()}
        scrollY={scrollY}
        showNavigationHeader={true}
        alwaysVisible={true}
      />
      <ThemedView style={styles.stepContainer}>
        {/* FAB */}
        <RobustPressable style={styles.fab} onPress={handleAddItem} testID="add-item-button">
          <MaterialIcons name="add" style={styles.fabIcon} />
        </RobustPressable>

        {/* Item list */}
        <Animated.FlatList
          ref={flatListRef}
          testID="items-list"
          data={isLoadingItems ? Array(4).fill(null) : sortedItems}
          keyExtractor={(itm, index) => itm?.Id ?? `skeleton-${index}`}
          keyboardShouldPersistTaps='handled'
          onScroll={Animated.event(
            [{ nativeEvent: { contentOffset: { y: scrollY } } }],
            { useNativeDriver: true }
          )}
          scrollEventThrottle={16}
          contentContainerStyle={styles.contentContainer}
          scrollIndicatorInsets={{ bottom: 40 }}
          initialNumToRender={14}
          maxToRenderPerBatch={14}
          windowSize={7}
          removeClippedSubviews={false}
          ListHeaderComponent={renderListHeader() as React.ReactElement}
          refreshControl={
            <RefreshControl
              refreshing={refreshing}
              onRefresh={onRefresh}
              colors={[colors.primary]}
              tintColor={colors.primary}
            />
          }
          renderItem={({ item: itm }) =>
            isLoadingItems ? (
              <SkeletonLoader count={1} height={60} parts={2} />
            ) : (
              <ItemCard item={itm} onItemDelete={onItemDelete} showFolderPath={!!searchQuery} />
            )
          }
          ListEmptyComponent={renderEmptyComponent() as React.ReactElement}
          ListFooterComponent={renderListFooter() as React.ReactElement}
        />
      </ThemedView>

      {/* Filter menu overlay */}
      <ItemFilterMenu
        visible={showFilterMenu}
        activeFilter={filterType}
        recentlyDeletedCount={recentlyDeletedCount}
        showFoldersToggle
        showFolders={showFolderItems}
        onClose={() => setShowFilterMenu(false)}
        onSelectFilter={(filter) => setFilterType(filter)}
        onSelectRecentlyDeleted={() => router.push('/(tabs)/items/deleted')}
        onToggleShowFolders={(next) => {
          setShowFolderItems(next);
          LocalPreferencesService.setShowFolders(next);
        }}
      />

      {/* Sort menu overlay */}
      <SortMenu
        visible={showSortMenu}
        sortOrder={sortOrder}
        onSelect={async (order: CredentialSortOrder) => {
          setSortOrder(order);
          // Save to settings and trigger vault sync
          await executeVaultMutation(async () => {
            await dbContext.sqliteClient?.settings.setCredentialsSortOrder(order);
          });
        }}
        onClose={() => setShowSortMenu(false)}
      />

      {/* Create folder modal */}
      <FolderModal
        isOpen={showFolderModal}
        onClose={() => setShowFolderModal(false)}
        onSave={handleCreateFolder}
        mode="create"
      />

      {/* Alert dialog */}
      <ConfirmDialog
        isVisible={alertConfig !== null}
        title={alertConfig?.title ?? ''}
        message={alertConfig?.message ?? ''}
        buttons={[{ text: t('common.ok'), style: 'default', onPress: hideAlert }]}
        onClose={hideAlert}
      />
    </ThemedContainer>
  );
}
