<?php
/**
 * @package    block_ned_teacher_tools
 * @category   NED
 * @copyright  2023 NED {@link http://ned.ca}
 * @author     NED {@link http://ned.ca}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */


namespace block_ned_teacher_tools;
use block_ned_teacher_tools\shared_lib as SH;

/**
 * Class to manage CTA activities and Tracker
 */
class cta_tracker {
    const CM_TYPES = [
        SH::ASSIGN => SH::ASSIGN,
        SH::FORUM => SH::FORUM,
    ];

    const CAP_VIEW = 'canviewcta';
    const CAP_GRADE = 'cangradecta';

    /**
     * Checks user capability to view CTA tracker
     *
     * @param numeric|object|null $course_or_id - course to check capability, by default (null) checks current course
     * @param numeric|object|null $user_or_id - user to check capability, by default (null) checks current $USER
     * @param boolean             $include_admin (optional) If false, ignores effect of admin role assignment
     *
     * @return bool
     */
    static public function can_view_tracker($course_or_id=null, $user_or_id=null, $include_admin=true){
        $courseid = SH::get_courseid_or_global($course_or_id);
        return SH::has_capability(static::CAP_VIEW, SH::ctx($courseid), $user_or_id, $include_admin);
    }

    /**
     * Checks user capability to grade CTA activities
     *
     * @param numeric|object|null $course_or_id  - course to check capability, by default (null) checks current course
     * @param numeric|object|null $user_or_id    - user to check capability, by default (null) checks current $USER
     * @param boolean             $include_admin (optional) If false, ignores effect of admin role assignment
     *
     * @return bool
     */
    static public function can_grade($course_or_id=null, $user_or_id=null, $include_admin=true){
        $courseid = SH::get_courseid_or_global($course_or_id);
        return SH::has_capability(static::CAP_GRADE, SH::ctx($courseid), $user_or_id, $include_admin);
    }

    /**
     * Checks, does CTA tracker is on
     *
     * @param numeric|object|null $course_or_id - course to check capability, by default (null) checks current course
     *
     * @return bool
     */
    static public function is_tracker_on($course_or_id=null){
        return (bool)SH::get_tt_block_config($course_or_id, 'ctatracker_enable');
    }

    /**
     * Checks that user can view CTA activities, and he should grade them (as he has native grade capability)
     *
     * @param numeric|object|null $course_or_id  - course to check capability, by default (null) checks current course
     * @param numeric|object|null $user_or_id    - user to check capability, by default (null) checks current $USER
     *
     * @return bool
     */
    static public function is_responsible_grader($course_or_id=null, $user_or_id=null){
        return static::can_view_tracker($course_or_id, $user_or_id) && static::can_grade($course_or_id, $user_or_id, false);
    }

    /**
     * Checks user capability to view CTA tracker, and it's turned on
     *
     * @param numeric|object|null $course_or_id - course to check capability, by default (null) checks current course
     * @param numeric|object|null $user_or_id - user to check capability, by default (null) checks current $USER
     *
     * @return bool
     */
    static public function can_view_active_tracker($course_or_id=null, $user_or_id=null){
        return static::is_tracker_on($course_or_id) && static::can_view_tracker($course_or_id, $user_or_id);
    }

    /**
     * @return string|\block_ned_teacher_tools\output\cta_tracker_render
     * @noinspection PhpReturnDocTypeMismatchInspection
     */
    static public function get_render(){
        return '\block_ned_teacher_tools\output\cta_tracker_render';
    }

    /**
     * Get CTA scale menu for course
     * @param numeric|object|null $course_or_id - course to check capability, by default (null) checks current course
     *
     * @return array
     */
    static public function get_scale_menu($course_or_id=null){
        $scale_id = SH::get_tt_block_config($course_or_id, 'cta_scale');
        if (empty($scale_id)){
            // get site def
            $scale_id = SH::get_config('cta_scale');
        }

        return SH::scale_get_menu_by_id($scale_id);
    }

    /**
     * Get CTA role id for course
     * @param numeric|object|null $course_or_id - course to check capability, by default (null) checks current course
     *
     * @return int|null
     */
    static public function get_cta_grader_role($course_or_id=null){
        $role_id = SH::get_tt_block_config($course_or_id, 'cta_role');
        if (empty($role_id)){
            // get site def
            $role_id = SH::get_config('cta_role');
        }

        return $role_id > 0 ? $role_id : null;
    }

    /**
     * Update (assign/unassign) CTA graders on the course
     *
     * @param object|numeric $course_or_id
     * @param bool $should_be_on - if true, and tracker is off, then function will do nothing
     *
     * @return bool - false, if it does nothing
     */
    static public function update_cta_graders_by_course($course_or_id=null, $should_be_on=false){
        $is_on = static::is_tracker_on($course_or_id);
        if ($should_be_on && !$is_on) return false;

        $courseid = SH::get_courseid_or_global($course_or_id);
        $cms_by_context = []; // [contextid => $cm]

        if ($is_on){
            $cta_roleid = static::get_cta_grader_role($courseid);

            $cms = SH::get_course_cms($courseid, null, static::CM_TYPES);
            foreach ($cms as $cm){
                $cms_by_context[$cm->context->id] = $cm;
            }
            $cta_cmids = SH::cmids_get_cta($courseid); // [cmid => cmid]

            $course_ctx = SH::ctx($courseid);
            $capability = SH::get_full_capability(static::CAP_GRADE);
            $cta_grader_ids = \get_enrolled_users($course_ctx, $capability, 0, 'u.id', null, 0, 0, true);
            if (!empty($cta_grader_ids)){
                $cta_grader_ids = array_keys($cta_grader_ids);
                $cta_grader_ids = array_combine($cta_grader_ids, $cta_grader_ids);
            }
        } else {
            $cta_roleid = null;
            $cms = [];
            $cta_cmids = [];
            $cta_grader_ids = [];
        }

        // unassign not relevant users
        $cm_exists_grader_ids = []; // cmid => [userid => userid]
        $exists_ra_records = SH::db()->get_records('role_assignments', ['component' => SH::TT, 'itemid' => $courseid]);
        foreach ($exists_ra_records as $ra_record){
            $unassign = true;
            do {
                if (!$is_on) break;

                $cm = $cms_by_context[$ra_record->contextid] ?? null;
                if (empty($cm)) break;
                if (empty($cta_cmids[$cm->id])) break;
                if (empty($cta_grader_ids[$ra_record->userid])) break;
                // For now, TT doesn't use other enrol methods, but lately it can be changed, and we will need different check
                if (!$cta_roleid || $ra_record->roleid != $cta_roleid) break;

                // all is fine
                $unassign = false;
                $cm_exists_grader_ids[$cm->id][$ra_record->userid] = $ra_record->userid;
            } while(false);

            if ($unassign){
                \role_unassign($ra_record->roleid, $ra_record->userid, $ra_record->contextid, $ra_record->component, $ra_record->itemid);
            }
        }

        // assign relevant users
        if (empty($cta_cmids) || empty($cta_roleid) || empty($cta_grader_ids)) return true;

        foreach ($cta_cmids as $cmid){
            $cm = $cms[$cmid] ?? null;
            if (empty($cm)) continue;

            $cm_grader_ids = $cm_exists_grader_ids[$cmid] ?? [];
            $new_user_ids = array_diff($cta_grader_ids, $cm_grader_ids);
            if (!empty($new_user_ids)){
                foreach ($new_user_ids as $new_user_id){
                    \role_assign($cta_roleid, $new_user_id, $cm->context->id, SH::TT, $courseid);
                }
            }
        }

        return true;
    }
}
