import '../../core/config/app_config.dart'; String _absolutePlaybackUrl(String value) { if (value.isEmpty || value.startsWith('http://') || value.startsWith('https://')) return value; final base = AppConfig.baseUrl.replaceAll(RegExp(r'/+$'), ''); return '$base/${value.replaceFirst(RegExp(r'^/+'), '')}'; } /// Model representing an In-Video Socratic Gatekeeping Checkpoint (فحص الفهم السقراطي) class SocraticCheckpointModel { final int id; final int questionId; final String questionText; final int timestampSeconds; final int rewindSecondsOnFail; final String hint; final String pedagogicalExplanation; final List options; const SocraticCheckpointModel({ required this.id, this.questionId = 0, required this.questionText, required this.timestampSeconds, this.rewindSecondsOnFail = 45, this.hint = '', this.pedagogicalExplanation = '', this.options = const [], }); factory SocraticCheckpointModel.fromJson(Map json) { final List parsedOptions = []; if (json['options'] != null && json['options'] is List) { for (var opt in json['options']) { if (opt is Map) { parsedOptions.add(SocraticOptionModel.fromJson(Map.from(opt))); } } } return SocraticCheckpointModel( id: json['id'] is num ? (json['id'] as num).toInt() : int.tryParse((json['id'] ?? json['exam_id'] ?? '0').toString()) ?? 0, questionId: json['question_id'] is num ? (json['question_id'] as num).toInt() : int.tryParse(json['question_id']?.toString() ?? '0') ?? 0, questionText: json['question']?.toString() ?? json['question_text']?.toString() ?? 'سؤال فحص الفهم السقراطي', timestampSeconds: (json['timestamp_seconds'] as num?)?.toInt() ?? 120, rewindSecondsOnFail: (json['rewind_seconds'] as num?)?.toInt() ?? 45, hint: json['hint']?.toString() ?? '', pedagogicalExplanation: json['explanation']?.toString() ?? '', options: parsedOptions, ); } } /// Model representing a single choice option in a checkpoint class SocraticOptionModel { final int id; final String text; final bool isCorrect; const SocraticOptionModel({ required this.id, required this.text, required this.isCorrect, }); factory SocraticOptionModel.fromJson(Map json) { return SocraticOptionModel( id: (json['id'] as num?)?.toInt() ?? 0, text: json['text']?.toString() ?? json['option_text']?.toString() ?? '', isCorrect: (json['is_correct'] as bool?) ?? false, ); } } /// Model for Lesson Playback Data (Streams, Versions, Chapters, Checkpoints) class LessonPlaybackData { final int lessonId; final String title; final int durationSeconds; final String videoUrl; final String storageType; final List availableVersions; final List checkpoints; final int? lastPositionSeconds; const LessonPlaybackData({ required this.lessonId, required this.title, required this.durationSeconds, required this.videoUrl, this.storageType = 'api_upload', this.availableVersions = const [], this.checkpoints = const [], this.lastPositionSeconds, }); factory LessonPlaybackData.fromJson(Map json) { final lesson = json['lesson'] is Map ? json['lesson'] as Map : {}; final playback = json['playback'] is Map ? json['playback'] as Map : {}; // Parse versions final List versions = []; if (json['available_versions'] is List) { for (var v in json['available_versions']) { if (v is Map) { versions.add(LessonVersionModel.fromJson(Map.from(v))); } } } // Parse checkpoints final List points = []; if (json['checkpoints'] is List) { for (var c in json['checkpoints']) { if (c is Map) { points.add(SocraticCheckpointModel.fromJson(Map.from(c))); } } } final vidUrl = _absolutePlaybackUrl( playback['hls_url']?.toString() ?? playback['stream_url']?.toString() ?? playback['video_url']?.toString() ?? '', ); final lastPos = (json['last_position_seconds'] ?? playback['last_position_seconds'] ?? json['progress']?['position_seconds']) as num?; return LessonPlaybackData( lessonId: (lesson['id'] as num?)?.toInt() ?? 0, title: lesson['title']?.toString() ?? 'الدرس التفاعلي', durationSeconds: (lesson['duration_seconds'] as num?)?.toInt() ?? 0, videoUrl: vidUrl, storageType: playback['storage_type']?.toString() ?? 'api_upload', availableVersions: versions, checkpoints: points, lastPositionSeconds: lastPos?.toInt(), ); } LessonPlaybackData copyWith({ int? lessonId, String? title, int? durationSeconds, String? videoUrl, String? storageType, List? availableVersions, List? checkpoints, int? lastPositionSeconds, }) { return LessonPlaybackData( lessonId: lessonId ?? this.lessonId, title: title ?? this.title, durationSeconds: durationSeconds ?? this.durationSeconds, videoUrl: videoUrl ?? this.videoUrl, storageType: storageType ?? this.storageType, availableVersions: availableVersions ?? this.availableVersions, checkpoints: checkpoints ?? this.checkpoints, lastPositionSeconds: lastPositionSeconds ?? this.lastPositionSeconds, ); } } /// Model for Teacher vs AI Lesson versions class LessonVersionModel { final int lessonId; final bool isAi; final String teacherName; final String schoolName; final String label; final bool isRecommended; final String videoUrl; const LessonVersionModel({ required this.lessonId, required this.isAi, required this.teacherName, required this.schoolName, required this.label, required this.isRecommended, required this.videoUrl, }); factory LessonVersionModel.fromJson(Map json) { final playback = json['playback'] is Map ? json['playback'] as Map : {}; final vidUrl = _absolutePlaybackUrl( playback['hls_url']?.toString() ?? playback['stream_url']?.toString() ?? playback['video_url']?.toString() ?? '', ); return LessonVersionModel( lessonId: (json['lesson_id'] as num?)?.toInt() ?? 0, isAi: (json['is_ai'] as bool?) ?? false, teacherName: json['teacher_name']?.toString() ?? '', schoolName: json['school_name']?.toString() ?? '', label: json['label']?.toString() ?? 'شرح معتمد', isRecommended: (json['is_recommended'] as bool?) ?? false, videoUrl: vidUrl, ); } }