<?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, $conn, $limit = 10) {
    try {
        // Ensure time_slots is an array and not empty
        if (!is_array($time_slots) || empty($time_slots)) {
            errorreturn(["message" => "Invalid or empty time slots."]);
        }

        // Fetch occupied slots from timetable
        $query = "SELECT id, period_start, end_time, faculty_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);

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

        // Check for conflicts
        foreach ($time_slots as $slot) {
            foreach ($occupiedSlots as $occupied) {
                if (
                    ($slot['start_time'] >= $occupied['period_start'] && $slot['start_time'] < $occupied['end_time']) || 
                    ($slot['end_time'] > $occupied['period_start'] && $slot['end_time'] <= $occupied['end_time'])
                ) {
                    if ($occupied['faculty_id'] == $faculty_id) {
                        errorreturn([
                            "message" => "Time slot conflict detected: Slot (" . $slot['start_time'] . " - " . $slot['end_time'] . ") conflicts with an existing schedule (" . $occupied['period_start'] . " - " . $occupied['end_time'] . ")."
                        ]);
                    }
                    break;
                }
            }
        }

        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

    // Distribute subjects fairly across available days
    $dayIndex = 0;
    while (!empty($subjectsQueue)) {
        $currentDay = $days_of_work[$dayIndex % count($days_of_work)];
        
        // Ensure we do not assign the same subject consecutively on the same day
        $prevSubject = end($distribution[$currentDay])['subject_id'] ?? null;
        foreach ($subjectsQueue as $key => $subject) {
            if ($subject['subject_id'] !== $prevSubject) {
                $distribution[$currentDay][] = $subject;
                unset($subjectsQueue[$key]);
                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 (!($conn instanceof PDO)) {
        errorreturn("Database connection error.");
    }

    $facultySubjects = [];
    foreach ($subjects_data as $data) {
        $facultySubjects[] = [
            'subject_id' => $data['subject_id'],
            'class_id' => $data['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) {
            $availableSlots = getAvailableSlots($institution_id, $branch_id,$faculty_id, $day, $time_slots, $conn);
            shuffle($availableSlots); // Shuffle time slots for randomness

            $daySchedule = [];
            $periodsAssigned = 0;

            foreach ($subjects as $subject) {
                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]]);
}