<?php

namespace local_treestudyplan;
require_once($CFG->libdir.'/externallib.php');
require_once($CFG->libdir.'/gradelib.php');
require_once($CFG->dirroot.'/course/lib.php');

use core_course\local\repository\caching_content_item_readonly_repository;
use core_course\local\repository\content_item_readonly_repository;
use \grade_item;
use \grade_scale;
use \grade_outcome;
use \core_plugin_manager;

class gradeinfo {
    private $studyitem = null;
    private $id;
    private $gradeitem;
   
    private $icon;
    private $link;
    private $gradinglink;
    private $scale;
    private $outcome;
    private $hidden = false;
    private $name;
    private $typename;
    private $section;
    private $sectionorder;
    private $cmid;
    private $coursesort;


    private static $contentitems = null;
    private $gradingscanner;

    private static $sections = [];

    protected static function getSectionSequence($sectionid){
        global $DB;
        if(!array_key_exists($sectionid,self::$sections)){
            self::$sections[$sectionid] = explode(",",$DB->get_field("course_sections","sequence",["id"=>$sectionid]));
        }
        return self::$sections[$sectionid];
    }

    public function getGradeitem(){
        return $this->gradeitem;
    }

    public function getGradingscanner(){
        return $this->gradingscanner;
    }

    public function getScale(){
        return $this->scale;
    }

    protected static function get_contentitems() {
        global $PAGE;
        if(empty(static::$contentitems)){
            $PAGE->set_context(\context_system::instance());
            static::$contentitems = (new content_item_readonly_repository())->find_all();
        }
        return static::$contentitems;
    }

    public static function get_contentitem($name) {
        $contentitems = static::get_contentitems();
        for($i = 0; $i < count($contentitems); $i++){
            if($contentitems[$i]->get_name() == $name){
                return $contentitems[$i];
            }
        }
        return null;
    }

    public static function getCourseContextById($id){
        $gi = grade_item::fetch(["id" => $id]);
        if(!$gi || course_module_instance_pending_deletion($gi->courseid, $gi->itemmodule, $gi->iteminstance))
        {
            throw new \InvalidArgumentException ("Grade {$id} not found in database". print_r($gi,true));
        }
        return \context_course::instance($gi->courseid);;
    }

    public function __construct($id,studyitem $studyitem = null){
        global $DB;
        $this->studyitem = $studyitem;

        $gi = grade_item::fetch(["id" => $id]);
        if(!$gi || course_module_instance_pending_deletion($gi->courseid, $gi->itemmodule, $gi->iteminstance))
        {
            throw new \InvalidArgumentException ("Grade {$id} not found in database". print_r($gi,true));
        }
        $this->id = $id;
        $this->gradeitem = $gi;

        // Determine the icon for the associated activity
        $contentitem = static::get_contentitem($gi->itemmodule);
        $this->icon = empty($contentitem)?"":$contentitem->get_icon();

        // Determine a link to the associated activity
        if($gi->itemtype != "mod" || empty($gi->itemmodule) || empty($gi->iteminstance)){
            $this->link = "";
            $this->cmid = 0;
            $this->section = 0;
            $this->sectionorder = 0;
        }
        else {
            list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
            $this->cmid = $cminfo->id;
            // sort by position in course
            // 
            $this->section = $cminfo->sectionnum;
            $ssequence = self::getSectionSequence($cminfo->section);
            $this->sectionorder = array_search($cminfo->id,$ssequence);

            $this->link = "/mod/{$gi->itemmodule}/view.php?id={$cminfo->id}";
            if($gi->itemmodule == 'quiz'){
                $this->gradinglink = "/mod/{$gi->itemmodule}/report.php?id={$cminfo->id}&mode=grading";
            }
            else if($gi->itemmodule == "assign") { 
                $this->gradinglink = $this->link ."&action=grading";
            }
            else {
                $this->gradinglink = $this->link;
            }
        }

        $this->scale = $gi->load_scale();
        $this->outcome = $gi->load_outcome();

        $this->hidden = ($gi->hidden || (!empty($outcome) && $outcome->hidden))?true:false;

        $this->name =  empty($outcome)?$gi->itemname:$outcome->name;
        $this->typename = empty($contentitem)?$gi->itemmodule:$contentitem->get_title()->get_value();
        $this->gradingscanner = new gradingscanner($gi);

        $this->coursesort = $this->section * 1000 + $this->sectionorder;

    }

    public function is_selected(){
        global $DB;
        if($this->studyitem){
            // Check if selected for this studyitem
            $r = $DB->get_record('local_treestudyplan_gradeinc',['studyitem_id' => $this->studyitem->id(), 'grade_item_id'=> $this->gradeitem->id]);
            if($r && $r->include) {
                return(true);
            }
        }
        return(false);
    }

