<?php
/**
 * معايرة الدرجات باستخدام نظام ثوراندويك
 * Thorndike Grade Calibration System
 * 
 * نظام ثوراندويك يقوم بمعايرة الدرجات بناءً على التوزيع الطبيعي للدرجات
 * ويحافظ على نفس المتوسط مع تعديل الانحراف المعياري
 */

require_once __DIR__ . '/../config/config.php';

class ThorndikeCalibrator {
    private $db;
    
    public function __construct() {
        $this->db = Database::getInstance();
    }
    
    /**
     * حساب المتوسط الحسابي
     */
    private function calculateMean($scores) {
        if (empty($scores)) {
            return 0;
        }
        return array_sum($scores) / count($scores);
    }
    
    /**
     * حساب الانحراف المعياري
     */
    private function calculateStandardDeviation($scores, $mean) {
        if (empty($scores) || count($scores) < 2) {
            return 0;
        }
        
        $variance = 0;
        foreach ($scores as $score) {
            $variance += pow($score - $mean, 2);
        }
        
        return sqrt($variance / count($scores));
    }
    
    /**
     * معايرة الدرجات باستخدام نظام ثوراندويك
     * 
     * @param array $scores مصفوفة الدرجات الأصلية
     * @param float $target_mean المتوسط المستهدف (افتراضي: 70)
     * @param float $target_std الانحراف المعياري المستهدف (افتراضي: 12)
     * @return array مصفوفة الدرجات المعايرة
     */
    public function calibrateScores($scores, $target_mean = 70, $target_std = 12) {
        if (empty($scores)) {
            return [];
        }
        
        // حساب المتوسط والانحراف المعياري للدرجات الأصلية
        $original_mean = $this->calculateMean($scores);
        $original_std = $this->calculateStandardDeviation($scores, $original_mean);
        
        // إذا كان الانحراف المعياري صفر، لا يمكن المعايرة
        if ($original_std == 0) {
            return $scores; // إرجاع الدرجات الأصلية بدون تغيير
        }
        
        // حساب الدرجات المعايرة
        $calibrated_scores = [];
        foreach ($scores as $score) {
            // صيغة ثوراندويك: New_Score = Target_Mean + (Score - Original_Mean) * (Target_STD / Original_STD)
            $calibrated = $target_mean + ($score - $original_mean) * ($target_std / $original_std);
            
            // التأكد من أن الدرجة المعايرة بين 0 و 100
            $calibrated = max(0, min(100, $calibrated));
            
            $calibrated_scores[] = round($calibrated, 2);
        }
        
        return $calibrated_scores;
    }
    
