user_id; $teacher = Database::selectOne("SELECT id, uuid, full_name, specialization, bio, is_marketplace_public FROM teachers WHERE id = ? LIMIT 1", [$teacherId]); if (!$teacher) { // Auto create teacher record if not exists $tUuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); $tId = Database::insert( "INSERT INTO teachers (uuid, full_name, specialization, bio, is_marketplace_public) VALUES (?, 'الأستاذ المعتمد', 'الرياضيات العلمي', 'معلم معتمد في منصة صَقِل', 1)", [$tUuid] ); $teacher = [ 'id' => $tId, 'uuid' => $tUuid, 'full_name' => 'الأستاذ المعتمد', 'specialization' => 'الرياضيات العلمي', 'bio' => 'معلم معتمد في منصة صَقِل' ]; } $rawName = (string)($teacher['full_name'] ?? ''); $decryptedName = Security::decrypt($rawName) ?: $rawName; if (empty($decryptedName)) { $decryptedName = 'الأستاذ المعتمد'; } $response->json([ 'status' => 'success', 'is_completed' => true, 'user' => [ 'uuid' => $teacher['uuid'], 'full_name' => $decryptedName, 'role' => 'teacher', 'status' => 'active', ], 'profile' => [ 'specialization' => $teacher['specialization'] ?? 'الرياضيات العلمي', 'bio' => $teacher['bio'] ?? 'معلم معتمد في منصة صَقِل' ] ]); } /** * Complete or Update Teacher Onboarding Profile (Password, Name, Bio, Grades, Subjects) * POST /api/teacher/profile/setup */ public function setupProfile(Request $request, Response $response): void { $userId = $request->user_id; $body = $request->getBody(); $validator = new Validator(); $isValid = $validator->validate($body, [ 'full_name' => 'required', 'specialization' => 'required', 'password' => 'required|min:8', ]); if (!$isValid) { $response->status(400)->json([ 'status' => 'error', 'message' => 'بيانات الملف الشخصي غير مكتملة أو كلمة المرور قصيرة (أقل من 8 خانات)', 'errors' => $validator->getErrors() ]); return; } $fullName = trim((string)$body['full_name']); $specialization = trim((string)$body['specialization']); $bio = trim((string)($body['bio'] ?? '')); $gradeLevels = is_array($body['grade_levels'] ?? null) ? implode(',', $body['grade_levels']) : (string)($body['grade_levels'] ?? 'tawjihi_2008'); $passwordHash = Security::hashPassword((string)$body['password']); // 1. Update User table $encryptedName = Security::encrypt($fullName); Database::query( "UPDATE users SET full_name = ?, password_hash = ?, grade_level = ?, status = 'active' WHERE id = ?", [$encryptedName, $passwordHash, $gradeLevels, $userId] ); // 2. Upsert Teacher Profile $existingProfile = Database::selectOne("SELECT id FROM teacher_profiles WHERE user_id = ? LIMIT 1", [$userId]); if ($existingProfile) { Database::query( "UPDATE teacher_profiles SET bio = ?, specialization = ? WHERE user_id = ?", [$bio, $specialization, $userId] ); } else { Database::insert( "INSERT INTO teacher_profiles (user_id, bio, specialization, revenue_share_pct, contract_type) VALUES (?, ?, ?, 50.00, 'exclusive')", [$userId, $bio, $specialization] ); } $response->json([ 'status' => 'success', 'message' => 'تم توثيق بيانات المعلم وإعداد كلمة المرور بنجاح!', 'data' => [ 'full_name' => $fullName, 'specialization' => $specialization, 'grade_levels' => $gradeLevels ] ]); } /** * Teacher Dashboard Statistics * GET /api/teacher/dashboard */ public function getDashboard(Request $request, Response $response): void { $userId = $request->user_id; // Total Courses $coursesCount = (int)Database::selectOne("SELECT COUNT(*) as total FROM courses WHERE teacher_id = ?", [$userId])['total']; // Total Lessons $lessonsCount = (int)Database::selectOne( "SELECT COUNT(l.id) as total FROM lessons l JOIN courses c ON l.course_id = c.id WHERE c.teacher_id = ?", [$userId] )['total']; // Total Unique Active Students $studentsCount = (int)Database::selectOne( "SELECT COUNT(DISTINCT lp.user_id) as total FROM lesson_progress lp JOIN lessons l ON lp.lesson_id = l.id JOIN courses c ON l.course_id = c.id WHERE c.teacher_id = ?", [$userId] )['total']; $response->json([ 'status' => 'success', 'data' => [ 'courses_count' => $coursesCount, 'lessons_count' => $lessonsCount, 'students_count' => $studentsCount, 'rating' => 4.9, 'completion_avg' => 87.5 ] ]); } /** * List Teacher's Courses * GET /api/teacher/courses */ public function getCourses(Request $request, Response $response): void { CurriculumService::ensureSchema(); $userId = (int)$request->user_id; $courses = Database::select( "SELECT c.id, c.uuid, c.title, c.description, c.semester, c.price_jod, c.is_published, c.created_at, COUNT(l.id) as lessons_total FROM courses c LEFT JOIN lessons l ON l.course_id = c.id WHERE c.teacher_id = ? GROUP BY c.id ORDER BY c.id ASC", [$userId] ); if (empty($courses)) { // Auto-seed default courses for this teacher $uuid1 = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); $uuid2 = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); Database::query( "INSERT INTO courses (uuid, teacher_id, subject_id, title, description, semester, price_jod, is_published) VALUES (?, ?, 1, 'الرياضيات العلمي — توجيهي 2008 (المستوى الثالث)', 'شرح المنهاج الوزاري الجديد مع التحليل الجنائي وتطبيقات التفاضل', 'first', 35.00, 1), (?, ?, 2, 'الثقافة العسكرية والتربية الوطنية (المستوى الموحد)', 'منهاج مدارس الثقافة العسكرية المعتمد مع بنك الأسئلة والخرائط الذهنية', 'first', 25.00, 1)", [$uuid1, $userId, $uuid2, $userId] ); $courses = Database::select( "SELECT c.id, c.uuid, c.title, c.description, c.semester, c.price_jod, c.is_published, c.created_at, COUNT(l.id) as lessons_total FROM courses c LEFT JOIN lessons l ON l.course_id = c.id WHERE c.teacher_id = ? GROUP BY c.id ORDER BY c.id ASC", [$userId] ); } $response->json([ 'status' => 'success', 'data' => $courses ]); } /** * Add New Course * POST /api/teacher/courses */ public function addCourse(Request $request, Response $response): void { $body = $request->getBody(); $title = trim((string)($body['title'] ?? '')); $description = trim((string)($body['description'] ?? '')); $subjectId = (int)($body['subject_id'] ?? 1); $semester = (string)($body['semester'] ?? 'first'); $price = (float)($body['price_jod'] ?? 35.00); if (empty($title)) { $response->status(400)->json([ 'status' => 'error', 'message' => 'عنوان الدورة مطلوب' ]); return; } $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) ); $courseId = Database::insert( "INSERT INTO courses (uuid, subject_id, teacher_id, title, description, semester, price_jod, is_published) VALUES (?, ?, ?, ?, ?, ?, ?, 1)", [$uuid, $subjectId, $request->user_id, $title, $description, $semester, $price] ); $response->status(201)->json([ 'status' => 'success', 'message' => 'تم إنشاء الدورة بنجاح', 'data' => [ 'course_id' => $courseId, 'uuid' => $uuid ] ]); } /** * Add New Lesson to Course * POST /api/teacher/lessons */ public function addLesson(Request $request, Response $response): void { $body = $request->getBody(); $courseId = (int)($body['course_id'] ?? 0); $title = trim((string)($body['title'] ?? '')); $bunnyVideoId = trim((string)($body['bunny_video_id'] ?? '')); $duration = (int)($body['duration_seconds'] ?? 0); $sequenceOrder = (int)($body['sequence_order'] ?? 1); if (!$courseId || empty($title) || empty($bunnyVideoId)) { $response->status(400)->json([ 'status' => 'error', 'message' => 'معرف الدورة، عنوان الدرس، ومعرف فيديو Bunny مطلوبين' ]); return; } // Verify Course Ownership $course = Database::selectOne("SELECT id, teacher_id FROM courses WHERE id = ? LIMIT 1", [$courseId]); if (!$course || ($course['teacher_id'] != $request->user_id && $request->role !== 'super_admin')) { $response->status(403)->json([ 'status' => 'error', 'message' => 'غير مصرح: لا تملك هذه الدورة' ]); return; } $lessonId = Database::insert( "INSERT INTO lessons (course_id, title, sequence_order, bunny_video_id, duration_seconds, is_free_preview) VALUES (?, ?, ?, ?, ?, 0)", [$courseId, $title, $sequenceOrder, $bunnyVideoId, $duration] ); $response->status(201)->json([ 'status' => 'success', 'message' => 'تمت إضافة الدرس بنجاح', 'data' => [ 'lesson_id' => $lessonId ] ]); } /** * Get All Teachers for Marketplace Discovery * GET /api/teachers */ public function getMarketplaceTeachers(Request $request, Response $response): void { $queryParams = $request->getQueryParams(); $sortBy = $queryParams['sort'] ?? 'merit'; $teachers = \App\Services\TeacherRatingService::getTeachersMarketplace($sortBy); $response->json([ 'status' => 'success', 'data' => $teachers ]); } /** * Get Teacher Metrics Breakdown & Live Reputation * GET /api/teachers/{id}/metrics */ public function getTeacherMetrics(Request $request, Response $response): void { $teacherId = (int)$request->getParam('id'); if (!$teacherId) { $response->status(400)->json(['status' => 'error', 'message' => 'معرف المعلم غير صالح']); return; } $metrics = \App\Services\TeacherRatingService::recalculateTeacherMetrics($teacherId); $reviews = Database::select( "SELECT tr.*, u.full_name as student_name FROM teacher_reviews tr JOIN users u ON tr.student_id = u.id WHERE tr.teacher_id = ? AND tr.is_flagged_anomaly = 0 ORDER BY tr.created_at DESC LIMIT 20", [$teacherId] ); $response->json([ 'status' => 'success', 'data' => [ 'metrics' => $metrics, 'reviews' => $reviews ] ]); } /** * Submit Student Review with Anti-Brigade & Engagement Weighting * POST /api/teachers/{id}/reviews */ public function submitReview(Request $request, Response $response): void { $teacherId = (int)$request->getParam('id'); $studentId = $request->user_id; $body = $request->getBody(); $ratingOverall = (float)($body['rating_overall'] ?? 5.0); $clarity = (int)($body['rating_clarity'] ?? 5); $speed = (int)($body['rating_response_speed'] ?? 5); $socratic = (int)($body['rating_socratic_interaction'] ?? 5); $text = trim((string)($body['review_text'] ?? '')); $courseId = !empty($body['course_id']) ? (int)$body['course_id'] : null; $lessonId = !empty($body['lesson_id']) ? (int)$body['lesson_id'] : null; if (!$teacherId || !$studentId) { $response->status(400)->json(['status' => 'error', 'message' => 'معرف المعلم والطالب مطلوبان']); return; } $res = \App\Services\TeacherRatingService::submitReview( $teacherId, $studentId, $ratingOverall, $clarity, $speed, $socratic, $text, $courseId, $lessonId ); $response->json([ 'status' => 'success', 'message' => 'تم تسجيل تقييمك واحتساب وزنه المعرفي بنجاح', 'data' => $res ]); } /** * Get Logged-in Teacher's own reputation score * GET /api/teacher/reputation */ public function getMyReputation(Request $request, Response $response): void { $teacherId = $request->user_id; $metrics = \App\Services\TeacherRatingService::recalculateTeacherMetrics($teacherId); $response->json([ 'status' => 'success', 'data' => $metrics ]); } }