    public function is_required(){
        global $DB;
        if($this->studyitem){
            // Check if selected for this studyitem
            $r = $DB->get_record('local_treestudyplan_gradeinc',['studyitem_id' => $this->studyitem->id(), 'grade_item_id'=> $this->gradeitem->id]);
            if($r && $r->include && $r->required) {
                return(true);
            }
        }
        return(false);
    }

    public static function editor_structure($value=VALUE_REQUIRED){
        return new \external_single_structure([
            "id" => new \external_value(PARAM_INT, 'grade_item id'),
            "cmid" => new \external_value(PARAM_INT, 'course module id'),
            "name" => new \external_value(PARAM_TEXT, 'grade item name'),
            "typename" => new \external_value(PARAM_TEXT, 'grade item type name'),
            "outcome" =>  new \external_value(PARAM_BOOL, 'is outcome'),
            "selected" =>  new \external_value(PARAM_BOOL, 'is selected for current studyitem'),
            "icon" => new \external_value(PARAM_RAW, 'html for icon of related activity'),
            "link" => new \external_value(PARAM_TEXT, 'link to related activity'),
            "gradinglink" => new \external_value(PARAM_TEXT, 'link to related activity'),
            "grading" => gradingscanner::structure(),
            "required" =>  new \external_value(PARAM_BOOL, 'is required for current studyitem'),
        ], 'referenced course information',$value);
    }

    public function editor_model(studyitem $studyitem = null) {
        $model = [
            "id" => $this->id,
            "cmid" => $this->cmid,
            "name" => $this->name,
            "typename" => $this->typename,
            "outcome" => isset($this->outcome),
            "selected" => $this->is_selected(),
            "icon" => $this->icon,
            "link" => $this->link,
            "gradinglink" => $this->gradinglink,
            "required" =>  $this->is_required(),
        ];
        // Unfortunately, lazy loading of the completion data is off, since we need the data to show study item completion...
        if($studyitem !== null && $this->is_selected() && has_capability('local/treestudyplan:viewuserreports',$studyitem->studyline()->studyplan()->context()) 
            && $this->gradingscanner->is_available()){
            $model['grading'] = $this->gradingscanner->model();
        }
        return $model;
    }


    public static function user_structure($value=VALUE_REQUIRED){
        return new \external_single_structure([
            "id" => new \external_value(PARAM_INT, 'grade_item id'),
            "cmid" => new \external_value(PARAM_INT, 'course module id'),
            "name" => new \external_value(PARAM_TEXT, 'grade item name'),
            "typename" => new \external_value(PARAM_TEXT, 'grade item type name'),
            "grade" =>  new \external_value(PARAM_TEXT, 'is outcome'),
            "gradetype" => new \external_value(PARAM_TEXT, 'grade type (completion|grade)'),
            "feedback" => new \external_value(PARAM_RAW, 'html for feedback'),
            "completion" =>  new \external_value(PARAM_TEXT, 'completion state (incomplete|progress|completed|excellent)'),
            "icon" => new \external_value(PARAM_RAW, 'html for icon of related activity'),
            "link" => new \external_value(PARAM_TEXT, 'link to related activity'),
            "pendingsubmission" =>  new \external_value(PARAM_BOOL, 'is selected for current studyitem',VALUE_OPTIONAL),
            "required" =>  new \external_value(PARAM_BOOL, 'is required for current studyitem'),
            "selected" =>  new \external_value(PARAM_BOOL, 'is selected for current studyitem'),
        ], 'referenced course information',$value);
    }

    public function user_model($userid) {
        global $DB;
        $grade = $this->gradeitem->get_final($userid);
        // convert scale grades to corresponding scale name
        if(!empty($grade)){
            if(!is_numeric($grade->finalgrade) && empty($grade->finalgrade)){
                $finalgrade = "-";
            }
            else if(isset($this->scale)){
                $finalgrade = $this->scale->get_nearest_item($grade->finalgrade);
            } 
            else 
            {
                $finalgrade = round($grade->finalgrade,1);
            }
        }
        else 
        {
            $finalgrade = "-";
        }

        // retrieve the aggregator and determine completion
        if(!isset($this->studyitem)){
            throw new \UnexpectedValueException("Study item not set (null) for gradeinfo in report mode");
        }
        $aggregator = $this->studyitem->studyline()->studyplan()->aggregator();
        $completion = $aggregator->grade_completion($this,$userid);

        $model = [
            "id" => $this->id,
            "cmid" => $this->cmid,
            "name" => $this->name,
            "typename" => $this->typename,
            "grade" => $finalgrade,
            "gradetype" => isset($this->scale)?"completion":"grade",
            "feedback" => empty($grade)?null:$grade->feedback,
            "completion" => completion::label($completion),
            "icon" => $this->icon,
            "link" => $this->link,
            "pendingsubmission" => $this->gradingscanner->pending($userid),
            "required" =>  $this->is_required(),
            "selected" => $this->is_selected(),
        ];

        return $model;
    }

