// routes/api/brain/queue.ts // API endpoint for building Brain study queue with mode progression import { define, State } from "../../../utils.ts"; import { SessionCookie } from "../../auth/callback.ts"; import { brainDataClient } from "../../../utils/BrainDataClient.ts"; import { getAllKvQuizConfigs } from "../../../utils/QuizDataClient.ts"; import { DEFAULT_QUIZ_MODE, QUIZ_MODES } from "../../../utils/quizModes.ts"; export const handler = define.handlers({ // GET: Build and return study queue for user async GET(ctx) { const state = ctx.state as State; const stateData: SessionCookie = state.data ?? { started_on: 0, user_name: "", user_id: "", role: "", user_encrypted: false, }; if (!state.stateId || !stateData.user_id) { return new Response( JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } }, ); } const url = new URL(ctx.req.url); // Mode parameter is now deprecated - each card has its own mode based on progression // We still accept it for backwards compatibility but it's ignored const newCardsLimit = parseInt( url.searchParams.get("newLimit") || "10", 10, ); const totalLimit = parseInt(url.searchParams.get("limit") || "50", 10); // Parse tab_index from query parameters for filtering const rawTabIndex = url.searchParams.get("tab_index"); let tabIndex: number | undefined = undefined; if (rawTabIndex !== null) { const parsed = Number.parseInt(rawTabIndex, 10); if (Number.isFinite(parsed) && parsed >= 0) { tabIndex = parsed; } } try { // Get all quiz configs for the user let quizConfigs = await getAllKvQuizConfigs(stateData.user_id); // Filter by tab_index if provided if (tabIndex !== undefined) { quizConfigs = quizConfigs.filter((kvObj) => { const quizTabIndex = kvObj.value?.tab_index ?? 0; return quizTabIndex === tabIndex; }); } // Build the study queue (mode is now per-card based on progression) const queue = await brainDataClient.buildStudyQueue( stateData.user_id, quizConfigs, DEFAULT_QUIZ_MODE, // This is now ignored internally but kept for API compatibility newCardsLimit, totalLimit, ); // Get stats const stats = await brainDataClient.getStats(stateData.user_id); // Calculate counts based on new locked-set behavior: // - newCount: cards still in active progression (either truly new or in-progress through modes) // - dueCount: cards that have completed all modes and are now in spaced repetition review // - reviewOnlyCount: same as dueCount (review-only cards) const activeSetCards = queue.filter((q) => q.is_new && !q.is_review_only); const reviewCards = queue.filter((q) => q.is_review_only); return new Response( JSON.stringify({ success: true, queue, stats, // Return default mode for backwards compatibility mode: DEFAULT_QUIZ_MODE, // Include the full mode list so client knows the progression order modeOrder: QUIZ_MODES, totalInQueue: queue.length, // Active set count: cards currently being mastered through 6 modes activeSetCount: activeSetCards.length, // New count: for UI, same as active set (cards not yet fully mastered) newCount: activeSetCards.length, // Review-only count: cards that have completed all 6 modes reviewOnlyCount: reviewCards.length, // Due count: for backwards compatibility, set to review-only count dueCount: reviewCards.length, }), { status: 200, headers: { "Content-Type": "application/json" } }, ); } catch (error) { console.error("Error building brain queue:", error); return new Response( JSON.stringify({ error: "Failed to build queue" }), { status: 500, headers: { "Content-Type": "application/json" } }, ); } }, });