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

/**
 * Webservice class for handling studyplan reports
 * @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();

use local_treestudyplan\local\helpers\webservicehelper;
use local_treestudyplan\period;
use local_treestudyplan\studyplan;
use local_treestudyplan\studyplanpage;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_description;
use core_external\external_value;
use core_external\external_multiple_structure;
use core_external\external_single_structure;

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

/**
 * Webservice class for handling studyplan reports
 */
class reportservice extends external_api {
    /**
     * Capability required to view studyplans (for other users)
     * @var string
     */
    const CAP_VIEW = "local/treestudyplan:viewuserreports";

    /**
     * Parameter description for webservice function
     */
    public static function get_report_structure_parameters(): external_function_parameters {
        return new external_function_parameters([
            "pageid" => new external_value(PARAM_INT, 'id of studyplan page'),
            "firstperiod" => new external_value(PARAM_INT, 'first period to include in report', VALUE_DEFAULT),
            "lastperiod" => new external_value(PARAM_INT, 'last period to include in report', VALUE_DEFAULT),
        ]);
    }

    /**
     * Return value description for webservice function list_user_studyplans
     */
    public static function get_report_structure_returns(): external_description {
        return new external_single_structure([
            "periods" => new external_multiple_structure(new external_single_structure([
                "period" => period::structure(),
                "lines" => new external_multiple_structure(new external_single_structure([
                    "line" => studyline::simple_structure(),
                    "items" => new external_multiple_structure(studyitem::editor_structure(), "Information per studyitem"),
                ])),
            ])),
            "firstperiod" => new external_value(PARAM_INT, 'first period included in report'),
            "lastperiod" => new external_value(PARAM_INT, 'last period included in report'),
            "studyplan" => studyplan::simple_structure(),
            "page" => studyplanpage::simple_structure(),
        ]);
    }

    /**
     * Get studyplan page structure for a report
     * @param int $pageid ID of user to check specific info for
     * @param int|null $firstperiod First period to include in report
     * @param int|null $lastperiod Last period to include in report
     * @return array
     */
    public static function get_report_structure($pageid, $firstperiod = null, $lastperiod = null) {
        $page = studyplanpage::find_by_id($pageid);
        webservicehelper::require_capabilities(self::CAP_VIEW, $page->studyplan()->context());

        $plan = $page->studyplan();
        if (empty($firstperiod)) {
            $firstperiod = 1;
        }
        if (empty($lastperiod)) {
            // Index for periods starts at 1, so the period count is same as length.
            $lastperiod = $page->periods();
        }

        $model = [
            "studyplan" => $plan->simple_model(),
            "page" => $page->simple_model(),
            "firstperiod" => $firstperiod,
            "lastperiod" => $lastperiod,
            "periods" => [],
        ];

        // Find all relevant parts in this order.

        for ($i = $firstperiod; $i <= $lastperiod; $i++) {
            $period = period::find($page, $i);
            $pmodel = [
                'period' => $period->model(),
                'lines' => [],
            ];

            $lines = studyline::find_page_children($page);
            foreach ($lines as $line) {
                $lmodel = [
                    "line" => $line->simple_model(),
                    "items" => [],
                ];
                $items = studyitem::search_studyline_children($line, $i, [studyitem::COURSE]);
                $badges = studyitem::search_studyline_children($line, $i, [studyitem::BADGE]);
                if (count($items) + count($badges) > 0) {
                    foreach ($items as $item) {
                        $lmodel["items"][] = $item->editor_model();
                    }
                    foreach ($badges as $badge) {
                        $lmodel["items"][] = $badge->editor_model();
                    }

                    // Only include the line if it has actual children - saves us from muddying up the report.
                    $pmodel['lines'][] = $lmodel;
                }
            }

            $model["periods"][] = $pmodel;
        }

        return $model;
    }

    /**
     * Parameter description for webservice function
     */
    public static function get_report_data_parameters(): external_function_parameters {
        return new external_function_parameters([
            "pageid" => new external_value(PARAM_INT, 'id of studyplan page'),
            "userid" => new external_value(PARAM_INT, 'id of user'),
            "firstperiod" => new external_value(PARAM_INT, 'first period to include in report', VALUE_DEFAULT),
            "lastperiod" => new external_value(PARAM_INT, 'last period to include in report', VALUE_DEFAULT),
        ]);
    }

    /**
     * Return value description for webservice function list_user_studyplans
     */
    public static function get_report_data_returns(): external_description {
        return new external_multiple_structure(studyitem::user_structure(), "Information per studyitem");
    }

    /**
     * Webservice function get_report data
     * @param int $pageid Id of the studyplan page to build the report for
     * @param int $userid ID ofthe user to gather the report for
     * @param int|null $firstperiod First period of the page to include in the report (first of page if left empty)
     * @param int|null $lastperiod Last period of the page to include in the report (last of page if left empty)
     */
    public static function get_report_data($pageid, $userid, $firstperiod = null, $lastperiod = null) {
        $page = studyplanpage::find_by_id($pageid);
        webservicehelper::require_capabilities(self::CAP_VIEW, $page->studyplan()->context());
        $plan = $page->studyplan();
        if (!$plan->has_linked_user($userid)) {
            throw new \moodle_exception("error:usernotassociated", "local_treestudyplan");
        }

        if (empty($firstperiod)) {
            $firstperiod = 1;
        }
        if (empty($lastperiod)) {
            // Index for periods starts at 1, so the period count is same as length.
            $lastperiod = $page->periods();
        }

        preloader::preload_for_users($plan, $userid);

        $model = [];
        for ($i = $firstperiod; $i <= $lastperiod; $i++) {
            $lines = studyline::find_page_children($page);
            foreach ($lines as $line) {
                $items = studyitem::search_studyline_children($line, $i, [studyitem::COURSE, studyitem::BADGE]);
                if (count($items) > 0) {
                    foreach ($items as $item) {
                        $model[] = $item->user_model($userid);
                    }
                }
            }
        }
        return $model;
    }

    /**
     * Parameter description for webservice function
     */
    public static function get_report_details_parameters(): external_function_parameters {
        return new external_function_parameters([
            "itemid" => new external_value(PARAM_INT, 'id of studyitem'),
            "userid" => new external_value(PARAM_INT, 'id of user'),
        ]);
    }

    /**
     * Return value description for webservice function list_user_studyplans
     */
    public static function get_report_details_returns(): external_description {
        return studyitem::user_structure();
    }

    /**
     * Get study item details for a user
     * @param int $itemid ID of study item
     * @param int $userid ID of user to check specific info for
     * @return array
     */
    public static function get_report_details($itemid, $userid) {
        $item = studyitem::find_by_id($itemid);
        $plan = $item->studyline()->studyplan();
        webservicehelper::require_capabilities(self::CAP_VIEW, $plan->context());
        if (!$plan->has_linked_user($userid)) {
            throw new \moodle_exception("error:usernotassociated", "local_treestudyplan");
        }
        return $item->user_model($userid);
    }
}