    public function export_model(){
        return [
            "name" => $this->name,
            "type" => $this->gradeitem->itemmodule,
            "selected" => $this->is_selected(),
            "required" =>  $this->is_required(),
        ];
    }
    public static function import(studyitem $item,array $model){
        if($item->type() == studyitem::COURSE){
            $course_id = $item->courseid();
            $gradeitems= grade_item::fetch_all(['itemtype' => 'mod', 'courseid' => $course_id]);
            foreach($gradeitems as $gi){
                $gi_name =  empty($outcome)?$gi->itemname:$outcome->name;
                $gi_type = $gi->itemmodule;

                if($gi_name == $model["name"] && $gi_type == $model["type"]){
                    // we have a match
                    if(!isset($model["selected"])){ $model["selected"] = true;}
                    if(!isset($model["required"])){ $model["required"] = false;}
                    if($model["selected"] || $model["required"]){
                        static::include_grade($gi->id,$item->id(),$model["selected"], $model["required"]);
                    } 
                }
            }
        } 
    }

    public static function list_course_gradables($course,studyitem $studyitem=null) {
        $list = [];

        if(method_exists("\course_modinfo","get_array_of_activities")){
            $activities = \course_modinfo::get_array_of_activities($course);
        } else {
            // Deprecated in Moodle 4.0+, but not yet available in Moodle 3.11
            $activities = get_array_of_activities($course->id);
        }
        foreach($activities as $act)
        {
            if($act->visible)
            {
                $gradeitems= grade_item::fetch_all(['itemtype' => 'mod', 'itemmodule' => $act->mod, 'iteminstance' => $act->id, 'courseid' => $course->id]);
                if(!empty($gradeitems))
                {
                    foreach($gradeitems as $gi){
                        if(($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE))
                        {
                            try {
                                $gradable = new static($gi->id,$studyitem);
                                $list[] = $gradable;
                            }
                            catch(\InvalidArgumentException $x){}
                        }
                    }
                }
            }
        }

        usort($list, function($a,$b){
            $course = $a->coursesort <=> $b->coursesort;
            return ($course != 0)?$course:$a->gradeitem->sortorder <=> $b->gradeitem->sortorder;
        });
        return $list;
    }

    public static function list_studyitem_gradables(studyitem $studyitem)
    {
        global $DB;
        $table = 'local_treestudyplan_gradeinc';
        $list = [];
        $records = $DB->get_records($table,['studyitem_id' => $studyitem->id()]);
        foreach($records as $r){
            if(isset($r->grade_item_id)){
                try {
                    if($r->include || $r->required){
                        $list[] = new static($r->grade_item_id,$studyitem);
                    }
                }
                catch(\InvalidArgumentException $x){
                    // on InvalidArgumentException, the grade_item id can no longer be found
                    // Remove the link to avoid database record hogging
                    $DB->delete_records($table, ['id' => $r->id]);
                }
            }
        }

        usort($list, function($a,$b){
            $course = $a->coursesort <=> $b->coursesort;
            return ($course != 0)?$course:$a->gradeitem->sortorder <=> $b->gradeitem->sortorder;
        });
        return $list;
    }

    public static function include_grade(int $grade_id,int $item_id,bool $include,bool $required=false) {
        global $DB;
        $table = 'local_treestudyplan_gradeinc';
        if($include){
            // make sure a record exits
            $r = $DB->get_record($table,['studyitem_id' => $item_id, 'grade_item_id' => $grade_id]);
            if($r){
                $r->include = 1;
                $r->required = boolval($required)?1:0;
                $id = $DB->update_record($table, $r);
            } else {
                $DB->insert_record($table, [
                    'studyitem_id' => $item_id, 
                    'grade_item_id' => $grade_id, 
                    'include' => 1, 
                    'required' =>boolval($required)?1:0]
                );
            }
        } else {
            // remove if it should not be included
            $r = $DB->get_record($table,['studyitem_id' => $item_id, 'grade_item_id' => $grade_id]);
            if($r){
                $DB->delete_records($table, ['id' => $r->id]);
            }
        }

        return success::success();
    }



}