import { useState, useCallback, useEffect, useMemo } from 'react'; import { flashcardsService, type Flashcard } from '@/lib/services/flashcards'; import { useDebounce } from './useDebounce'; // 類型定義 export interface SearchFilters { search: string; difficultyLevel: string; partOfSpeech: string; masteryLevel: string; favoritesOnly: boolean; createdAfter?: string; createdBefore?: string; reviewCountMin?: number; reviewCountMax?: number; } export interface SortOptions { sortBy: string; sortOrder: 'asc' | 'desc'; } export interface PaginationState { currentPage: number; pageSize: number; totalPages: number; totalCount: number; hasNext: boolean; hasPrev: boolean; } export interface SearchState { // 資料 flashcards: Flashcard[]; pagination: PaginationState; // UI 狀態 loading: boolean; error: string | null; isInitialLoad: boolean; // 搜尋條件 filters: SearchFilters; sorting: SortOptions; // 元數據 lastUpdated: Date | null; cacheHit: boolean; } export interface SearchActions { // 篩選操作 updateFilters: (filters: Partial) => void; clearFilters: () => void; resetFilters: () => void; // 排序操作 updateSorting: (sorting: Partial) => void; toggleSortOrder: () => void; // 分頁操作 goToPage: (page: number) => void; changePageSize: (size: number) => void; goToNextPage: () => void; goToPrevPage: () => void; // 資料操作 refresh: () => Promise; refetch: () => Promise; } // 初始狀態 const initialState: SearchState = { flashcards: [], pagination: { currentPage: 1, pageSize: 20, totalPages: 0, totalCount: 0, hasNext: false, hasPrev: false, }, loading: false, error: null, isInitialLoad: true, filters: { search: '', difficultyLevel: '', partOfSpeech: '', masteryLevel: '', favoritesOnly: false, }, sorting: { sortBy: 'createdAt', sortOrder: 'desc', }, lastUpdated: null, cacheHit: false, }; export const useFlashcardSearch = (activeTab: 'all-cards' | 'favorites' = 'all-cards'): [SearchState, SearchActions] => { const [state, setState] = useState(initialState); // 搜尋邏輯 const executeSearch = useCallback(async () => { setState(prev => ({ ...prev, loading: true, error: null })); try { // 構建 API 參數 const apiParams = { search: state.filters.search || undefined, favoritesOnly: activeTab === 'favorites' || state.filters.favoritesOnly, difficultyLevel: state.filters.difficultyLevel || undefined, partOfSpeech: state.filters.partOfSpeech || undefined, masteryLevel: state.filters.masteryLevel || undefined, sortBy: state.sorting.sortBy, sortOrder: state.sorting.sortOrder, page: state.pagination.currentPage, limit: state.pagination.pageSize, }; // 暫時不發送difficultyLevel給後端,因為後端不支援 const result = await flashcardsService.getFlashcards( apiParams.search, apiParams.favoritesOnly, undefined, // difficultyLevel 客戶端處理 apiParams.partOfSpeech, apiParams.masteryLevel, apiParams.sortBy, apiParams.sortOrder, 1, // 獲取第一頁 1000 // 大數值以獲取所有資料用於客戶端篩選 ); if (result.success && result.data) { let allFlashcards = result.data.flashcards; // 客戶端篩選 (因為後端不支援某些篩選功能) if (state.filters.difficultyLevel) { allFlashcards = allFlashcards.filter(card => (card as any).difficultyLevel === state.filters.difficultyLevel ); } // 客戶端排序 (確保排序正確) allFlashcards.sort((a, b) => { let aValue: any, bValue: any; switch (state.sorting.sortBy) { case 'word': aValue = a.word.toLowerCase(); bValue = b.word.toLowerCase(); break; case 'createdAt': aValue = new Date(a.createdAt); bValue = new Date(b.createdAt); break; case 'masteryLevel': aValue = a.masteryLevel; bValue = b.masteryLevel; break; case 'difficultyLevel': const levels = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2']; aValue = levels.indexOf((a as any).difficultyLevel || 'A1'); bValue = levels.indexOf((b as any).difficultyLevel || 'A1'); break; case 'timesReviewed': aValue = a.timesReviewed; bValue = b.timesReviewed; break; default: aValue = new Date(a.createdAt); bValue = new Date(b.createdAt); } if (state.sorting.sortOrder === 'asc') { return aValue < bValue ? -1 : aValue > bValue ? 1 : 0; } else { return aValue > bValue ? -1 : aValue < bValue ? 1 : 0; } }); const totalFilteredCount = allFlashcards.length; // 客戶端分頁處理 const startIndex = (state.pagination.currentPage - 1) * state.pagination.pageSize; const endIndex = startIndex + state.pagination.pageSize; const paginatedFlashcards = allFlashcards.slice(startIndex, endIndex); const totalPages = Math.ceil(totalFilteredCount / state.pagination.pageSize); const currentPage = state.pagination.currentPage; setState(prev => ({ ...prev, flashcards: paginatedFlashcards, pagination: { ...prev.pagination, totalPages, totalCount: totalFilteredCount, // 使用篩選後的總數 hasNext: currentPage < totalPages, hasPrev: currentPage > 1, }, loading: false, isInitialLoad: false, lastUpdated: new Date(), cacheHit: false, // 暫時設為 false,等後端支援 })); } else { setState(prev => ({ ...prev, loading: false, error: result.error || 'Failed to load flashcards', })); } } catch (error) { setState(prev => ({ ...prev, loading: false, error: error instanceof Error ? error.message : 'Unknown error', })); } }, [ state.filters.search, state.filters.difficultyLevel, state.filters.partOfSpeech, state.filters.masteryLevel, state.filters.favoritesOnly, state.sorting.sortBy, state.sorting.sortOrder, state.pagination.currentPage, state.pagination.pageSize, activeTab ]); // 防抖搜尋 const debouncedSearch = useDebounce(executeSearch, 300); // Actions const updateFilters = useCallback((newFilters: Partial) => { setState(prev => ({ ...prev, filters: { ...prev.filters, ...newFilters }, pagination: { ...prev.pagination, currentPage: 1 }, // 重置頁碼 })); }, []); const clearFilters = useCallback(() => { setState(prev => ({ ...prev, filters: { search: '', difficultyLevel: '', partOfSpeech: '', masteryLevel: '', favoritesOnly: false, }, pagination: { ...prev.pagination, currentPage: 1 }, })); }, []); const resetFilters = useCallback(() => { setState(prev => ({ ...prev, filters: initialState.filters, sorting: initialState.sorting, pagination: { ...prev.pagination, currentPage: 1, pageSize: 20 }, })); }, []); const updateSorting = useCallback((newSorting: Partial) => { setState(prev => ({ ...prev, sorting: { ...prev.sorting, ...newSorting }, pagination: { ...prev.pagination, currentPage: 1 }, })); }, []); const toggleSortOrder = useCallback(() => { setState(prev => ({ ...prev, sorting: { ...prev.sorting, sortOrder: prev.sorting.sortOrder === 'asc' ? 'desc' : 'asc' }, pagination: { ...prev.pagination, currentPage: 1 }, })); }, []); const goToPage = useCallback((page: number) => { if (page >= 1 && page <= state.pagination.totalPages) { setState(prev => ({ ...prev, pagination: { ...prev.pagination, currentPage: page }, })); } }, [state.pagination.totalPages]); const changePageSize = useCallback((pageSize: number) => { setState(prev => ({ ...prev, pagination: { ...prev.pagination, pageSize, currentPage: 1 }, })); }, []); const goToNextPage = useCallback(() => { if (state.pagination.hasNext) { goToPage(state.pagination.currentPage + 1); } }, [state.pagination.hasNext, state.pagination.currentPage, goToPage]); const goToPrevPage = useCallback(() => { if (state.pagination.hasPrev) { goToPage(state.pagination.currentPage - 1); } }, [state.pagination.hasPrev, state.pagination.currentPage, goToPage]); const refresh = useCallback(async () => { await executeSearch(); }, [executeSearch]); const refetch = useCallback(async () => { setState(prev => ({ ...prev, isInitialLoad: true })); await executeSearch(); }, [executeSearch]); // 自動執行搜尋 useEffect(() => { if (state.filters.search) { debouncedSearch(); } else { executeSearch(); } }, [ state.filters, state.sorting, state.pagination.currentPage, state.pagination.pageSize, activeTab ]); // 檢查是否有活動篩選 const hasActiveFilters = useMemo(() => { return !!( state.filters.search || state.filters.difficultyLevel || state.filters.partOfSpeech || state.filters.masteryLevel || state.filters.favoritesOnly ); }, [state.filters]); // 增強的狀態 const enhancedState = useMemo(() => ({ ...state, hasActiveFilters, }), [state, hasActiveFilters]); return [ enhancedState, { updateFilters, clearFilters, resetFilters, updateSorting, toggleSortOrder, goToPage, changePageSize, goToNextPage, goToPrevPage, refresh, refetch, }, ]; };