<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

set_time_limit(0); 


require_once '/home/rejoicebilldesk/public_html/rjllearning_development/resources/base_path_include.php';
require_once $base_path_to_learning_resources  . "includes/connect.php"; // use require for mandatory files
require_once $base_path_to_learning_resources . 'lib/function.php';
require_once $base_path_to_learning_resources . 'lib/validate_api_signature.php';

//valdation file
require_once $base_path_to_learning_resources.'lib/validate_function.php';
require_once $base_path_to_learning.'user_management/authenticate.php';
require_once $base_path_to_learning.'functions/common_functions.php';

//db access function
require_once $base_path_to_learning_resources.'includes/specific_db_config.php';

$postdata = json_decode(file_get_contents("php://input"), true);

checkIfNoPhpContentsReceive();

$required_fields = [ 'api_signature', 'limit', 'function_type', 'jwt_token'];

foreach ($required_fields as $field) {
    if (!isset($postdata[$field]) || !checkIfIssetAndNotEmpty($postdata[$field])) {
        errorreturn("All * fields are required.");
    }
}

validateApiSignature($postdata['api_signature']);

$jwttoken = $postdata['jwt_token'];
$jwt_validate = authenticate_jwt($jwttoken);

// Extract common JWT data
$userid       = $jwt_validate->data->id;
$workspace_id = $jwt_validate->data->workspace_id;
$username     = $jwt_validate->data->username;
$role         = $jwt_validate->data->role;

// Decide where to get institution_id and branch_id based on the user's role
if ($role === 'teacher' || $role === 'principal') {
    // For teachers and principals, use values from the JWT token
    $institution_id = $jwt_validate->data->institution_id;
    $branch_id      = $jwt_validate->data->branch_id;
} else {
    // For other roles, use the values provided by the frontend
    $institution_id = trim($postdata['institution_id']);
    $branch_id      = trim($postdata['branch_id']);
}


$specific_db_name = validateUserAccess($conn_rjllearning, $userid, $role, $workspace_id,['superadmin','admin','principal'],null);
if (!$specific_db_name) {
    errorreturn('Access denied.');
} else {
    $conn = dynamicDbConnection($specific_db_name);
}



function getAvailableSlots($institution_id, $branch_id, $faculty_id, $day, $time_slots, $class_id, $section_id, $conn, $limit = 10) {
    try {
        if (!is_array($time_slots) || empty($time_slots)) {
            errorreturn(["message" => "Invalid or empty time slots."]);
        }

        // Get faculty name
        $faculty_name = getFacultyInfo($conn, $institution_id, $faculty_id)['name'];

        // Fetch occupied slots from timetable
        $query = "SELECT id, period_start, end_time, faculty_id, class_id, section_id FROM timetable 
                  WHERE institution_id = ? AND branch_id = ? AND day = ?";
        $stmt = $conn->prepare($query);
        $stmt->execute([$institution_id, $branch_id, $day]);
        $occupiedSlots = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (count($time_slots) > 20) {
            errorreturn(["message" => "Unreasonable request: More than 20 classes per week is not allowed."]);
        }

        foreach ($time_slots as $slot) {
            foreach ($occupiedSlots as $occupied) {
                // Get the conflicting faculty name
                $conflictingFacultyName = getFacultyInfo($conn, $institution_id, $occupied['faculty_id'])['name'];

                // Check if the same faculty is already scheduled at this time
                if (
                    $occupied['faculty_id'] == $faculty_id && 
                    (
                        ($slot['start_time'] >= $occupied['period_start'] && $slot['start_time'] < $occupied['end_time']) || 
                        ($slot['end_time'] > $occupied['period_start'] && $slot['end_time'] <= $occupied['end_time'])
                    )
                ) {
                    errorreturn([
                        "message" => "Faculty Conflict: $faculty_name is already assigned from " . 
                                     $occupied['period_start'] . " - " . $occupied['end_time'] . "."
                    ]);
                }

                // Check if another faculty is already assigned to this class and section at the same time
                if (
                    $occupied['class_id'] == $class_id && 
                    $occupied['section_id'] == $section_id && 
                    $occupied['faculty_id'] != $faculty_id && 
                    (
                        ($slot['start_time'] >= $occupied['period_start'] && $slot['start_time'] < $occupied['end_time']) || 
                        ($slot['end_time'] > $occupied['period_start'] && $slot['end_time'] <= $occupied['end_time'])
                    )
                ) {
                    errorreturn([
                        "message" => "Class Conflict: Another faculty ($conflictingFacultyName) is already assigned to Class $class_id, Section $section_id from " . 
                                     $occupied['period_start'] . " - " . $occupied['end_time'] . "."
                    ]);
                }
            }
        }

        return array_slice($time_slots, 0, $limit);
    } catch (Exception $e) {
        errorreturn(["message" => $e->getMessage()]);
    }
}