    /**
     * معايرة درجات فصل دراسي معين
     * 
     * @param int $semester_id معرف الفصل الدراسي
     * @param int $course_id معرف المادة (اختياري - إذا كان null، يتم معايرة جميع المواد)
     * @param float $target_mean المتوسط المستهدف الافتراضي
     * @param float $target_std الانحراف المعياري المستهدف الافتراضي
     * @param array $course_settings إعدادات المواد: course_id => ['enabled' => bool, 'target_mean' => float, 'target_std' => float, 'min_grade' => float]
     * @param float $global_min_grade الحد الأدنى العام (إذا لم يتم تحديده لكل مادة)
     * @return array بيانات المعايرة
     */
    public function calibrateSemesterGrades($semester_id, $course_id = null, $target_mean = 70, $target_std = 12, $course_settings = [], $global_min_grade = 0) {
        // بناء الاستعلام حسب وجود معرف المادة
        if ($course_id) {
            $grades = $this->db->fetchAll(
                "SELECT g.id, g.student_id, g.course_id, g.marks, g.letter_grade, g.points
                 FROM grades g
                 WHERE g.semester_id = ? AND g.course_id = ?
                 ORDER BY g.marks DESC",
                [$semester_id, $course_id]
            );
        } else {
            $grades = $this->db->fetchAll(
                "SELECT g.id, g.student_id, g.course_id, g.marks, g.letter_grade, g.points
                 FROM grades g
                 WHERE g.semester_id = ?
                 ORDER BY g.course_id, g.marks DESC",
                [$semester_id]
            );
        }
        
        if (empty($grades)) {
            return [
                'calibrated_grades' => [],
                'statistics' => []
            ];
        }
        
        // تجميع الدرجات حسب المادة
        $grades_by_course = [];
        foreach ($grades as $grade) {
            $course_id_key = $grade['course_id'];
            if (!isset($grades_by_course[$course_id_key])) {
                $grades_by_course[$course_id_key] = [];
            }
            $grades_by_course[$course_id_key][] = $grade;
        }
        
        $calibrated_grades = [];
        $statistics = [];
        
        // معايرة كل مادة على حدة
        foreach ($grades_by_course as $course_id_key => $course_grades) {
            // التحقق من إعدادات المادة - وسط مستهدف وانحراف معياري وحد أدنى لكل مادة
            $course_setting = isset($course_settings[$course_id_key]) ? $course_settings[$course_id_key] : null;
            $is_enabled = $course_setting ? ($course_setting['enabled'] ?? true) : true;
            $course_target_mean = ($course_setting && isset($course_setting['target_mean']) && $course_setting['target_mean'] !== null && $course_setting['target_mean'] !== '') ? (float)$course_setting['target_mean'] : $target_mean;
            $course_target_std = ($course_setting && isset($course_setting['target_std']) && $course_setting['target_std'] !== null && $course_setting['target_std'] !== '') ? (float)$course_setting['target_std'] : $target_std;
            $min_grade = ($course_setting && isset($course_setting['min_grade']) && $course_setting['min_grade'] !== null && $course_setting['min_grade'] !== '') ? (float)$course_setting['min_grade'] : $global_min_grade;

            // إذا كانت المادة معطلة، استخدم الدرجات الأصلية
            if (!$is_enabled) {
                foreach ($course_grades as $grade) {
                    $calibrated_grades[] = [
                        'grade_id' => $grade['id'],
                        'student_id' => $grade['student_id'],
                        'course_id' => $grade['course_id'],
                        'original_marks' => $grade['marks'],
                        'calibrated_marks' => $grade['marks'],
                        'original_letter_grade' => $grade['letter_grade'],
                        'original_points' => $grade['points'],
                        'was_calibrated' => false,
                        'calibration_reason' => 'المادة غير مفعلة للمعايرة'
                    ];
                }
                $statistics[$course_id_key] = [
                    'original_mean' => 0,
                    'original_std' => 0,
                    'calibrated_mean' => 0,
                    'calibrated_std' => 0,
                    'target_mean' => $course_target_mean,
                    'target_std' => $course_target_std,
                    'can_calibrate' => false,
                    'calibration_reason' => 'المادة غير مفعلة للمعايرة',
                    'student_count' => count($course_grades)
                ];
                continue;
            }
            
            // تصفية الدرجات حسب الحد الأدنى
            $filtered_grades = [];
            foreach ($course_grades as $grade) {
                $marks_value = $grade['marks'];
                // إذا كانت الدرجة رقمية وتحقق الشرط
                if (is_numeric($marks_value)) {
                    if ((float)$marks_value >= $min_grade) {
                        $filtered_grades[] = $grade;
                    }
                } else {
                    // الدرجات النصية (بد، غش، إلخ) لا تدخل في المعايرة
                    $filtered_grades[] = $grade;
                }
            }
            
            // استخراج الدرجات الرقمية فقط للمعايرة
            $scores = [];
            $numeric_grades = [];
            foreach ($filtered_grades as $g) {
                if (is_numeric($g['marks'])) {
                    $scores[] = (float)$g['marks'];
                    $numeric_grades[] = $g;
                }
            }
            
            // حساب الإحصائيات الأصلية
            $original_mean = !empty($scores) ? $this->calculateMean($scores) : 0;
            $original_std = !empty($scores) ? $this->calculateStandardDeviation($scores, $original_mean) : 0;
            
            // التحقق من إمكانية المعايرة
            $can_calibrate = true;
            $calibration_reason = '';
            
            if (count($scores) < 2) {
                $can_calibrate = false;
                $calibration_reason = 'عدد الطلاب أقل من 2 (لا يمكن حساب الانحراف المعياري)';
            } elseif ($original_std == 0) {
                $can_calibrate = false;
                $calibration_reason = 'جميع الدرجات متساوية (الانحراف المعياري = 0)';
            } elseif ($min_grade > 0 && count($scores) < 2) {
                $can_calibrate = false;
                $calibration_reason = 'عدد الطلاب الذين درجاتهم أعلى من الحد الأدنى (' . $min_grade . ') أقل من 2';
            }
            
            // معايرة الدرجات باستخدام وسط وانحراف المعياري المستهدف لهذه المادة
            $calibrated_scores_map = [];
            if ($can_calibrate && !empty($scores)) {
                $calibrated_scores = $this->calibrateScores($scores, $course_target_mean, $course_target_std);
                // ربط الدرجات المعايرة بالطلاب
                foreach ($numeric_grades as $index => $grade) {
                    $calibrated_scores_map[$grade['id']] = $calibrated_scores[$index] ?? (float)$grade['marks'];
                }
            }
            
            // حساب الإحصائيات بعد المعايرة
            $calibrated_mean = $can_calibrate ? $this->calculateMean($calibrated_scores) : $original_mean;
            $calibrated_std = $can_calibrate ? $this->calculateStandardDeviation($calibrated_scores, $calibrated_mean) : $original_std;
            
            // ربط الدرجات المعايرة بالطلاب
            foreach ($course_grades as $grade) {
                $grade_id = $grade['id'];
                $marks_value = $grade['marks'];
                $is_numeric_grade = is_numeric($marks_value);
                
                if ($is_numeric_grade && isset($calibrated_scores_map[$grade_id])) {
                    // درجة رقمية تمت معايرتها
                    $calibrated_marks = $calibrated_scores_map[$grade_id];
                } elseif ($is_numeric_grade && (float)$marks_value < $min_grade) {
                    // درجة رقمية أقل من الحد الأدنى - لا تُعاير
                    $calibrated_marks = $marks_value;
                    $can_calibrate = false;
                    $calibration_reason = 'الدرجة (' . $marks_value . ') أقل من الحد الأدنى (' . $min_grade . ')';
                } else {
                    // درجة نصية أو لم تُعاير - استخدم الأصلية
                    $calibrated_marks = $marks_value;
                }
                
                $calibrated_grades[] = [
                    'grade_id' => $grade_id,
                    'student_id' => $grade['student_id'],
                    'course_id' => $grade['course_id'],
                    'original_marks' => $marks_value,
                    'calibrated_marks' => $calibrated_marks,
                    'original_letter_grade' => $grade['letter_grade'],
                    'original_points' => $grade['points'],
                    'was_calibrated' => $can_calibrate && $is_numeric_grade && (float)$marks_value >= $min_grade,
                    'calibration_reason' => $calibration_reason
                ];
            }
            
            // حساب الإحصائيات بعد المعايرة
            $calibrated_mean = $can_calibrate && !empty($calibrated_scores_map) ? $this->calculateMean(array_values($calibrated_scores_map)) : $original_mean;
            $calibrated_std = $can_calibrate && !empty($calibrated_scores_map) ? $this->calculateStandardDeviation(array_values($calibrated_scores_map), $calibrated_mean) : $original_std;
            
            // حفظ الإحصائيات
            $statistics[$course_id_key] = [
                'original_mean' => round($original_mean, 2),
                'original_std' => round($original_std, 2),
                'calibrated_mean' => round($calibrated_mean, 2),
                'calibrated_std' => round($calibrated_std, 2),
                'target_mean' => $course_target_mean,
                'target_std' => $course_target_std,
                'can_calibrate' => $can_calibrate,
                'calibration_reason' => $calibration_reason,
                'student_count' => count($scores),
                'min_grade' => $min_grade,
                'enabled' => $is_enabled
            ];
        }
        
        return [
            'calibrated_grades' => $calibrated_grades,
            'statistics' => $statistics
        ];
    }
    
