import type { ApiDocument, DocumentStatus, ExamModeOutput, FlashcardResponseItem, FlashcardsPayload, NotesOutput, PreviousPaperAnalysisResponse, QuizQuestionResponse, QuizQuestionsPayload, StudyPathResponse, VideoPlanV2Response, VideoRenderJob, VideoRenderJobStatus, VideoScenePlanResponse, } from "@/lib/api/types"; type UnknownRecord = Record; function isRecord(value: unknown): value is UnknownRecord { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } function readString(value: unknown, fallback = "") { return typeof value === "string" && value.trim() ? value : fallback; } function readStringArray(value: unknown): string[] { if (!Array.isArray(value)) return []; return value.map((item) => String(item)).filter((item) => item.trim().length > 0); } export function normalizeDocument(value: unknown): ApiDocument { const record = isRecord(value) ? value : {}; const now = new Date().toISOString(); return { id: readString(record.id, `doc-local-${Date.now()}`), user_id: readString(record.user_id, "demo-user-student"), title: readString(record.title, "Study source"), file_name: readString(record.file_name, "source.pdf"), file_type: readString(record.file_type, "PDF"), subject: typeof record.subject === "string" ? record.subject : null, chapter: typeof record.chapter === "string" ? record.chapter : null, syllabus: typeof record.syllabus === "string" ? record.syllabus : null, status: normalizeDocumentStatus(record.status), // extracted_text removed — raw OCR text is not returned by the API extracted_text_length: typeof record.extracted_text_length === "number" ? record.extracted_text_length : null, chunk_count: typeof record.chunk_count === "number" ? record.chunk_count : 0, created_at: readString(record.created_at, now), }; } export function normalizeDocumentStatus(value: unknown): DocumentStatus { if (value === "ready" || value === "processing" || value === "failed" || value === "uploaded") { return value; } return "ready"; } export function normalizeNotes(value: unknown): NotesOutput { const record = isRecord(value) ? value : {}; // Backend returns exam_keywords; exam_focus is a frontend-side alias const examKeywords = readStringArray(record.exam_keywords); return { title: readString(record.title, "Smart Notes"), summary: typeof record.summary === "string" ? record.summary : undefined, student_level_summary: typeof record.student_level_summary === "string" ? record.student_level_summary : undefined, key_points: readStringArray(record.key_points).length ? readStringArray(record.key_points) : ["Review the main ideas first."], important_keywords: readStringArray(record.important_keywords), important_definitions: readStringArray(record.important_definitions), formulas: readStringArray(record.formulas), diagrams_to_practice: readStringArray(record.diagrams_to_practice), memory_tricks: readStringArray(record.memory_tricks), exam_keywords: examKeywords, exam_focus: readStringArray(record.exam_focus).length ? readStringArray(record.exam_focus) : examKeywords, possible_exam_questions: readStringArray(record.possible_exam_questions), last_minute_revision: readStringArray(record.last_minute_revision), quick_check: readStringArray(record.quick_check), source_summary: typeof record.source_summary === "string" ? record.source_summary : undefined, scoring_keywords: readStringArray(record.scoring_keywords), common_mistakes: readStringArray(record.common_mistakes), ten_minute_revision: readStringArray(record.ten_minute_revision), next_study_action: typeof record.next_study_action === "string" ? record.next_study_action : undefined, caution_note: typeof record.caution_note === "string" ? record.caution_note : undefined, }; } export function normalizeQuiz(value: unknown): QuizQuestionResponse[] { const payload: QuizQuestionsPayload | unknown = value; if (Array.isArray(payload)) return payload.filter(isRecord).map(normalizeQuizQuestion); if (isRecord(payload) && Array.isArray(payload.questions)) { return payload.questions.filter(isRecord).map(normalizeQuizQuestion); } return []; } function normalizeQuizQuestion(value: UnknownRecord): QuizQuestionResponse { return { question: readString(value.question, "Practice question"), type: readString(value.type, "MCQ"), options: readStringArray(value.options), answer: readString(value.answer, "Review the explanation."), explanation: typeof value.explanation === "string" ? value.explanation : undefined, difficulty: readString(value.difficulty, "Practice"), topic: typeof value.topic === "string" ? value.topic : undefined, why_this_matters: typeof value.why_this_matters === "string" ? value.why_this_matters : undefined, common_trap: typeof value.common_trap === "string" ? value.common_trap : undefined, }; } export function normalizeFlashcards(value: unknown): FlashcardResponseItem[] { const payload: FlashcardsPayload | unknown = value; if (Array.isArray(payload)) return payload.filter(isRecord).map(normalizeFlashcard); if (isRecord(payload) && Array.isArray(payload.cards)) { return payload.cards.filter(isRecord).map(normalizeFlashcard); } return []; } function normalizeFlashcard(value: UnknownRecord): FlashcardResponseItem { return { front: readString(value.front, "Key idea"), back: readString(value.back, "Short explanation"), hint: typeof value.hint === "string" ? value.hint : undefined, tag: typeof value.tag === "string" ? value.tag : undefined, topic: typeof value.topic === "string" ? value.topic : undefined, memory_hook: typeof value.memory_hook === "string" ? value.memory_hook : undefined, exam_use: typeof value.exam_use === "string" ? value.exam_use : undefined, }; } export function normalizePYQAnalysis(value: unknown): PreviousPaperAnalysisResponse | null { return isRecord(value) && Array.isArray(value.repeated_topics) ? (value as PreviousPaperAnalysisResponse) : null; } export function normalizeStudyPlan(value: unknown): StudyPathResponse | null { return isRecord(value) && Array.isArray(value.blocks) ? (value as StudyPathResponse) : null; } export function normalizeExamAnswer(value: unknown): ExamModeOutput | null { return isRecord(value) ? (value as ExamModeOutput) : null; } export function normalizeVideoPlan(value: unknown): VideoScenePlanResponse | VideoPlanV2Response | null { if (!isRecord(value)) return null; if (Array.isArray(value.scenes) || Array.isArray(value.sections)) { return value as VideoScenePlanResponse | VideoPlanV2Response; } return null; } export function normalizeRenderJob(value: unknown): VideoRenderJob | null { return isRecord(value) && typeof value.id === "string" ? (value as VideoRenderJob) : null; } export function normalizeRenderStatus(value: unknown): VideoRenderJobStatus { if ( value === "ready" || value === "failed" || value === "queued" || value === "running" || value === "planning" || value === "script_ready" || value === "audio_generating" || value === "visuals_generating" || value === "rendering" || value === "uploading" || value === "complete" ) { return value; } if (value === "completed") return "complete"; if (value === "processing") return "running"; return "queued"; }