function isTeacherAvailable($teacher_id, $day, $timeSlot, $conn, $avoid_consecutive) {
    static $cache = [];
    $cacheKey = "{$teacher_id}_{$day}";
    if (!isset($cache[$cacheKey])) {
        $query = "SELECT period_start, end_time FROM timetable WHERE faculty_id = ? AND day = ?";
        $stmt = $conn->prepare($query);
        $stmt->execute([$teacher_id, $day]);
        $cache[$cacheKey] = $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    $timeSlot = strtotime($timeSlot);
    foreach ($cache[$cacheKey] as $occupied) {
        $start_time = strtotime($occupied['period_start']);
        $end_time = strtotime($occupied['end_time']);
        if (
            ($timeSlot >= $start_time && $timeSlot < $end_time) || 
            ($avoid_consecutive && abs($timeSlot - $end_time) < 60) // Avoid consecutive periods
        ) {
            return false;
        }
    }
    return true;
}


function distributeLessonsFairly($facultySubjects, $days_of_work) {
    $distribution = [];

    // Initialize days with empty arrays
    foreach ($days_of_work as $day) {
        $distribution[$day] = [];
    }

    // Create a list of subjects maintaining their classes per week count
    $subjectsQueue = [];
    foreach ($facultySubjects as $subject) {
        for ($i = 0; $i < $subject['classesPerWeek']; $i++) {
            $subjectsQueue[] = $subject;
        }
    }

    shuffle($subjectsQueue); // Shuffle before distribution

    $dayIndex = 0;
    $maxIterations = count($subjectsQueue) * 2; // Prevent infinite loop
    $iterations = 0;

    while (!empty($subjectsQueue) && $iterations < $maxIterations) {
        $iterations++; // Track iterations

        $currentDay = $days_of_work[$dayIndex % count($days_of_work)];
        $prevSubject = end($distribution[$currentDay])['subject_id'] ?? null;

        foreach (array_keys($subjectsQueue) as $key) {
            if ($subjectsQueue[$key]['subject_id'] !== $prevSubject) {
                $distribution[$currentDay][] = $subjectsQueue[$key];
                unset($subjectsQueue[$key]); // Remove assigned subject
                $subjectsQueue = array_values($subjectsQueue); // Re-index array
                break;
            }
        }

        $dayIndex++;
    }

    return $distribution;
}





if ($postdata['function_type'] == "fetch_automatic_timetable") {
   
    $faculty_id = $postdata['faculty_id'];
    $days_of_work = is_array($postdata['days']) ? $postdata['days'] : [$postdata['days']];
    $time_slots = $postdata['time_slots']; // Array of time slots
    $subjects_data = $postdata['class_section_subjects']; // Array of class, section, subject
    $max_periods_per_day = (int)$postdata['max_periods_per_day'];
    $avoid_consecutive = (bool)$postdata['avoid_consecutive_teacher'];
    $limit = isset($postdata['limit']) ? (int)$postdata['limit'] : 2;

    if (empty($faculty_id)) {
    errorreturn("Faculty ID is missing. Cannot generate timetable without assigning a faculty.");
    }
    
    if (empty($days_of_work)) {
        errorreturn("Days of working are not provided.");
    }
    
    if (empty($time_slots)) {
        errorreturn("No time slots provided for timetable generation.");
    }
    
    if (count($time_slots) < $max_periods_per_day) {
        errorreturn("Provided time slots (" . count($time_slots) . ") are less than maximum periods per day ($max_periods_per_day).");
    }
    
    if (empty($subjects_data)) {
        errorreturn("No subjects, classes, or sections found for timetable generation.");
    }
    
    foreach ($subjects_data as $index => $data) {
        if (empty($data['class_id']) || empty($data['section_id']) || empty($data['subject_id']) || empty($data['classesPerWeek'])) {
            errorreturn("Invalid subject entry at index $index. 'class_id', 'section_id', 'subject_id' and 'classesPerWeek' are required.");
        }
    }

   
    $facultySubjects = [];
    foreach ($subjects_data as $data) {
        $facultySubjects[] = [
            'subject_id' => $data['subject_id'],
            'class_id' => $data['class_id'], // ✅ Passing class_id
            'section_id' => $data['section_id'],
            'classesPerWeek' => $data['classesPerWeek']
        ];
    }

    if (empty($facultySubjects)) {
        errorreturn("No subjects, classes, or sections found for this faculty.");
    }

    $subjectDistribution = distributeLessonsFairly($facultySubjects, $days_of_work);
    $allSchedules = [];
    
    for ($i = 0; $i < $limit; $i++) {
         
        $weekSchedule = [];
        foreach ($subjectDistribution as $day => $subjects) {
            foreach ($subjects as $subject) {
                $availableSlots = getAvailableSlots($institution_id, $branch_id, $faculty_id, $day, $time_slots, $subject['class_id'], $subject['section_id'], $conn);
                shuffle($availableSlots); // Shuffle time slots for randomness

                $daySchedule = [];
                $periodsAssigned = 0;

                foreach ($availableSlots as $index => $slot) {
                    if (isTeacherAvailable($faculty_id, $day, $slot['start_time'], $conn, $avoid_consecutive)) {
                        $class_info = getClass($conn, $institution_id, $subject['class_id']);
                        $section_info = getSection($conn, $institution_id, $subject['section_id']);
                        $subject_info = getSubject($conn, $institution_id, $subject['subject_id']);
                        $faculty_name = getFacultyInfo($conn, $institution_id, $faculty_id);

                        // Avoid consecutive subject repetition
                        if (!empty($daySchedule) && end($daySchedule)['subject_id']['subject_id'] == $subject_info['subject_id']) {
                            continue; // Skip to next slot to avoid repetition
                        }

                        $daySchedule[] = [
                            'day' => $day,
                            'period_start' => $slot['start_time'],
                            'end_time' => $slot['end_time'],
                            'class_id' => $class_info,
                            'section_id' => $section_info,
                            'subject_id' => $subject_info,
                            'faculty_id' => $faculty_name
                        ];

                        unset($availableSlots[$index]);
                        $periodsAssigned++;

                        if ($periodsAssigned >= $max_periods_per_day) {
                            break 2; // Stop scheduling for the day
                        }
                    }
                }

                if (!empty($daySchedule)) {
                    $weekSchedule[] = $daySchedule;
                }
            }
        }

        $allSchedules[] = $weekSchedule;
        if (count($allSchedules) >= $limit) {
            break;
        }
    }

    successreturn(["response" => ["data" => $allSchedules]]);
}
