<?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/>.

/**
 * Aggregate course results with moodle course completion
 * @package    local_treestudyplan
 * @copyright  2023 P.M. Kuipers
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace local_treestudyplan\local\aggregators;

use local_treestudyplan\courseinfo;
use local_treestudyplan\corecompletioninfo;
use local_treestudyplan\gradeinfo;
use local_treestudyplan\studyitem;
use local_treestudyplan\completion;
use local_treestudyplan\preloader_core;

/**
 * Aggregate course results with moodle course completion
 */
class core_aggregator extends \local_treestudyplan\aggregator {
    /** @var bool */
    public const DEPRECATED = false;

    /**
     * Create new instance of aggregation method
     * @param string $configstr Aggregation configuration string
     */
    public function __construct($configstr) {
        // Allow public constructor for testing purposes.
        $this->initialize($configstr);
    }

    /**
     * Initialize the aggregation method
     * @param string $configstr Aggregation configuration string
     */
    protected function initialize($configstr) {
    }

    /**
     * Return the current configuration string.
     * @return string Configuration string
     */
    public function config_string() {
        return "";
    }

    /**
     * Determine if aggregation method wants to select gradables
     * @return bool True if aggregation method needs gradables to be selected
     */
    public function select_gradables() {
        return false;
    }

    /**
     * Determine if aggregation method is deprecated
     * @return bool True if aggregation method is deprecated
     */
    public function deprecated() {
        return self::DEPRECATED;
    }

    /**
     * Determine if Aggregation method makes use of "required grades" in a course/module.
     * @return bool True if Aggregation method makes use of "required grades" in a course/module.
     */
    public function use_required_grades() {
        return true;
    }

    /**
     * Determine if aggregation method makes use of required grades
     * @return bool True if aggregation method makes use of
     */
    public function use_item_conditions() {
        return false;
    }

    /**
     * Determine if the aggregation method uses core_completion, or treestudyplan custom completion.
     * @return bool True if the aggregation method uses core_completion
     */
    public function use_corecompletioninfo() {
        return true;
    }

    /**
     * Return course completion information based on the core completion infromation
     * Possible states:
     *   completion::EXCELLENT  - Completed with excellent results
     *   completion::GOOD       - Completed with good results
     *   completion::COMPLETED  - Completed
     *   completion::PROGRESS   - Started, but not completed yey
     *   completion::FAILED     - Failed
     *   completion::INCOMPLETE - Not yet started
     * @param courseinfo $courseinfo Courseinfo object for the course to check
     * @param studyitem $studyitem Studyitem object for the course to check
     * @param int $userid Id of user to check this course for
     * @return int Aggregated completion as completion class constant
     */
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
        // Retrieve the core completion info from the core.
        $course = $courseinfo->course();
        $completion = new \completion_info($course);

        if ($completion->is_enabled() && $completion->is_tracked_user($userid)) {
            $ccompletion = preloader_core::find_course_completion($course->id, $userid);
            if ($ccompletion->is_complete()) {
                // Completed is completed.
                return completion::COMPLETED;
            } else {
                // Check if the course is over or not, if it is over, display failed.
                // Else, return PROGRESS.

                // Retrieve timing through courseinfo .
                $timing = courseinfo::coursetiming($course);

                // Not met and time is passed, means FAILED.
                if ($timing == "past") {
                    return completion::FAILED;
                } else {
                    // Check if any of the requirements are being met?.
                    $completions = preloader_core::find_criteria_completions($course->id, $userid);
                    foreach ($completions as $c) {
                        if ($c->is_complete()) {
                            // If so, return progress.
                            return completion::PROGRESS;
                        }
                    }
                    return completion::INCOMPLETE;
                }
            }
        } else {
            return completion::INCOMPLETE;
        }
    }

    /**
     * Aggregate juncton/filter inputs into one final junction outcome
     * @param int[] $completion List of completion inputs
     * @param studyitem $studyitem Studyitem object for the junction
     * @param int $userid Id of user to check completion for
     * @return int Aggregated completion as completion class constant
     */
    public function aggregate_junction(array $completion, studyitem $studyitem, $userid = 0) {
        // Aggregate multiple incoming states into one junction or finish.
        // Possible states:.
        // - completion::EXCELLENT  - All incoming states are excellent.
        // - completion::GOOD       - All incoming states are at least good.
        // - completion::COMPLETED  - All incoming states are at least completed.
        // - completion::FAILED     - All incoming states are failed.
        // - completion::INCOMPLETE - All incoming states are incomplete.
        // - completion::PROGRESS   - All other states.

        $method = strtoupper($studyitem->conditions()); // One of ANY or ALL.

        // First count all states.
        $statecount = completion::count_states($completion);
        $total = count($completion);

        if ($method == "ANY") {
            if ($statecount[completion::EXCELLENT] >= 1) {
                return completion::EXCELLENT;
            } else if ($statecount[completion::GOOD] >= 1) {
                return completion::GOOD;
            } else if ($statecount[completion::COMPLETED] >= 1) {
                return completion::COMPLETED;
            } else if ($statecount[completion::PROGRESS] >= 1) {
                return completion::PROGRESS;
            } else if ($statecount[completion::FAILED] >= 1) {
                return completion::FAILED;
            } else {
                return completion::INCOMPLETE;
            }
        } else { /* default value of ALL */
            if ($total == $statecount[completion::EXCELLENT]) {
                return completion::EXCELLENT;
            } else if (
                $total == (
                    $statecount[completion::EXCELLENT]
                    + $statecount[completion::GOOD]
                )
            ) {
                return completion::GOOD;
            } else if (
                $total == (
                    $statecount[completion::EXCELLENT]
                    + $statecount[completion::GOOD]
                    + $statecount[completion::COMPLETED]
                )
            ) {
                return completion::COMPLETED;
            } else if ($statecount[completion::FAILED]) {
                return completion::FAILED;
            } else if ($total == $statecount[completion::INCOMPLETE]) {
                return completion::INCOMPLETE;
            } else {
                return completion::PROGRESS;
            }
        }
    }

    /**
     * Determine completion for a single grade and user
     * @param gradeinfo $gradeinfo Gradeinfo object for grade to check
     * @param mixed $userid Id of user to check completion for
     * @return int Aggregated completion as completion class constant
     */
    public function grade_completion(gradeinfo $gradeinfo, $userid) {
        // CORE COMPLETION DOESN'T REALLY USE THIS FUNCTION.
        return 0;
    }
}
