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

namespace mod_data;

use context_module;
use moodle_url;
use core_component;
use stdClass;

/**
 * Manager tests class for mod_data.
 *
 * @package    mod_data
 * @category   test
 * @copyright  2022 Ferran Recio <ferran@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @coversDefaultClass \mod_data\manager
 */
final class manager_test extends \advanced_testcase {

    /**
     * Test for static create methods.
     *
     * @covers ::create_from_instance
     * @covers ::create_from_coursemodule
     * @covers ::create_from_data_record
     */
    public function test_create(): void {

        $this->resetAfterTest();
        $this->setAdminUser();

        $course = $this->getDataGenerator()->create_course();
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST);
        $context = context_module::instance($cm->id);

        $manager = manager::create_from_instance($activity);
        $manageractivity = $manager->get_instance();
        $this->assertEquals($activity->id, $manageractivity->id);
        $managercm = $manager->get_coursemodule();
        $this->assertEquals($cm->id, $managercm->id);
        $managercontext = $manager->get_context();
        $this->assertEquals($context->id, $managercontext->id);

        $manager = manager::create_from_coursemodule($cm);
        $manageractivity = $manager->get_instance();
        $this->assertEquals($activity->id, $manageractivity->id);
        $managercm = $manager->get_coursemodule();
        $this->assertEquals($cm->id, $managercm->id);
        $managercontext = $manager->get_context();
        $this->assertEquals($context->id, $managercontext->id);

