import 'package:flutter_bloc/flutter_bloc.dart'; import '../../core/utils/app_logger.dart'; import '../../data/models/socratic_checkpoint_model.dart'; import '../../data/models/subject_model.dart'; import '../../data/repositories/curriculum_repository.dart'; abstract class VideoPlaybackState {} class VideoPlaybackInitial extends VideoPlaybackState {} class VideoPlaybackLoading extends VideoPlaybackState {} class VideoPlaybackReady extends VideoPlaybackState { final LessonPlaybackData playbackData; final CurriculumLessonItemModel? lessonItem; final SubjectModel? subject; final int currentPositionSeconds; final bool isPlaying; final SocraticCheckpointModel? activeCheckpoint; final bool isCheckpointPassed; final String? remediationNotice; final double readinessBonusAdded; final Set passedCheckpointIds; VideoPlaybackReady({ required this.playbackData, this.lessonItem, this.subject, this.currentPositionSeconds = 0, this.isPlaying = true, this.activeCheckpoint, this.isCheckpointPassed = false, this.remediationNotice, this.readinessBonusAdded = 0.0, this.passedCheckpointIds = const {}, }); VideoPlaybackReady copyWith({ LessonPlaybackData? playbackData, CurriculumLessonItemModel? lessonItem, SubjectModel? subject, int? currentPositionSeconds, bool? isPlaying, SocraticCheckpointModel? activeCheckpoint, bool? isCheckpointPassed, String? remediationNotice, double? readinessBonusAdded, Set? passedCheckpointIds, bool clearActiveCheckpoint = false, }) { return VideoPlaybackReady( playbackData: playbackData ?? this.playbackData, lessonItem: lessonItem ?? this.lessonItem, subject: subject ?? this.subject, currentPositionSeconds: currentPositionSeconds ?? this.currentPositionSeconds, isPlaying: isPlaying ?? this.isPlaying, activeCheckpoint: clearActiveCheckpoint ? null : (activeCheckpoint ?? this.activeCheckpoint), isCheckpointPassed: isCheckpointPassed ?? this.isCheckpointPassed, remediationNotice: remediationNotice, readinessBonusAdded: readinessBonusAdded ?? this.readinessBonusAdded, passedCheckpointIds: passedCheckpointIds ?? this.passedCheckpointIds, ); } } class VideoPlaybackError extends VideoPlaybackState { final String message; VideoPlaybackError(this.message); } class VideoPlaybackCubit extends Cubit { final CurriculumRepository _repo; VideoPlaybackCubit({CurriculumRepository? repo}) : _repo = repo ?? CurriculumRepository(), super(VideoPlaybackInitial()); Future loadLesson(CurriculumLessonItemModel lesson, {SubjectModel? subject}) async { AppLogger.log('Loading Socratic playback for lesson: ${lesson.title}', tag: 'VIDEO_CUBIT'); emit(VideoPlaybackLoading()); try { final playback = await _repo.getLessonPlayback(lesson.id, subjectId: subject?.id); emit(VideoPlaybackReady( playbackData: playback, lessonItem: lesson, subject: subject, currentPositionSeconds: 0, isPlaying: true, )); } catch (e) { AppLogger.log('Playback API notice for ${lesson.title}: $e (Generating dynamic Socratic session)', tag: 'VIDEO_CUBIT'); // Create dynamic fallback Socratic session with lesson checkpoints final fallbackPlayback = LessonPlaybackData( lessonId: 1, title: lesson.title, durationSeconds: lesson.durationSeconds > 0 ? lesson.durationSeconds : 600, videoUrl: 'https://saqel.intaleqapp.com/api/videos/stream/demo-vector-lesson', availableVersions: const [ LessonVersionModel( lessonId: 1, isAi: true, teacherName: 'المعلم الافتراضي بالذكاء الاصطناعي', schoolName: 'وزارة التربية والتعليم', label: 'شرح المنهاج المعتمد 🤖', isRecommended: true, videoUrl: '', ), ], checkpoints: [ SocraticCheckpointModel( id: 1, questionText: lesson.outcomes.isNotEmpty ? 'وفقاً لنتاجات هذا الدرس (${lesson.outcomes.first}): ما هو المفهوم الجوهري الواجب إتقانه هنا؟' : 'ما هو المبدأ الأساسي المشروح في هذا المبحث؟', timestampSeconds: 15, rewindSecondsOnFail: 20, hint: 'راجع نتاجات التعلم الموضحة أسفل الفيديو بدقة.', pedagogicalExplanation: 'الإتقان السقراطي يتطلب الفهم المفاهيمي العميق قبل الانتقال للمسائل الحسابية.', options: const [ SocraticOptionModel(id: 1, text: 'الفهم المنهجي والتطبيق المباشر للقواعد', isCorrect: true), SocraticOptionModel(id: 2, text: 'الحفظ المجرد دون فهم', isCorrect: false), SocraticOptionModel(id: 3, text: 'تخطي المفهوم', isCorrect: false), ], ), ], ); emit(VideoPlaybackReady( playbackData: fallbackPlayback, lessonItem: lesson, subject: subject, currentPositionSeconds: 0, isPlaying: true, )); } } void updatePosition(int seconds) { final currentState = state; if (currentState is VideoPlaybackReady && currentState.activeCheckpoint == null) { // Check if this second triggers a Socratic checkpoint for (var cp in currentState.playbackData.checkpoints) { if (!currentState.passedCheckpointIds.contains(cp.id) && seconds >= cp.timestampSeconds && seconds <= cp.timestampSeconds + 2) { AppLogger.log('🚨 Socratic Checkpoint Triggered! (${cp.questionText})', tag: 'SOCRATIC_ENGINE'); emit(currentState.copyWith( currentPositionSeconds: seconds, isPlaying: false, // Freeze video playback activeCheckpoint: cp, )); return; } } emit(currentState.copyWith(currentPositionSeconds: seconds)); } } void togglePlayPause() { final currentState = state; if (currentState is VideoPlaybackReady && currentState.activeCheckpoint == null) { emit(currentState.copyWith(isPlaying: !currentState.isPlaying)); } } void seekTo(int seconds) { final currentState = state; if (currentState is VideoPlaybackReady) { emit(currentState.copyWith(currentPositionSeconds: seconds)); } } /// Submit Socratic Checkpoint Answer bool submitCheckpointAnswer(SocraticOptionModel selectedOption) { final currentState = state; if (currentState is VideoPlaybackReady && currentState.activeCheckpoint != null) { final cp = currentState.activeCheckpoint!; if (selectedOption.isCorrect) { // Correct Answer -> Reward readiness score (+0.5%) & resume video AppLogger.log('✅ Correct answer! Rewarding +0.5% readiness.', tag: 'SOCRATIC_ENGINE'); final updatedPassed = Set.from(currentState.passedCheckpointIds)..add(cp.id); emit(currentState.copyWith( clearActiveCheckpoint: true, isPlaying: true, readinessBonusAdded: currentState.readinessBonusAdded + 0.5, passedCheckpointIds: updatedPassed, remediationNotice: 'إجابة نموذجية ممتازة! تم تعزيز مؤشر الجاهزية (+0.5%) 🚀', )); return true; } else { // Wrong Answer -> Socratic Productive Struggle: Rewind video by N seconds final rewindTo = (currentState.currentPositionSeconds - cp.rewindSecondsOnFail).clamp(0, currentState.playbackData.durationSeconds); AppLogger.log('❌ Incorrect answer. Socratic remediation: Rewinding to ${rewindTo}s.', tag: 'SOCRATIC_ENGINE'); emit(currentState.copyWith( clearActiveCheckpoint: true, currentPositionSeconds: rewindTo, isPlaying: true, remediationNotice: 'تعثرت في هذا المفهوم. تم إرجاع الفيديو ${cp.rewindSecondsOnFail} ثانية لإعادة الاستماع بتركيز 🔄', )); return false; } } return false; } }