import { apiRequest } from './api'; export interface QuestionOption { text?: string; label?: string; value?: string | number; content?: string; [key: string]: unknown; } export interface QuestionItem { id: string; questionId?: string; content?: string; type?: string; typeLabel?: string; mediaUrl?: string | null; options?: QuestionOption[] | string[] | null; answerText?: string | null; correctOptionIndex?: number | null; correctOptionIndices?: number[] | null; explanation?: string | null; subQuestions?: Record[] | null; hasVideoExplanation?: boolean; wrongCount?: number; lastWrongAt?: string | null; resolvedAt?: string | null; createdAt?: string; } export interface SubAnswerInput { subQuestionId: string; selectedOptions?: string[]; answerText?: string; selfJudgedCorrect?: boolean; } export interface SubAnswerResult { subQuestionId: string; order?: number; type?: string; typeLabel?: string | null; content?: string | null; selectedOptions?: string[]; answerText?: string | null; isCorrect?: boolean | null; selfJudged?: boolean; correctOptionIndex?: number | null; correctOptionIndices?: number[]; correctAnswerText?: string | null; explanation?: string | null; answered?: boolean; score?: number; totalScore?: number; } export interface AnswerPayload { mode?: string; subAnswers?: SubAnswerInput[]; subResults?: SubAnswerResult[]; summary?: Record; } export interface PracticeSession { id: string; mode: string; questionIds: string[]; questionCount: number; durationMinutes?: number | null; accessMode?: string; consumedFreeQuota?: number; expiresAt?: string | null; } export interface PracticeSessionDetail extends PracticeSession { status?: string; startedAt?: string; finishedAt?: string | null; entryId?: string | null; contentNodeId?: string | null; collectionId?: string | null; blueprintId?: string | null; questions?: QuestionItem[]; answersByQuestion?: Record; } export interface AnswerResult { id: string; questionId: string; isCorrect: boolean | null; selectedOptions?: string[]; answerText?: string | null; answerPayload?: AnswerPayload; answeredAt?: string; selfJudged?: boolean; subResults?: SubAnswerResult[]; compositeSummary?: Record; } export interface PracticeReport { id: string; practiceSessionId: string; mode: string; totalQuestions: number; answeredCount: number; correctCount: number; wrongCount: number; unansweredCount: number; score: number; totalScore: number; accuracy: number; durationSeconds?: number; sectionStats?: Record[]; questionResults?: Record[]; wrongQuestionIds?: string[]; submittedAt?: string; } export interface PracticeHistoryItem { id: string; mode: string; blueprintName?: string | null; collectionName?: string | null; entryName?: string | null; contentNodeName?: string | null; questionCount?: number; durationMinutes?: number | null; accessMode?: string | null; answeredCount?: number; correctCount?: number; wrongCount?: number; status?: string; reportId?: string | null; score?: number | null; reportTotalScore?: number | null; accuracy?: number | null; startedAt?: string; finishedAt?: string | null; submittedAt?: string | null; } export interface WrongQuestionReviewItem { questionId: string; wrongCount?: number; lastWrongAt?: string | null; resolvedAt?: string | null; type?: string; typeLabel?: string | null; subjectName?: string | null; categoryName?: string | null; content?: string | null; answerAttempts?: number; reviewStatus?: string; suggestedReviewAt?: string | null; } export interface VocabularyWord { id?: string; wordId?: string; unitId?: string; word: string; phonetic?: string | null; meaning?: string | null; example?: string | null; exampleTranslation?: string | null; difficulty?: string | null; status?: string; dueLevel?: string; favoritedAt?: string | null; note?: string | null; } export interface LearningStats { windowDays: number; answers: { totalAnswered: number; correctCount: number; wrongCount: number; todayAnswered: number; accuracy: number; latestAnsweredAt?: string | null; }; sessions: { totalSessions: number; activeSessions: number; finishedSessions: number; latestStartedAt?: string | null; }; reports: { reportCount: number; avgAccuracy: number; bestScore: number; latestSubmittedAt?: string | null; }; wrongBook: { unresolvedWrong: number; resolvedWrong: number; totalWrongBook: number; }; favorites: { favoriteQuestions: number; }; questionTypes: LearningQuestionTypeStats[]; } export interface LearningQuestionTypeStats { questionType: string; typeLabel?: string | null; answeredCount: number; correctCount: number; wrongCount: number; accuracy: number; } export interface LearningTrendItem { date: string; answeredCount: number; correctCount: number; wrongCount: number; accuracy: number; sessionCount: number; reportCount: number; score: number; totalScore: number; } export interface LeaderboardItem { rank: number; userId: string; displayName: string; avatarUrl?: string | null; avatarPreset?: 'male' | 'female'; primaryRole?: string; regionId?: string | null; regionName?: string | null; classIds?: string[]; classNames?: string[]; value: number; secondaryValue?: number | null; latestAt?: string | null; isCurrentUser?: boolean; } export interface LeaderboardPayload { metric: 'questions' | 'score' | 'vocabulary' | 'mock_exam'; label: string; unit: string; period: 'all' | '7d' | '30d'; items?: LeaderboardItem[]; currentUser?: LeaderboardItem | null; generatedAt?: string; } export async function createPracticeSession(body: { blueprintId?: string; collectionId?: string; entryId?: string; contentNodeId?: string; mode?: string; questionLimit?: number; }) { return apiRequest<{ item: PracticeSession }>('/api/learning/practice-sessions', { method: 'POST', body, }); } export async function loadPracticeSessionDetail(practiceSessionId: string) { return apiRequest<{ item: PracticeSessionDetail }>('/api/learning/practice-sessions/detail', { query: { practiceSessionId }, }); } export async function submitAnswer(body: { practiceSessionId: string; questionId: string; selectedOptions?: string[]; answerText?: string; selfJudgedCorrect?: boolean; subAnswers?: SubAnswerInput[]; }) { return apiRequest<{ item: AnswerResult }>('/api/learning/answers', { method: 'POST', body, }); } export async function submitPracticeSession(practiceSessionId: string) { return apiRequest<{ item?: PracticeReport }>('/api/learning/practice-sessions/submit', { method: 'POST', body: { practiceSessionId }, }); } export async function loadPracticeSessionReport(practiceSessionId: string) { return apiRequest<{ item?: PracticeReport }>('/api/learning/practice-sessions/report', { query: { practiceSessionId }, }); } export async function loadPracticeReports(query: { mode?: string; blueprintId?: string; limit?: number } = {}) { return apiRequest<{ items?: PracticeReport[] }>('/api/learning/practice-reports', { query: { ...query, limit: query.limit || 20 }, }); } export async function loadPracticeHistory(query: { mode?: string; status?: string; limit?: number } = {}) { return apiRequest<{ items?: PracticeHistoryItem[] }>('/api/learning/practice-sessions/history', { query: { ...query, limit: query.limit || 20 }, }); } export async function toggleQuestionFavorite(questionId: string, favorite: boolean) { return apiRequest<{ ok: boolean; favorite: boolean }>('/api/learning/favorites/questions', { method: 'POST', body: { questionId, favorite }, }); } export async function loadWrongQuestions(limit = 30) { return apiRequest<{ items?: QuestionItem[] }>('/api/learning/wrong-questions', { query: { limit } }); } export async function loadWrongQuestionReviewPlan(limit = 20) { return apiRequest<{ items?: WrongQuestionReviewItem[]; nextAction?: Record }>('/api/learning/wrong-questions/review-plan', { query: { limit }, }); } export async function resolveWrongQuestion(questionId: string) { return apiRequest<{ ok: boolean }>('/api/learning/wrong-questions/resolve', { method: 'POST', body: { questionId }, }); } export async function loadFavoriteQuestions(limit = 30) { return apiRequest<{ items?: QuestionItem[] }>('/api/learning/favorites/questions', { query: { limit } }); } export async function loadLearningStats(days = 30) { return apiRequest<{ item?: LearningStats }>('/api/learning/stats', { query: { days } }); } export async function loadLeaderboard(metric: 'questions' | 'score' | 'vocabulary' | 'mock_exam' = 'questions') { return apiRequest('/api/learning/leaderboard', { query: { metric, period: '7d', scope: 'tenant' }, }); } export async function loadLearningTrend(days = 14) { return apiRequest<{ items?: LearningTrendItem[] }>('/api/learning/trend', { query: { days } }); } export async function loadVocabularyReviewPlan(unitId?: string) { return apiRequest<{ item?: { words?: VocabularyWord[]; dueCount?: number; newCount?: number; totalPlanned?: number } }>( '/api/learning/vocabulary/review-plan', { query: { unitId, reviewLimit: 20, newLimit: 10 } }, ); } export async function reviewVocabularyWord(wordId: string, result: 'known' | 'unknown') { return apiRequest<{ item?: Record }>('/api/learning/vocabulary/review', { method: 'POST', body: { wordId, result }, }); } export async function toggleWordFavorite(wordId: string, favorite: boolean) { return apiRequest<{ ok: boolean; favorite: boolean }>('/api/learning/vocabulary/favorites', { method: 'POST', body: { wordId, favorite }, }); } export async function loadFavoriteWords(unitId?: string, limit = 500) { return apiRequest<{ items?: VocabularyWord[] }>('/api/learning/vocabulary/favorites', { query: { unitId, limit }, }); } export async function loadVocabularyStats(unitId?: string) { return apiRequest<{ item?: Record }>('/api/learning/vocabulary/stats', { query: { unitId } }); }