import { UserProfile, Exam, Question, Attempt, StudentGroup } from '../types'; export class DataService { private static authFetch(url: string, init: RequestInit = {}): Promise { const token = localStorage.getItem('local_user_token'); const headers = { ...(init.headers || {}), } as Record; if (token) { headers['Authorization'] = `Bearer ${token}`; } return fetch(url, { ...init, headers }); } // --- User Operations --- static async getProfile(): Promise { try { const response = await this.authFetch('/api/auth/me'); if (!response.ok) return null; return response.json(); } catch { return null; } } static async getAllStudents(): Promise { try { const response = await this.authFetch('/api/students'); if (!response.ok) return []; return response.json(); } catch { return []; } } // --- Question Bank Operations --- static async createQuestion(question: Partial): Promise { const response = await this.authFetch('/api/questions/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(question) }); if (!response.ok) { throw new Error("Failed to create question"); } return response.json(); } // Orchestrator: Launches are asynchronous background document parsing job static async startAiCompilationJob(text: string): Promise<{ jobId: string }> { const response = await this.authFetch('/api/ai/jobs/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }) }); if (!response.ok) { const err = await response.json().catch(() => ({})); throw new Error(err.error || "Failed to start AI compilation workspace"); } return response.json(); } // Orchestrator: Polls the current state of an outstanding compilation job static async getAiJobStatus(jobId: string): Promise { const response = await this.authFetch(`/api/ai/jobs/status/${jobId}`, { method: 'GET' }); if (!response.ok) throw new Error("Failed to consult background job telemetry"); return response.json(); } // Legacy Parser Wrapper: Calls background parsing job and polls to completion, preserving existing workflows safely static async parseDocument(text: string, model?: string): Promise<{ questions: Question[], auditReport?: any, modelUsed?: string }> { const { jobId } = await this.startAiCompilationJob(text); while (true) { await new Promise(resolve => setTimeout(resolve, 1000)); const job = await this.getAiJobStatus(jobId); if (job.status === 'completed') { return { questions: job.result.questions, auditReport: job.result.auditReport, modelUsed: 'meta/llama-3.1-8b-instruct + meta/llama-3.1-70b-instruct' }; } if (job.status === 'failed') { throw new Error(job.error || "Background parsing pipeline returned a fatal error"); } } } // Deep Validation Audit: Verifies exam questions, duplicates, option errors on the fly static async validateQuestions(questions: Question[]): Promise<{ examQualityScore: number; warnings: any[]; duplicates: any[] }> { const response = await this.authFetch('/api/ai/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ questions }) }); if (!response.ok) { const err = await response.json().catch(() => ({})); throw new Error(err.error || "Academic validation report generation failed"); } return response.json(); } // Academic Analyzer: Translates scoring metrics, pass-rates, and category metrics to professional reviews static async explainAnalytics(metrics: any): Promise<{ summary: string; strengths: string[]; weaknesses: string[]; recommendations: string[] }> { const response = await this.authFetch('/api/ai/analytics-explain', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ metrics }) }); if (!response.ok) { const err = await response.json().catch(() => ({})); throw new Error(err.error || "Analytics explain compiler failed"); } return response.json(); } // Contextual Assistant: Real-time queries regarding analytics, warnings, or syllabus alignments static async askAiAssistant(prompt: string, context?: any): Promise<{ response: string; modelUsed: string }> { const response = await this.authFetch('/api/ai/assistant', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, context }) }); if (!response.ok) { const err = await response.json().catch(() => ({})); throw new Error(err.error || "Academic Assistant is currently unresponsive"); } return response.json(); } static async getTeacherQuestions(): Promise { try { const response = await this.authFetch('/api/questions/list'); if (!response.ok) return []; return response.json(); } catch { return []; } } static async deleteQuestion(id: string): Promise { const response = await this.authFetch(`/api/questions/delete/${id}`, { method: 'DELETE' }); if (!response.ok) { throw new Error("Failed to delete question"); } } // --- Group Operations --- static async createGroup(group: Partial): Promise { const response = await this.authFetch('/api/groups/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(group) }); if (!response.ok) { throw new Error("Failed to create group"); } return response.json(); } static async deleteGroup(id: string): Promise { const response = await this.authFetch(`/api/groups/delete/${id}`, { method: 'DELETE' }); if (!response.ok) { throw new Error("Failed to delete group"); } } static async getTeacherGroups(): Promise { try { const response = await this.authFetch('/api/groups/list'); if (!response.ok) return []; return response.json(); } catch { return []; } } static async getTeacherStats() { try { const response = await this.authFetch('/api/teacher/stats'); if (!response.ok) throw new Error("Failed to consult teacher dashboard stats"); return response.json(); } catch (e) { console.error(e); return { totalExams: 0, totalQuestions: 0, totalSubmissions: 0, avgScore: 0 }; } } static async getStudentGroups(): Promise { try { const response = await this.authFetch('/api/student/groups'); if (!response.ok) return []; return response.json(); } catch { return []; } } static async getAttempt(id: string): Promise { try { const response = await this.authFetch(`/api/attempts/get/${id}`); if (!response.ok) return null; return response.json(); } catch { return null; } } static async getQuestionsByIds(ids: string[]): Promise { if (ids.length === 0) return []; try { const response = await this.authFetch('/api/questions/batch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids }) }); if (!response.ok) return []; return response.json(); } catch { return []; } } // --- Exam Operations --- static async createExam(exam: Partial): Promise { const response = await this.authFetch('/api/exams/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(exam) }); if (!response.ok) { const err = await response.json().catch(() => ({})); throw new Error(err.error || "Failed to create exam"); } const data = await response.json(); return data.id; } static async getExam(id: string): Promise { try { const response = await this.authFetch(`/api/exams/get/${id}`); if (!response.ok) return null; return response.json(); } catch { return null; } } static async getTeacherExams(): Promise { try { const response = await this.authFetch('/api/exams/list'); if (!response.ok) return []; return response.json(); } catch { return []; } } static async updateExam(id: string, data: Partial): Promise { const response = await this.authFetch(`/api/exams/update/${id}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error("Failed to update exam"); } } static async getExamAttempts(examId: string): Promise { try { const response = await this.authFetch(`/api/exams/attempts/${examId}`); if (!response.ok) return []; return response.json(); } catch { return []; } } // --- Draft Synchronization Cache Operations --- static async syncDraft(draftData: any): Promise { try { const response = await this.authFetch('/api/drafts/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(draftData) }); if (!response.ok) return null; return response.json(); } catch { return null; } } static async getDraft(): Promise { try { const response = await this.authFetch('/api/drafts/get'); if (!response.ok) return null; return response.json(); } catch { return null; } } static async clearDraft(): Promise { try { await this.authFetch('/api/drafts/clear', { method: 'POST' }); } catch (e) { console.warn("Clear draft failed:", e); } } // --- Attempt Operations --- static async startAttempt(examId: string, student: UserProfile): Promise { const response = await this.authFetch('/api/attempts/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ examId, student }) }); if (!response.ok) { throw new Error("Failed to start exam attempt"); } const data = await response.json(); return data.attemptId; } static async updateAttemptProgress(id: string, answers: any, timeSpent: number): Promise { try { await this.authFetch(`/api/attempts/progress/${id}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ answers, timeSpent }) }); } catch (e) { console.warn("Progress sync failed", e); } } static async submitAttempt( attemptId: string, examId: string, answers: any, timeSpent: number, offlineDuration: number = 0, submissionTimestamp?: string ): Promise { const response = await this.authFetch('/api/exams/submit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ attemptId, examId, answers, timeSpent, offlineDuration, submissionTimestamp }) }); if (!response.ok) { const err = await response.json().catch(() => ({})); throw new Error(err.error || "Failed to submit exam attempt"); } return response.json(); } static async getStudentAttempts(): Promise { try { const response = await this.authFetch('/api/student/attempts'); if (!response.ok) return []; return response.json(); } catch { return []; } } }