<?php
// This file is part of Moodle - http://moodle.org/
//
// 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 <http://www.gnu.org/licenses/>.

/**
 * Class for performing DB actions for the verbalfeedback activity module.
 *
 * @package   mod_verbalfeedback
 * @copyright 2020 Kevin Tippenhauer <kevin.tippenhauer@bfh.ch>
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace mod_verbalfeedback\repository;

use Exception;
use mod_verbalfeedback\model\localized_string;
use mod_verbalfeedback\model\template\template_category;
use mod_verbalfeedback\repository\model\db_localized_string;
use mod_verbalfeedback\repository\model\db_parametrized_criterion;
use mod_verbalfeedback\repository\model\db_template_category;
use mod_verbalfeedback\repository\model\localized_string_type;

/**
 * The template category repository class
 */
class template_category_repository {
    /**
     * Gets all the template categories in the database.
     *
     * @return array<int, template_category> The resulting categories.
     */
    public function get_all(): array {
        global $DB;

        static $results = null;
        if ($results !== null && !PHPUNIT_TEST) {
            return $results;
        }

        $results = [];
        $rs = $DB->get_recordset(tables::TEMPLATE_CATEGORY_TABLE);
        $templatecategories = [];
        foreach ($rs as $dbocategory) {
            $templatecategory = db_template_category::to_template_category($dbocategory);
            $templatecategories[$templatecategory->get_id()] = $templatecategory;
        }
        $rs->close();

        $headersbycatids = $this->get_headers_by_category_ids();
        $parametrizedcriteriabycatid = $this->get_parameterized_criteria_by_categoryid();

        foreach ($templatecategories as $catid => $templatecategory) {
            $headers = $headersbycatids[$catid];
            $templatecategory->set_headers($headers);
            $parametrizedcriteria = $parametrizedcriteriabycatid[$catid] ?? [];
            $templatecategory->set_template_criteria($parametrizedcriteria);

            $results[$catid] = $templatecategory;
        }
        return $results;
    }

    /**
     * Gets the category template for the given id
     *
     * @param int $id The category template id.
     * @return template_category|null The template categories.
     */
    public function get_by_id(int $id): ?template_category {
        $all = $this->get_all();
        return $all[$id] ?? null;
    }

    /**
     * Saves the template category
     *
     * @param template_category $templatecategory
     * @return int
     * @throws \dml_transaction_exception
     */
    public function save(template_category $templatecategory) {
        global $DB;
        try {
            $transaction = $DB->start_delegated_transaction();
            $dbocategory = db_template_category::from_template_category($templatecategory);
            if ($templatecategory->get_id() === null || $templatecategory->get_id() == 0) {
                $id = $DB->insert_record(tables::TEMPLATE_CATEGORY_TABLE, $dbocategory);
                $templatecategory->set_id($id);
            } else {
                $DB->update_record(tables::TEMPLATE_CATEGORY_TABLE, $dbocategory);
            }

            // Insert or update category headers.
            foreach ($templatecategory->get_headers() as $header) {
                $dboheader = db_localized_string::from_localized_string(
                    $header,
                    localized_string_type::TEMPLATE_CATEGORY_HEADER,
                    $templatecategory->get_id()
                );

                if ($header->get_id() == 0) {
                    $DB->insert_record(tables::LOCALIZED_STRING_TABLE, $dboheader);
                } else {
                    $DB->update_record(tables::LOCALIZED_STRING_TABLE, $dboheader);
                }
            }

            // Update linked criteria.
            $DB->delete_records(tables::PARAMETRIZED_TEMPLATE_CRITERION_TABLE, ['categoryid' => $templatecategory->get_id()]);
            foreach ($templatecategory->get_template_criteria() as $criterion) {
                $dboparamcriterion = db_parametrized_criterion::from_parametrized_criterion(
                    $criterion,
                    $templatecategory->get_id()
                );
                $DB->insert_record(tables::PARAMETRIZED_TEMPLATE_CRITERION_TABLE, $dboparamcriterion);
            }

            $transaction->allow_commit();
        } catch (Exception $e) {
            $transaction->rollback($e);
        }
        return $templatecategory->get_id();
    }

    /**
     * Deletes the category template with the given id in the database
     *
     * @param int $id The id of the category template.
     * @return bool True, if successful
     */
    public function delete_by_id(int $id): bool {
        global $DB;
        try {
            $transaction = $DB->start_delegated_transaction();
            $DB->delete_records(tables::LOCALIZED_STRING_TABLE, [
                'foreignkey' => $id,
                'typeid' => localized_string_type::str2id(localized_string_type::TEMPLATE_CATEGORY_HEADER),
            ]);
            $DB->delete_records(tables::PARAMETRIZED_TEMPLATE_CRITERION_TABLE, ['categoryid' => $id]);
            $DB->delete_records(tables::TEMPLATE_CATEGORY_TABLE, ['id' => $id]);

            $transaction->allow_commit();
        } catch (Exception $e) {
            $transaction->rollback($e);
        }
        return true;
    }

    /**
     * Get all category headers hashed by category id.
     *
     * @return array
     * @throws \dml_exception
     */
    private function get_headers_by_category_ids(): array {
        global $DB;

        $headers = [];
        $rs = $DB->get_recordset(
            tables::LOCALIZED_STRING_TABLE,
            ['typeid' => localized_string_type::str2id(localized_string_type::TEMPLATE_CATEGORY_HEADER)]
        );
        foreach ($rs as $row) {
            if (!\array_key_exists($row->foreignkey, $headers)) {
                $headers[$row->foreignkey] = [];
            }
            $headers[$row->foreignkey][] = new localized_string($row->languageid, $row->id, $row->string);
        }
        $rs->close();
        return $headers;
    }

    /**
     * Get all parameterized criteria hashed by category id
     * @return array The paramterized criteria in an array hashed by category id
     * @throws \dml_exception
     */
    private function get_parameterized_criteria_by_categoryid(): array {
        global $DB;

        $critbycatid = [];
        $rs = $DB->get_recordset(tables::PARAMETRIZED_TEMPLATE_CRITERION_TABLE);
        foreach ($rs as $row) {
            if (!\array_key_exists($row->categoryid, $critbycatid)) {
                $critbycatid[$row->categoryid] = [];
            }
            $critbycatid[$row->categoryid][] = db_parametrized_criterion::to_parametrized_criterion($row);
        }
        $rs->close();
        return $critbycatid;
    }
}
