<?php
// This file is part of the Studyplan plugin for Moodle
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <https://www.gnu.org/licenses/>.

/**
 * Synchronize enrolled users in courses with users associated with studyplans these courses are in
 * @package    local_treestudyplan
 * @copyright  2023 P.M. Kuipers
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace local_treestudyplan;
defined('MOODLE_INTERNAL') || die();

require_once($CFG->libdir.'/externallib.php');

use local_treestudyplan\studyplan;

/**
 * Task class to synchronize enrolled users in courses with users associated with studyplans these courses are in
 */
class cascadeusersync {
    /**
     * The method to use for enrolling students
     * @var string
     */
    private const METHOD = "manual";
    /** @var studyplan */
    private $studyplan;
    /** @var object */
    private $enrol;
    /** @var int */
    private $roleid;
    /** @var array */
    private $userids;

    /**
     * Create a synchronization task for a studyplan
     * @param studyplan $studyplan The studyplan to enrol students for
     */
    public function __construct(studyplan $studyplan) {
        $this->studyplan = $studyplan;
        $this->enrol = \enrol_get_plugin(self::METHOD);
        // Get the roleid to use for synchronizations.
        $this->roleid = get_config("local_treestudyplan", "csync_roleid");
        // And find the users that are linked to this studyplan.
        /* The userid's are loaded in the constructor and cached in the class, to load it only once,
           and have cleaner code in the line syncline function, which does not always need all userids linked.
           */
        $this->userids = $this->studyplan->get_linked_user_ids();
    }

    /**
     * Enroll all users associated to the studyplan in the courses linked to this studyplan
     */
    public function sync() {
        /*  Explainer:
            This script uses {enrol}.customtext4 to store a json array of all studyplans that need this cohort sync to exist.
            Since the cohort-sync enrolment method uses only customint1 and customint2, this is a safe place to store the data.
            (Should the cohortsync script at any future time be modified to use customtext fields, it is still extremely
            unlikely that customtext4 will be used.)
            Because of the overhead involved in keeping an extra table up to date and clean it up if cohort syncs are removed
            outside of this script, it was determined to be the simplest and cleanest solution.
        */

        // Find the study lines associated to this studyplan.
        preloader::preload_courses($this->studyplan);
        $lines = $this->studyplan->get_all_studylines();

        foreach ($lines as $line) {
            $this->syncline($line);
        }
    }

    /**
     * Enroll all cohorts associated to the studyplan in the courses linked to the specified study line
     * @param studyline $line Studyline to sync enrolment for
     */
    public function syncline(studyline $line) {
        // Find the courses that need to be synced to the associated cohorts.
        $courseids = $line->get_linked_course_ids();
        if ($line->enrollable()) {
            $lineuids = $line->get_enrolled_userids();
            // Next, for each course that is linked:.
            foreach ($courseids as $courseid) {
                $this->perform_enrol($courseid, $lineuids);
                // We do not do any autoremoval for user syncs, to avoid students losing access to the course data.
            }
        } else {
            // Next, for each course that is linked:.
            foreach ($courseids as $courseid) {
                $this->perform_enrol($courseid, $this->userids);
                // We do not do any autoremoval for user syncs, to avoid students losing access to the course data.
            }
        }
    }

    /**
     * Enrol a list of users into a specific course
     * @param int $courseid Id of the course
     * @param array $userids List of userids to enrol
     */
    private function perform_enrol($courseid, $userids) {
        global $DB;
        $course = preloader::get_course($courseid);
        if (count($userids) > 0) {
            // Get the manual enrol instance for this course.
            $instanceparams = ['courseid' => $courseid, 'enrol' => 'manual'];
            if (!($instance = $DB->get_record('enrol', $instanceparams))) {
                if ($instanceid = $this->enrol->add_default_instance($course)) {
                    $instance = $DB->get_record('enrol', ['id' => $instanceid]);
                } else {
                    // Instance not added for some reason, so report an error somewhere.
                    // (or not).
                    $instance = null;
                }
            }
            if ($instance !== null) {
                foreach ($userids as $uid) {
                    // Try a manual registration - it will just be updated if it is already there....
                    $this->enrol->enrol_user($instance, $uid, $this->roleid);
                }
            }
        }
    }
}