    /**
     * حساب التقدير الحرفي والنقاط من الدرجة المعايرة
     * التقدير الجديد: A (80-100), B (70-79), C (60-69), D (50-59), F (<50)
     */
    public function calculateGradeFromMarks($marks) {
        $marks = (float)$marks;
        
        if ($marks >= 80 && $marks <= 100) {
            return ['letter' => 'A', 'points' => 4.0];
        } elseif ($marks >= 70 && $marks <= 79) {
            return ['letter' => 'B', 'points' => 3.0];
        } elseif ($marks >= 60 && $marks <= 69) {
            return ['letter' => 'C', 'points' => 2.0];
        } elseif ($marks >= 50 && $marks <= 59) {
            return ['letter' => 'D', 'points' => 1.0];
        } else {
            return ['letter' => 'F', 'points' => 0.0];
        }
    }
    
    /**
     * حفظ الدرجات المعايرة في قاعدة البيانات (جدول منفصل)
     * 
     * @param int $semester_id معرف الفصل الدراسي
     * @param float $target_mean المتوسط المستهدف الافتراضي
     * @param float $target_std الانحراف المعياري المستهدف الافتراضي
     * @param int $user_id معرف المستخدم الذي قام بالمعايرة
     * @param array $course_settings إعدادات كل مادة: course_id => ['enabled', 'target_mean', 'target_std', 'min_grade']
     * @param float $global_min_grade الحد الأدنى العام
     * @return array نتيجة العملية
     */
    public function saveCalibratedGrades($semester_id, $target_mean, $target_std, $user_id, $course_settings = [], $global_min_grade = 0) {
        try {
            // بدء معاملة قاعدة البيانات
            $this->db->getConnection()->beginTransaction();
            
            // حذف المعايرة السابقة إن وجدت
            $this->db->query("DELETE FROM calibrated_grades WHERE semester_id = ?", [$semester_id]);
            
            // معايرة الدرجات (مع إعدادات كل مادة)
            $calibration_data = $this->calibrateSemesterGrades($semester_id, null, $target_mean, $target_std, $course_settings, $global_min_grade);
            
            if (empty($calibration_data['calibrated_grades'])) {
                $this->db->getConnection()->rollBack();
                return [
                    'success' => false,
                    'message' => 'لا توجد درجات لمعايرتها'
                ];
            }
            
            $inserted_count = 0;
            
            // حفظ الدرجات المعايرة في الجدول المنفصل
            foreach ($calibration_data['calibrated_grades'] as $cal_grade) {
                $grade_info = $this->calculateGradeFromMarks($cal_grade['calibrated_marks']);
                
                // جلب معلومات إضافية من الدرجة الأصلية
                $original_grade = $this->db->fetchOne(
                    "SELECT student_id, course_id, marks, letter_grade, points 
                     FROM grades WHERE id = ?",
                    [$cal_grade['grade_id']]
                );
                
                if ($original_grade) {
                    $cid = $original_grade['course_id'];
                    $c_mean = isset($calibration_data['statistics'][$cid]['target_mean']) ? $calibration_data['statistics'][$cid]['target_mean'] : $target_mean;
                    $c_std = isset($calibration_data['statistics'][$cid]['target_std']) ? $calibration_data['statistics'][$cid]['target_std'] : $target_std;
                    $this->db->query(
                        "INSERT INTO calibrated_grades 
                         (grade_id, student_id, course_id, semester_id, 
                          original_marks, original_letter_grade, original_points,
                          calibrated_marks, calibrated_letter_grade, calibrated_points,
                          calibration_target_mean, calibration_target_std, calibrated_by)
                         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                        [
                            $cal_grade['grade_id'],
                            $original_grade['student_id'],
                            $original_grade['course_id'],
                            $semester_id,
                            $original_grade['marks'],
                            $original_grade['letter_grade'],
                            $original_grade['points'],
                            $cal_grade['calibrated_marks'],
                            $grade_info['letter'],
                            $grade_info['points'],
                            $c_mean,
                            $c_std,
                            $user_id
                        ]
                    );
                    
                    $inserted_count++;
                }
            }
            
            // حفظ معلومات المعايرة في جدول semester_calibrations
            $this->db->query(
                "INSERT INTO semester_calibrations (semester_id, target_mean, target_std, calibrated_by)
                 VALUES (?, ?, ?, ?)
                 ON DUPLICATE KEY UPDATE
                 target_mean = VALUES(target_mean),
                 target_std = VALUES(target_std),
                 calibrated_by = VALUES(calibrated_by),
                 calibrated_at = CURRENT_TIMESTAMP",
                [$semester_id, $target_mean, $target_std, $user_id]
            );
            
            // إتمام المعاملة
            $this->db->getConnection()->commit();
            
            return [
                'success' => true,
                'message' => "تم حفظ الدرجات المعايرة بنجاح ($inserted_count درجة)",
                'updated_count' => $inserted_count,
                'statistics' => $calibration_data['statistics']
            ];
            
        } catch (Exception $e) {
            $this->db->getConnection()->rollBack();
            return [
                'success' => false,
                'message' => 'حدث خطأ أثناء حفظ الدرجات المعايرة: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * جلب الدرجات المعايرة المحفوظة من قاعدة البيانات (من الجدول المنفصل)
     * 
     * @param int $semester_id معرف الفصل الدراسي
     * @return array الدرجات المعايرة المحفوظة
     */
    public function getSavedCalibratedGrades($semester_id) {
        // محاولة جلب من الجدول المنفصل أولاً
        $grades = $this->db->fetchAll(
            "SELECT cg.grade_id as id, cg.student_id, cg.course_id, 
                    cg.original_marks, cg.original_letter_grade, cg.original_points,
                    cg.calibrated_marks, cg.calibrated_letter_grade, cg.calibrated_points,
                    cg.calibration_target_mean, cg.calibration_target_std, cg.calibrated_at
             FROM calibrated_grades cg
             WHERE cg.semester_id = ?
             ORDER BY cg.course_id, cg.calibrated_marks DESC",
            [$semester_id]
        );
        
        // إذا لم توجد في الجدول المنفصل، جلب من جدول grades (للتوافق مع البيانات القديمة)
        if (empty($grades)) {
            $grades = $this->db->fetchAll(
                "SELECT g.id, g.student_id, g.course_id, g.marks as original_marks,
                        g.letter_grade as original_letter_grade, g.points as original_points,
                        g.calibrated_marks, g.calibrated_letter_grade, g.calibrated_points,
                        g.calibration_target_mean, g.calibration_target_std, g.calibrated_at
                 FROM grades g
                 WHERE g.semester_id = ? AND g.calibrated_marks IS NOT NULL
                 ORDER BY g.course_id, g.calibrated_marks DESC",
                [$semester_id]
            );
        }
        
        return $grades;
    }
    
    /**
     * التحقق من وجود درجات معايرة محفوظة للفصل الدراسي
     * 
     * @param int $semester_id معرف الفصل الدراسي
     * @return bool
     */
    public function hasCalibratedGrades($semester_id) {
        $calibration = $this->db->fetchOne(
            "SELECT id FROM semester_calibrations WHERE semester_id = ?",
            [$semester_id]
        );
        
        return $calibration !== null;
    }
    
    /**
     * جلب معلومات المعايرة المحفوظة للفصل الدراسي
     * 
     * @param int $semester_id معرف الفصل الدراسي
     * @return array|null
     */
    public function getCalibrationInfo($semester_id) {
        return $this->db->fetchOne(
            "SELECT sc.*, u.full_name_ar as calibrated_by_name
             FROM semester_calibrations sc
             LEFT JOIN users u ON sc.calibrated_by = u.id
             WHERE sc.semester_id = ?",
            [$semester_id]
        );
    }
}