        $datarecord = (object)[
            'dataid' => $activity->id,
            'id' => 0,
            'userid' => 0,
            'groupid' => 0,
            'timecreated' => 0,
            'timemodified' => 0,
            'approved' => 0,
        ];
        $manager = manager::create_from_data_record($datarecord);
        $manageractivity = $manager->get_instance();
        $this->assertEquals($activity->id, $manageractivity->id);
        $managercm = $manager->get_coursemodule();
        $this->assertEquals($cm->id, $managercm->id);
        $managercontext = $manager->get_context();
        $this->assertEquals($context->id, $managercontext->id);
    }

    /**
     * Test set_module_viewed
     * @covers ::set_module_viewed
     */
    public function test_set_module_viewed(): void {
        global $CFG;

        $CFG->enablecompletion = 1;
        $this->resetAfterTest();
        $this->setAdminUser();

        // Setup test data.
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
        $instance = $this->getDataGenerator()->create_module(
            'data',
            ['course' => $course->id],
            ['completion' => 2, 'completionview' => 1]
        );
        $manager = manager::create_from_instance($instance);
        $context = $manager->get_context();
        $cm = $manager->get_coursemodule();

        // Trigger and capture the event.
        $sink = $this->redirectEvents();

        $manager->set_module_viewed($course);

        $events = $sink->get_events();
        // 2 additional events thanks to completion.
        $this->assertCount(3, $events);
        $event = array_shift($events);

        // Checking that the event contains the expected values.
        $this->assertInstanceOf('\mod_data\event\course_module_viewed', $event);
        $this->assertEquals($context, $event->get_context());
        $moodleurl = new moodle_url('/mod/data/view.php', ['id' => $cm->id]);
        $this->assertEquals($moodleurl, $event->get_url());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());

        // Check completion status.
        $completion = new \completion_info($course);
        $completiondata = $completion->get_data($cm);
        $this->assertEquals(1, $completiondata->completionstate);
    }

    /**
     * Test set_template_viewed
     * @covers ::set_template_viewed
     */
    public function test_set_template_viewed(): void {
        $this->resetAfterTest();
        $this->setAdminUser();

        // Setup test data.
        $course = $this->getDataGenerator()->create_course();
        $instance = $this->getDataGenerator()->create_module(
            'data',
            ['course' => $course->id]
        );
        $manager = manager::create_from_instance($instance);
        $context = $manager->get_context();

        // Trigger and capture the event.
        $sink = $this->redirectEvents();

        $manager->set_template_viewed();

        $events = $sink->get_events();
        $this->assertCount(1, $events);
        $event = reset($events);

        // Checking that the event contains the expected values.
        $this->assertInstanceOf('mod_data\event\template_viewed', $event);
        $this->assertEquals($context, $event->get_context());
        $moodleurl = new moodle_url('/mod/data/templates.php', ['d' => $instance->id]);
        $this->assertEquals($moodleurl, $event->get_url());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());
    }

    /**
     * Test for has_records().
     *
     * @covers ::has_records
     */
    public function test_has_records(): void {
        global $DB;

        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $data = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $manager = manager::create_from_instance($data);

        // Empty database should return false.
        $this->assertFalse($manager->has_records());

        // Create data record.
        $datarecords = new \stdClass();
        $datarecords->userid = '2';
        $datarecords->dataid = $data->id;
        $datarecords->id = $DB->insert_record('data_records', $datarecords);

        // Database with records should return true.
        $this->assertTrue($manager->has_records());
    }

    /**
     * Test for has_fields().
     *
     * @covers ::has_fields
     */
    public function test_has_fields(): void {
        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $manager = manager::create_from_instance($activity);

        // Empty database should return false.
        $this->assertFalse($manager->has_fields());

        // Add a field to the activity.
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = new \stdClass();
        $fieldrecord->name = 'field1';
        $fieldrecord->type = 'text';
        $datagenerator->create_field($fieldrecord, $activity);

        // Database with fields should return true.
        $this->assertTrue($manager->has_fields());
    }

    /**
     * Test for get_available_presets().
     *
     * @covers ::get_available_presets
     */
    public function test_get_available_presets(): void {
        global $DB;

        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $this->setUser($user);

        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST);

        // Check available presets meet the datapreset plugins when there are no any preset saved by users.
        $datapresetplugins = core_component::get_plugin_list('datapreset');
        $manager = manager::create_from_coursemodule($cm);
        $presets = $manager->get_available_presets();
        $this->assertCount(count($datapresetplugins), $presets);
        // Confirm that, at least, the "Image gallery" is one of them.
        $namepresets = array_map(function($preset) {
            return $preset->name;
        }, $presets);
        $this->assertContains('Image gallery', $namepresets);

        // Login as admin and create some presets saved manually by users.
        $this->setAdminUser();
        $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $savedpresets = [];
        for ($i = 1; $i <= 3; $i++) {
            $preset = (object) [
                'name' => 'Preset name ' . $i,
            ];
            $plugingenerator->create_preset($activity, $preset);
            $savedpresets[] = $preset;
        }
        $savedpresetsnames = array_map(function($preset) {
            return $preset->name;
        }, $savedpresets);
        $this->setUser($user);

        // Check available presets meet the datapreset plugins + presets saved manually by users.
        $presets = $manager->get_available_presets();
        $this->assertCount(count($datapresetplugins) + count($savedpresets), $presets);
        // Confirm that, apart from the "Image gallery" preset, the ones created manually have been also returned.
        $namepresets = array_map(function($preset) {
            return $preset->name;
        }, $presets);
        $this->assertContains('Image gallery', $namepresets);
        foreach ($savedpresets as $savedpreset) {
            $this->assertContains($savedpreset->name, $namepresets);
        }
        // Check all the presets have the proper value for the isplugin attribute.
        foreach ($presets as $preset) {
            if (in_array($preset->name, $savedpresetsnames)) {
                $this->assertFalse($preset->isplugin);
            } else {
                $this->assertTrue($preset->isplugin);
            }
        }

        // Unassign the capability to the teacher role and check that only plugin presets are returned (because the saved presets
        // have been created by admin).
        $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
        unassign_capability('mod/data:viewalluserpresets', $teacherrole->id);
        $presets = $manager->get_available_presets();
        $this->assertCount(count($datapresetplugins), $presets);
        // Confirm that, at least, the "Image gallery" is one of them.
        $namepresets = array_map(function($preset) {
            return $preset->name;
        }, $presets);
        $this->assertContains('Image gallery', $namepresets);
        foreach ($savedpresets as $savedpreset) {
            $this->assertNotContains($savedpreset->name, $namepresets);
        }

        // Create a preset with the current user and check that, although the viewalluserpresets is not assigned to the teacher
        // role, the preset is returned because the teacher is the owner.
        $savedpreset = (object) [
            'name' => 'Preset created by teacher',
        ];
        $plugingenerator->create_preset($activity, $savedpreset);
        $presets = $manager->get_available_presets();
        // The presets total is all the plugin presets plus the preset created by the teacher.
        $this->assertCount(count($datapresetplugins) + 1, $presets);
        // Confirm that, at least, the "Image gallery" is one of them.
        $namepresets = array_map(function($preset) {
            return $preset->name;
        }, $presets);
        $this->assertContains('Image gallery', $namepresets);
        // Confirm that savedpresets are still not returned.
        foreach ($savedpresets as $savedpreset) {
            $this->assertNotContains($savedpreset->name, $namepresets);
        }
        // Confirm the new preset created by the teacher is returned too.
        $this->assertContains('Preset created by teacher', $namepresets);
    }

    /**
     * Test for get_available_plugin_presets().
     *
     * @covers ::get_available_plugin_presets
     */
    public function test_get_available_plugin_presets(): void {
        $this->resetAfterTest();
        $this->setAdminUser();

        $course = $this->getDataGenerator()->create_course();
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);

        // Check available plugin presets meet the datapreset plugins.
        $datapresetplugins = core_component::get_plugin_list('datapreset');
        $manager = manager::create_from_instance($activity);
        $presets = $manager->get_available_plugin_presets();
        $this->assertCount(count($datapresetplugins), $presets);
        // Confirm that, at least, the "Image gallery" is one of them.
        $namepresets = array_map(function($preset) {
            return $preset->name;
        }, $presets);
        $this->assertContains('Image gallery', $namepresets);

        // Create a preset saved manually by users.
        $savedpreset = (object) [
            'name' => 'Preset name 1',
        ];
        $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $plugingenerator->create_preset($activity, $savedpreset);

        // Check available plugin presets don't contain the preset saved manually.
        $presets = $manager->get_available_plugin_presets();
        $this->assertCount(count($datapresetplugins), $presets);
        // Confirm that, at least, the "Image gallery" is one of them.
        $namepresets = array_map(function($preset) {
            return $preset->name;
        }, $presets);
        $this->assertContains('Image gallery', $namepresets);
        // Confirm that the preset saved manually hasn't been returned.
        $this->assertNotContains($savedpreset->name, $namepresets);
        // Check all the presets have the proper value for the isplugin attribute.
        foreach ($presets as $preset) {
            $this->assertTrue($preset->isplugin);
        }
    }

    /**
     * Test for get_available_saved_presets().
     *
     * @covers ::get_available_saved_presets
     */
    public function test_get_available_saved_presets(): void {
        global $DB;

        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $this->setUser($user);

        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST);

        // Check available saved presets is empty (because, for now, no user preset has been created).
        $manager = manager::create_from_coursemodule($cm);
        $presets = $manager->get_available_saved_presets();
        $this->assertCount(0, $presets);

        // Create some presets saved manually by the admin user.
        $this->setAdminUser();
        $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $savedpresets = [];
        for ($i = 1; $i <= 3; $i++) {
            $preset = (object) [
                'name' => 'Preset name ' . $i,
            ];
            $plugingenerator->create_preset($activity, $preset);
            $savedpresets[] = $preset;
        }
        // Create one more preset saved manually by the teacher user.
        $this->setUser($user);
        $teacherpreset = (object) [
            'name' => 'Preset created by teacher',
        ];
        $plugingenerator->create_preset($activity, $teacherpreset);
        $savedpresets[] = $teacherpreset;

        $savedpresetsnames = array_map(function($preset) {
            return $preset->name;
        }, $savedpresets);

        // Check available saved presets only contain presets saved manually by users.
        $presets = $manager->get_available_saved_presets();
        $this->assertCount(count($savedpresets), $presets);
        // Confirm that it contains only the presets created manually.
        foreach ($presets as $preset) {
            $this->assertContains($preset->name, $savedpresetsnames);
            $this->assertFalse($preset->isplugin);
        }

        // Unassign the mod/data:viewalluserpresets capability to the teacher role and check that saved presets are not returned.
        $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
        unassign_capability('mod/data:viewalluserpresets', $teacherrole->id);

        $presets = $manager->get_available_saved_presets();
        $this->assertCount(1, $presets);
        $preset = reset($presets);
        $this->assertEquals($teacherpreset->name, $preset->name);
    }

    /**
     * Test for can_view_preset().
     *
     * @covers ::can_view_preset
     * @dataProvider can_view_preset_provider
     * @param string $rolename the user role name
     * @param bool $ownpreset if the preset belongs to the user
     * @param bool|null $useridparam if the method should be called with a user id param
     * @param bool $plugin if the preset is a plugin or not
     * @param bool $expected the expected result
     */
    public function test_can_view_preset(string $rolename, bool $ownpreset, ?bool $useridparam, bool $plugin, bool $expected): void {

        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $user = $this->getDataGenerator()->create_and_enrol($course, $rolename);
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST);
        $manager = manager::create_from_coursemodule($cm);

        // Create preset.
        if ($ownpreset) {
            $this->setUser($user);
        } else {
            $this->setAdminUser();
        }

        if ($plugin) {
            $preset = preset::create_from_plugin($manager, 'imagegallery');
        } else {
            $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
            $preset = $plugingenerator->create_preset($activity, (object)['name' => 'Preset name']);
        }

        // Setup user param.
        if ($useridparam) {
            // Login as a different user to validate the userid param is working.
            $otheruser = $this->getDataGenerator()->create_user();
            $this->setUser($otheruser);
            $useridparam = $user->id;
        } else {
            $this->setUser($user);
        }

        $result = $manager->can_view_preset($preset, $useridparam);
        $this->assertEquals($expected, $result);
    }

    /**
     * Data provider for test_can_view_preset.
     *
     * @return array
     */
    public static function can_view_preset_provider(): array {
        return [
            // User presets.
            'Teacher owned preset without user id param' => [
                'rolename' => 'editingteacher',
                'ownpreset' => true,
                'useridparam' => null,
                'plugin' => false,
                'expected' => true,
            ],
            'Teacher owned preset with user id param' => [
                'rolename' => 'editingteacher',
                'ownpreset' => true,
                'useridparam' => true,
                'plugin' => false,
                'expected' => true,
            ],
            'Teacher not owned preset without user id param' => [
                'rolename' => 'editingteacher',
                'ownpreset' => false,
                'useridparam' => null,
                'plugin' => false,
                'expected' => true,
            ],
            'Teacher not owned preset with user id param' => [
                'rolename' => 'editingteacher',
                'ownpreset' => false,
                'useridparam' => true,
                'plugin' => false,
                'expected' => true,
            ],
            'Student owned preset without user id param' => [
                'rolename' => 'student',
                'ownpreset' => true,
                'useridparam' => null,
                'plugin' => false,
                'expected' => true,
            ],
            'Student owned preset with user id param' => [
                'rolename' => 'student',
                'ownpreset' => true,
                'useridparam' => true,
                'plugin' => false,
                'expected' => true,
            ],
            'Student not owned preset without user id param' => [
                'rolename' => 'student',
                'ownpreset' => false,
                'useridparam' => null,
                'plugin' => false,
                'expected' => false,
            ],
            'Student not owned preset with user id param' => [
                'rolename' => 'student',
                'ownpreset' => false,
                'useridparam' => true,
                'plugin' => false,
                'expected' => false,
            ],
            // Plugin presets.
            'Teacher plugin preset without user id param' => [
                'rolename' => 'editingteacher',
                'ownpreset' => false,
                'useridparam' => null,
                'plugin' => true,
                'expected' => true,
            ],
            'Teacher plugin preset with user id param' => [
                'rolename' => 'editingteacher',
                'ownpreset' => false,
                'useridparam' => true,
                'plugin' => true,
                'expected' => true,
            ],
            'Student plugin preset without user id param' => [
                'rolename' => 'student',
                'ownpreset' => false,
                'useridparam' => null,
                'plugin' => true,
                'expected' => true,
            ],
            'Student plugin preset with user id param' => [
                'rolename' => 'student',
                'ownpreset' => false,
                'useridparam' => true,
                'plugin' => true,
                'expected' => true,
            ],
        ];
    }

    /**
     * Test for can_export_entries().
     *
     * @covers ::can_export_entries
     */
    public function test_can_export_entries(): void {
        global $DB;

        $this->resetAfterTest();

        // Create course with activity and enrol users.
        $course = $this->getDataGenerator()->create_course();
        $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $cm = get_coursemodule_from_id(manager::MODULE, $activity->cmid, 0, false, MUST_EXIST);
        $manager = manager::create_from_coursemodule($cm);

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $activity);

        // Teacher with default capabilities can export entries.
        $this->setUser($teacher);
        $result = $manager->can_export_entries();
        $this->assertEquals(true, $result);

        // Teacher without exportallentries can still export entries.
        unassign_capability('mod/data:exportallentries', $teacherrole->id);
        $result = $manager->can_export_entries();
        $this->assertEquals(true, $result);

        // Teacher without exportallentries and exportentry can't export entries (unless they have created some entries).
        unassign_capability('mod/data:exportentry', $teacherrole->id);
        $result = $manager->can_export_entries();
        $this->assertEquals(false, $result);

        $generator->create_entry(
            $activity,
            [$field->field->id => 'Example entry'],
        );
        $result = $manager->can_export_entries();
        $this->assertEquals(true, $result);

        // Student without entries can't export.
        $this->setUser($student);
        $result = $manager->can_export_entries();
        $this->assertEquals(false, $result);

        // However, student who has created any entry, can export.
        $generator->create_entry(
            $activity,
            [$field->field->id => 'Another example entry'],
        );
        $this->setUser($student);
        $result = $manager->can_export_entries();
        $this->assertEquals(true, $result);
    }

    /*
     * Test reset_all_templates.
     *
     * @covers ::reset_all_templates
     */
    public function test_reset_all_templates(): void {
        global $DB;

        $this->resetAfterTest();
        $this->setAdminUser();

        // Setup test data.
        $course = $this->getDataGenerator()->create_course();
        $instance = $this->getDataGenerator()->create_module(
            'data',
            ['course' => $course->id]
        );
        $manager = manager::create_from_instance($instance);

        // Create some initial templates.
        $initialtemplates = new stdClass();
        foreach (manager::TEMPLATES_LIST as $templatename => $unused) {
            $initialtemplates->$templatename = "Initial $templatename";
        }
        $manager->update_templates($initialtemplates);
        $instance = $manager->get_instance();
        $record = $DB->get_record('data', ['id' => $instance->id]);
        foreach (manager::TEMPLATES_LIST as $templatename => $unused) {
            $this->assertEquals($initialtemplates->$templatename, $instance->$templatename);
            $this->assertEquals($initialtemplates->$templatename, $record->$templatename);
        }

        // Reset all templates.
        $result = $manager->reset_all_templates();
        $this->assertTrue($result);
        $instance = $manager->get_instance();
        $record = $DB->get_record('data', ['id' => $instance->id]);
        foreach (manager::TEMPLATES_LIST as $templatename => $unused) {
            $this->assertEquals('', $instance->$templatename);
            $this->assertEquals('', $record->$templatename);
        }
    }

    /**
     * Test reset_template.
     *
     * @covers ::reset_template
     * @dataProvider reset_template_provider
     * @param string $templatetoreset the template to reset
     * @param string[] $expected the expected templates to be reset
     */
    public function test_reset_template(string $templatetoreset, array $expected): void {
        global $DB;

        $this->resetAfterTest();
        $this->setAdminUser();

        // Setup test data.
        $course = $this->getDataGenerator()->create_course();
        $instance = $this->getDataGenerator()->create_module(
            'data',
            ['course' => $course->id]
        );
        $manager = manager::create_from_instance($instance);

        // Create some initial templates.
        $initialtemplates = new stdClass();
        foreach (manager::TEMPLATES_LIST as $templatename => $unused) {
            $initialtemplates->$templatename = "Initial $templatename";
        }
        $manager->update_templates($initialtemplates);
        $instance = $manager->get_instance();
        $record = $DB->get_record('data', ['id' => $instance->id]);
        foreach (manager::TEMPLATES_LIST as $templatename => $unused) {
            $this->assertEquals($initialtemplates->$templatename, $instance->$templatename);
            $this->assertEquals($initialtemplates->$templatename, $record->$templatename);
        }

        // Reset template.
        $result = $manager->reset_template($templatetoreset);
        $this->assertTrue($result);
        $instance = $manager->get_instance();
        $record = $DB->get_record('data', ['id' => $instance->id]);
        foreach (manager::TEMPLATES_LIST as $templatename => $unused) {
            if (in_array($templatename, $expected)) {
                $this->assertEquals('', $instance->$templatename);
                $this->assertEquals('', $record->$templatename);
            } else {
                $this->assertEquals($initialtemplates->$templatename, $instance->$templatename);
                $this->assertEquals($initialtemplates->$templatename, $record->$templatename);
            }
        }
    }

    /**
     * Data provider for test_reset_templatet.
     *
     * @return array
     */
    public static function reset_template_provider(): array {
        return [
            // User presets.
            'listtemplate' => [
                'templatetoreset' => 'listtemplate',
                'expected' => ['listtemplate', 'listtemplateheader', 'listtemplatefooter'],
            ],
            'singletemplate' => [
                'templatetoreset' => 'singletemplate',
                'expected' => ['singletemplate'],
            ],
            'asearchtemplate' => [
                'templatetoreset' => 'asearchtemplate',
                'expected' => ['asearchtemplate'],
            ],
            'addtemplate' => [
                'templatetoreset' => 'addtemplate',
                'expected' => ['addtemplate'],
            ],
            'rsstemplate' => [
                'templatetoreset' => 'rsstemplate',
                'expected' => ['rsstemplate', 'rsstitletemplate'],
            ],
            'csstemplate' => [
                'templatetoreset' => 'csstemplate',
                'expected' => ['csstemplate'],
            ],
            'jstemplate' => [
                'templatetoreset' => 'jstemplate',
                'expected' => ['jstemplate'],
            ],
        ];
    }

    /**
     * Test for get_all_entries().
     *
     * @covers ::get_all_entries
     */
    public function test_get_all_entries(): void {
        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $data = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
        $manager = manager::create_from_instance($data);

        // Empty database should return empty array.
        $this->assertEmpty($manager->get_all_entries());

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $data);
        $generator->create_entry(
            $data,
            [$field->field->id => 'Example entry'],
        );
        $this->assertCount(1, $manager->get_all_entries());
    }

    /**
     * Test for get_all_entries() with groups parameter.
     *
     * @covers ::get_all_entries
     */
    public function test_get_all_entries_with_groups(): void {
        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $g1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
        $g2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);

        $data = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course, 'groupmode' => SEPARATEGROUPS]);
        $manager = manager::create_from_instance($data);

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $data);
        $generator->create_entry(
            $data,
            [$field->field->id => 'Example entry'],
        );

        // Create entries for each group.
        $generator->create_entry(
            $data,
            [$field->field->id => 'G1'],
            $g1->id,
        );
        $generator->create_entry(
            $data,
            [$field->field->id => 'G2'],
            $g2->id,
        );

        $this->assertCount(3, $manager->get_all_entries([]));
        $this->assertCount(2, $manager->get_all_entries([$g1->id => $g1]));
        $this->assertCount(3, $manager->get_all_entries([$g1->id => $g1, $g2->id => $g2]));
        // Non-existing group. It returns the entries with no group.
        $this->assertCount(1, $manager->get_all_entries([666 => null]));
    }

    /**
     * Test filter_entries_by_user.
     *
     * @covers ::filter_entries_by_user
     * @dataProvider provider_test_filter_entries_by_user
     *
     * @param array $entries
     * @param array $myentries
     * @param int $expected
     * @return void
     */
    public function test_filter_entries_by_user(
        array $entries,
        array $myentries,
        int $expected,
    ): void {
        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $this->setAdminUser();

        $activity = $this->getDataGenerator()->create_module(
            manager::MODULE,
            ['course' => $course, 'approval' => 1],
        );

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $activity);
        foreach ($entries as $entry => $approved) {
            $generator->create_entry(
                $activity,
                [$field->field->id => 'Example entry: ' . $entry],
                0,
                [],
                ['approved' => $approved],
            );
        }

        $currentuser = $this->getDataGenerator()->create_and_enrol($course, 'student');
        $this->setUser($currentuser);
        foreach ($myentries as $entry => $approved) {
            $generator->create_entry(
                $activity,
                [$field->field->id => 'Example entry: ' . $entry],
                0,
                [],
                ['approved' => $approved],
            );
        }

        $manager = manager::create_from_instance($activity);
        $allentries = $manager->get_all_entries();
        $this->assertCount($expected, $manager->filter_entries_by_user($allentries, $currentuser->id));
    }

    /**
     * Data provider for test_filter_entries_by_user.
     *
     * @return array
     */
    public static function provider_test_filter_entries_by_user(): array {
        return [
            'Empty database filtered by current user' => [
                'entries' => [],
                'myentries' => [],
                'expected' => 0,
            ],
            'User without own entries filtered by current user' => [
                'entries' => [1, 0],
                'myentries' => [],
                'expected' => 0,
            ],
            'User with own entries filtered by current user' => [
                'entries' => [1, 0],
                'myentries' => [1, 0],
                'expected' => 2,
            ],
        ];
    }

    /**
     * Test filter_entries_by_approval.
     *
     * @covers ::filter_entries_by_approval
     * @dataProvider provider_test_filter_entries_by_approval
     *
     * @param array $entries
     * @param int $approvalfilter
     * @param int $expected
     * @return void
     */
    public function test_filter_entries_by_approval(
        array $entries,
        int $approvalfilter,
        int $expected,
    ): void {
        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $this->setAdminUser();

        $activity = $this->getDataGenerator()->create_module(
            manager::MODULE,
            ['course' => $course, 'approval' => 1],
        );

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $activity);
        foreach ($entries as $entry => $approved) {
            $generator->create_entry(
                $activity,
                [$field->field->id => 'Example entry: ' . $entry],
                0,
                [],
                ['approved' => $approved],
            );
        }

        $manager = manager::create_from_instance($activity);
        $allentries = $manager->get_all_entries();

        $this->assertCount($expected, $manager->filter_entries_by_approval($allentries, $approvalfilter));
    }

    /**
     * Data provider for test_filter_entries_by_approval.
     *
     * @return array
     */
    public static function provider_test_filter_entries_by_approval(): array {
        return [
            'Empty database filtered by approved' => [
                'entries' => [],
                'approvalfilter' => 1,
                'expected' => 0,
            ],
            'Database with entries filtered by approved' => [
                'entries' => [1, 0, 1, 1],
                'approvalfilter' => 1,
                'expected' => 3,
            ],
            'Database with entries filtered by non approved' => [
                'entries' => [1, 0, 1, 1],
                'approvalfilter' => 0,
                'expected' => 1,
            ],
        ];
    }

    /**
     * Test get_comments.
     *
     * @covers ::get_comments
     * @dataProvider provider_test_get_comments
     *
     * @param array $entries
     * @param int $expected
     * @param ?int $approvalfilter
     * @return void
     */
    public function test_get_comments(
        array $entries,
        int $expected,
        ?int $approvalfilter = null,
    ): void {
        global $CFG;

        $this->resetAfterTest();

        $CFG->usecomments = true;

        $course = $this->getDataGenerator()->create_course(['enablecomment' => 1]);
        $this->setAdminUser();

        $activity = $this->getDataGenerator()->create_module(
            manager::MODULE,
            ['course' => $course, 'approval' => 1, 'comments' => 1],
        );

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $activity);
        foreach ($entries as $entry) {
            $entryid = $generator->create_entry(
                $activity,
                [$field->field->id => 'Example entry'],
                0,
                [],
                ['approved' => $entry['approved']],
            );
            if ($entry['comments']) {
                $commentdata = [
                    [
                        'contextlevel' => 'module',
                        'instanceid' => $activity->cmid,
                        'component' => 'mod_data',
                        'content' => 'abc',
                        'itemid' => $entryid,
                        'area' => 'database_entry',
                    ],
                ];
                \core_comment_external::add_comments($commentdata);
            }
        }
        $manager = manager::create_from_instance($activity);
        $this->assertCount($expected, $manager->get_comments(approved: $approvalfilter));
    }

    /**
     * Data provider for test get_comments function.
     *
     * @return array
     */
    public static function provider_test_get_comments(): array {
        return [
            'No comments - no filter' => [
                'entries' => [
                    ['approved' => 1, 'comments' => false],
                    ['approved' => 0, 'comments' => false],
                ],
                'expected' => 0,
                'approvalfilter' => null,
            ],
            'No comments - filtered by approved' => [
                'entries' => [
                    ['approved' => 1, 'comments' => false],
                    ['approved' => 0, 'comments' => false],
                ],
                'expected' => 0,
                'approvalfilter' => 1,
            ],
            'No comments - filtered by pending' => [
                'entries' => [
                    ['approved' => 1, 'comments' => false],
                    ['approved' => 0, 'comments' => false],
                ],
                'expected' => 0,
                'approvalfilter' => 0,
            ],
            'With comments - no filter' => [
                'entries' => [
                    ['approved' => 1, 'comments' => true],
                    ['approved' => 0, 'comments' => true],
                ],
                'expected' => 2,
                'approvalfilter' => null,
            ],
            'With comments - filtered by approved' => [
                'entries' => [
                    ['approved' => 1, 'comments' => true],
                    ['approved' => 0, 'comments' => true],
                ],
                'expected' => 1,
                'approvalfilter' => 1,
            ],
            'With comments - filtered by pending' => [
                'entries' => [
                    ['approved' => 1, 'comments' => true],
                    ['approved' => 0, 'comments' => true],
                ],
                'expected' => 1,
                'approvalfilter' => 0,
            ],
        ];
    }

    /**
     * Test get_comments with groups parameter.
     *
     * @covers ::get_comments
     */
    public function test_get_comments_with_groups(): void {
        global $CFG;

        $this->resetAfterTest();
        $this->setAdminUser();

        $CFG->usecomments = true;

        $course = $this->getDataGenerator()->create_course();
        $g1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
        $g2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);

        $data = $this->getDataGenerator()->create_module(
            manager::MODULE,
            ['course' => $course, 'approval' => 1, 'comments' => 1, 'groupmode' => SEPARATEGROUPS],
        );

        // Add a field.
        /** @var \mod_data_generator $generator */
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldrecord = (object)[
            'name' => 'myfield',
            'type' => 'text',
        ];
        $field = $generator->create_field($fieldrecord, $data);

        $comments = [];
        $comment = [
            'contextlevel' => 'module',
            'instanceid' => $data->cmid,
            'component' => 'mod_data',
            'content' => 'abc',
            'area' => 'database_entry',
        ];

        $nogroupsentry = $generator->create_entry(
            $data,
            [$field->field->id => 'Example entry'],
        );
        $comment['itemid'] = $nogroupsentry;
        $comments[] = $comment;

        // Create entries for each group.
        $g1entry = $generator->create_entry(
            $data,
            [$field->field->id => 'G1'],
            $g1->id,
        );
        $comment['itemid'] = $g1entry;
        $comments[] = $comment;

        $g2entry = $generator->create_entry(
            $data,
            [$field->field->id => 'G2'],
            $g2->id,
        );
        $comment['itemid'] = $g2entry;
        $comments[] = $comment;

        \core_comment_external::add_comments($comments);
        $manager = manager::create_from_instance($data);

        $this->assertCount(3, $manager->get_comments());
        $this->assertCount(2, $manager->get_comments(groups: [$g1->id => $g1]));
        $this->assertCount(3, $manager->get_comments(groups: [$g1->id => $g1, $g2->id => $g2]));
        // Non-existing group. It returns the entries with no group.
        $this->assertCount(1, $manager->get_comments(groups: [666 => null]));
    }

    /**
     * Test get_approval_requested.
     *
     * @covers ::get_approval_requested
     */
    public function test_get_approval_requested(): void {
        $this->resetAfterTest();

        $course = $this->getDataGenerator()->create_course();
        $this->setAdminUser();

        $activity = $this->getDataGenerator()->create_module(
            manager::MODULE,
            ['course' => $course, 'approval' => 1],
        );
        $manager = manager::create_from_instance($activity);
        $this->assertEquals(1, $manager->get_approval_requested());

        $activity = $this->getDataGenerator()->create_module(
            manager::MODULE,
            ['course' => $course, 'approval' => 0],
        );
        $manager = manager::create_from_instance($activity);
        $this->assertEquals(0, $manager->get_approval_requested());
    }
}
