/// Exam, Question, and Socratic Diagnostic Models for Saqel Student App class ExamModel { final int id; final String uuid; final int courseId; final int? lessonId; final String title; final String description; final String scope; final double passingPercentage; final int durationMinutes; final int questionsCount; final List questions; const ExamModel({ required this.id, required this.uuid, required this.courseId, this.lessonId, required this.title, required this.description, required this.scope, required this.passingPercentage, required this.durationMinutes, this.questionsCount = 0, this.questions = const [], }); factory ExamModel.fromJson(Map json) { var rawQuestions = json['questions']; List parsedQuestions = []; if (rawQuestions is List) { parsedQuestions = rawQuestions .map((q) => QuestionModel.fromJson(Map.from(q))) .toList(); } return ExamModel( id: _toInt(json['id']), uuid: json['uuid']?.toString() ?? '', courseId: _toInt(json['course_id']), lessonId: json['lesson_id'] != null ? _toInt(json['lesson_id']) : null, title: json['title']?.toString() ?? 'امتحان تشخيصي', description: json['description']?.toString() ?? '', scope: json['scope']?.toString() ?? 'unit_exam', passingPercentage: _toDouble(json['passing_percentage'], 60.0), durationMinutes: _toInt(json['duration_minutes'], 20), questionsCount: _toInt(json['questions_count'], parsedQuestions.length), questions: parsedQuestions, ); } } int _toInt(dynamic value, [int defaultValue = 0]) { if (value == null) return defaultValue; if (value is num) return value.toInt(); return int.tryParse(value.toString()) ?? defaultValue; } double _toDouble(dynamic value, [double defaultValue = 0.0]) { if (value == null) return defaultValue; if (value is num) return value.toDouble(); return double.tryParse(value.toString()) ?? defaultValue; } class QuestionModel { final int id; final String questionText; final String questionType; final int points; final String topicTag; final String? explanationText; final String? aiHint; final List options; const QuestionModel({ required this.id, required this.questionText, required this.questionType, required this.points, required this.topicTag, this.explanationText, this.aiHint, this.options = const [], }); factory QuestionModel.fromJson(Map json) { var rawOptions = json['options']; List parsedOptions = []; if (rawOptions is List) { parsedOptions = rawOptions .map((opt) => QuestionOptionModel.fromJson(Map.from(opt))) .toList(); } return QuestionModel( id: _toInt(json['id']), questionText: json['question_text']?.toString() ?? '', questionType: json['question_type']?.toString() ?? 'multiple_choice', points: _toInt(json['points'], 10), topicTag: json['topic_tag']?.toString() ?? 'المفاهيم العامة', explanationText: json['explanation_text']?.toString(), aiHint: json['ai_hint']?.toString(), options: parsedOptions, ); } } class QuestionOptionModel { final int id; final String optionText; final bool isCorrect; final int sequenceOrder; const QuestionOptionModel({ required this.id, required this.optionText, this.isCorrect = false, this.sequenceOrder = 1, }); factory QuestionOptionModel.fromJson(Map json) { return QuestionOptionModel( id: _toInt(json['id']), optionText: json['option_text']?.toString() ?? '', isCorrect: json['is_correct'] == 1 || json['is_correct'] == true || json['is_correct'] == '1', sequenceOrder: _toInt(json['sequence_order'], 1), ); } } class ExamSubmissionResultModel { final int attemptId; final int score; final int totalScore; final double percentage; final bool passed; final int rewindSeconds; final String aiDiagnosticReport; final List weakTopics; final double tawjihiReadinessScore; final List detailedAnswers; const ExamSubmissionResultModel({ required this.attemptId, required this.score, required this.totalScore, required this.percentage, required this.passed, required this.rewindSeconds, required this.aiDiagnosticReport, required this.weakTopics, required this.tawjihiReadinessScore, this.detailedAnswers = const [], }); factory ExamSubmissionResultModel.fromJson(Map json) { var rawTopics = json['weak_topics']; List parsedTopics = []; if (rawTopics is List) { parsedTopics = rawTopics.map((e) => e.toString()).toList(); } var rawDetails = json['detailed_answers']; List parsedDetails = []; if (rawDetails is List) { parsedDetails = rawDetails .map((d) => DetailedAnswerModel.fromJson(Map.from(d))) .toList(); } return ExamSubmissionResultModel( attemptId: _toInt(json['attempt_id']), score: _toInt(json['score']), totalScore: _toInt(json['total_score'], 100), percentage: _toDouble(json['percentage']), passed: json['passed'] == true || json['passed'] == 1 || json['passed'] == '1', rewindSeconds: _toInt(json['rewind_seconds']), aiDiagnosticReport: json['ai_diagnostic_report']?.toString() ?? '', weakTopics: parsedTopics, tawjihiReadinessScore: _toDouble(json['tawjihi_readiness_score'], 50.0), detailedAnswers: parsedDetails, ); } } class DetailedAnswerModel { final int questionId; final int selectedOptionId; final bool isCorrect; final int pointsAwarded; final String? explanation; final String? aiHint; const DetailedAnswerModel({ required this.questionId, required this.selectedOptionId, required this.isCorrect, required this.pointsAwarded, this.explanation, this.aiHint, }); factory DetailedAnswerModel.fromJson(Map json) { return DetailedAnswerModel( questionId: _toInt(json['question_id']), selectedOptionId: _toInt(json['selected_option_id']), isCorrect: json['is_correct'] == 1 || json['is_correct'] == true || json['is_correct'] == '1', pointsAwarded: _toInt(json['points_awarded']), explanation: json['explanation']?.toString(), aiHint: json['ai_hint']?.toString(), ); } }