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

namespace offlinequiz_rimport\task\adhoc;

defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot . '/mod/offlinequiz/lib.php');
require_once($CFG->dirroot . '/mod/offlinequiz/locallib.php');
require_once($CFG->dirroot . '/mod/offlinequiz/evallib.php');
require_once($CFG->dirroot . '/mod/offlinequiz/report/rimport/scanner.php');

/**
 * The results import report for offlinequizzes
 *
 * @package       offlinequiz_rimport
 * @subpackage    offlinequiz
 * @author        Thomas Wedekind
 * @copyright     2025 Academic Moodle Cooperation {@link http://www.academic-moodle-cooperation.org}
 * @since         Moodle 5.0
 * @license       http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 *
 */
class scan_file extends \core\task\adhoc_task {
    /**
     * constructor of this adhoc task
     * @param int $queuedataid
     * @return scan_file
     */
    public static function instance(int $queuedataid): self {
        $task = new self();
        $task->set_custom_data((object) [
            'queuedataid' => $queuedataid,
        ]);
        return $task;
    }
    /**
     * run adhoc task
     * @return void
     */
    public function execute() {
        global $DB, $CFG;
        $data = $this->get_custom_data();
        $queuedata = $DB->get_record('offlinequiz_queue_data', [
            'id' => $data->queuedataid,
        ]);
        $queue = $DB->get_record('offlinequiz_queue', [
            'id' => $queuedata->queueid,
        ]);
        $queuedata->timestart = time();
        $queuedata->status = 'processing';
        $DB->update_record('offlinequiz_queue_data', $queuedata);
        // Set up the context for this job.
        if (
            ! $offlinequiz = $DB->get_record('offlinequiz', [
            'id' => $queue->offlinequizid,
            ])
        ) {
            $this->log_error($queuedata, 'offlinequiznotfound');
        }
        if (
            ! $course = $DB->get_record('course', [
            'id' => $offlinequiz->course,
            ])
        ) {
            $this->log_error($queuedata, 'coursenotfound');
        }

        if (! $cm = get_coursemodule_from_instance("offlinequiz", $offlinequiz->id, $course->id)) {
            $this->log_error($queuedata, 'coursemodulenotfound');
        }
        if (! $context = \context_module::instance($cm->id)) {
            $this->log_error($queuedata, 'contextnotfound');
        }
        if (! $coursecontext = \context_course::instance($course->id)) {
            $this->log_error($queuedata, 'coursecontextnotfound');
        }
        if (
            ! $groups = $DB->get_records('offlinequiz_groups', [
            'offlinequizid' => $offlinequiz->id,
            ], 'groupnumber', '*', 0, $offlinequiz->numgroups)
        ) {
            $this->log_error($queuedata, 'nogroupsfound');
        }
        $dirname = "{$CFG->dataroot}/offlinequiz/import/$queue->id";
        $importfile = "$dirname/$queuedata->filename";
        if (!file_exists($importfile)) {
            $this->restorefile($context->id, $queue->id, $queue->filename, $importfile);
        }
         [$maxquestions, $maxanswers, $formtype, $questionsperpage] = \offlinequiz_get_question_numbers($offlinequiz, $groups);

        set_time_limit(120);
        try {
            $version = $offlinequiz->algorithmversion;
            if ($version < 2) {
                // Create a new scanner for every page.
                $scanner = new \offlinequiz_page_scanner($offlinequiz, $context->id, $maxquestions, $maxanswers);
                // Try to load the image file.
                echo 'job ' . $queue->id . ': evaluating ' . $queuedata->filename . "\n";
                $scannedpage = $scanner->load_image("$dirname/$queuedata->filename");
                $scannedpage->id = $DB->get_field(
                    'offlinequiz_scanned_pages',
                    'id',
                    ['queuedataid' => $queuedata->id],
                    IGNORE_MISSING
                );
                if ($scannedpage->status == 'ok') {
                    echo 'job ' . $queue->id . ': image loaded ' . $scannedpage->filename . "\n";
                } else if ($scannedpage->error == 'filenotfound') {
                    echo 'job ' . $queue->id . ': image file not found: ' . $scannedpage->filename . "\n";
                }
                $scannedpage->offlinequizid = $offlinequiz->id;
                $scannedpage->queuedataid = $queuedata->id;

                // If we could load the image file, the status is 'ok', so we can check the page for errors.
                if ($scannedpage->status == 'ok') {
                    // We autorotate so check_scanned_page will return a potentially new scanner and the scannedpage.
                     [$scanner, $scannedpage] = \offlinequiz_check_scanned_page(
                         $offlinequiz,
                         $scanner,
                         $scannedpage,
                         $queue->importuserid,
                         $coursecontext,
                         true
                     );
                } else {
                    if (property_exists($scannedpage, 'id') && ! empty($scannedpage->id)) {
                        $DB->update_record('offlinequiz_scanned_pages', $scannedpage);
                    } else {
                        $scannedpage->id = $DB->insert_record('offlinequiz_scanned_pages', $scannedpage);
                    }
                }
                echo 'job ' . $queue->id . ': scannedpage id ' . $scannedpage->id . "\n";

                // If the status is still 'ok', we can process the answers. This potentially submits the page and
                // checks whether the result for a student is complete.
                if ($scannedpage->status == 'ok') {
                    // We can process the answers and submit them if possible.
                    $scannedpage = \offlinequiz_process_scanned_page(
                        $offlinequiz,
                        $scanner,
                        $scannedpage,
                        $queue->importuserid,
                        $questionsperpage,
                        $coursecontext,
                        true
                    );
                    echo 'job ' . $queue->id . ': processed answers for ' . $scannedpage->id . "\n";
                } else if ($scannedpage->status == 'error' && $scannedpage->error == 'resultexists') {
                    // Already process the answers but don't submit them.
                    $scannedpage = \offlinequiz_process_scanned_page(
                        $offlinequiz,
                        $scanner,
                        $scannedpage,
                        $queue->importuserid,
                        $questionsperpage,
                        $coursecontext,
                        false
                    );

                    // Compare the old and the new result wrt. the choices.
                    $scannedpage = \offlinequiz_check_different_result($scannedpage);
                }

                // If there is something to correct then store the hotspots for retrieval in correct.php.
                if (
                    $scannedpage->status != 'ok' && $scannedpage->error != 'couldnotgrab'
                    && $scannedpage->error != 'notadjusted' && $scannedpage->error != 'grouperror'
                ) {
                    $scanner->store_hotspots($scannedpage->id);
                }

                if (
                    $scannedpage->status == 'ok' || $scannedpage->status == 'submitted' ||
                      $scannedpage->status == 'suspended' || $scannedpage->error == 'missingpages'
                ) {
                    // Mark the file as processed.
                    $DB->set_field('offlinequiz_queue_data', 'status', 'processed', [
                        'id' => $queuedata->id,
                    ]);
                } else {
                    $DB->set_field('offlinequiz_queue_data', 'status', 'error', [
                        'id' => $queuedata->id,
                    ]);
                    $DB->set_field('offlinequiz_queue_data', 'error', $scannedpage->error, [
                        'id' => $queuedata->id,
                    ]);
                }
            } else {
                $contextid = 0;
                $engine = new \offlinequiz_result_import\offlinequiz_result_engine(
                    $offlinequiz,
                    $context->id,
                    $queuedata->filename,
                    0
                );
                $resultpage = $engine->scanpage();
                $engine->save_page(2);
            }
            $this->send_notifications($queue->id);
        } catch (\Exception $e) {
            echo 'job ' . $queue->id . ': ' . $e->getMessage() . "\n";
            $DB->set_field('offlinequiz_queue_data', 'status', 'error', [
                'id' => $queuedata->id,
            ]);
            $DB->set_field('offlinequiz_queue_data', 'error', 'couldnotgrab', [
                'id' => $queuedata->id,
            ]);
            $DB->set_field('offlinequiz_queue_data', 'info', $e->getMessage(), [
                'id' => $queuedata->id,
            ]);
            $scannedpage->status = 'error';
            $scannedpage->error = 'couldnotgrab';
            if ($scannedpage->id) {
                $DB->update_record('offlinequiz_scanned_pages', $scannedpage);
            } else {
                $DB->insert_record('offlinequiz_scanned_pages', $scannedpage);
            }
            $this->send_notifications($queue->id);
        }
    }
    /**
     * send notifications for a queue
     * @param mixed $queueid
     * @return void
     */
    private function send_notifications($queueid) {
        $task = \offlinequiz_rimport\task\adhoc\send_notifications::instance($queueid);
        // Execute ASAP.
        $task->set_next_run_time(time());
        \core\task\manager::queue_adhoc_task($task, true);
    }
    /**
     * restore a file to the temp folder
     * @param mixed $contextid
     * @param mixed $queuedataid
     * @param mixed $filename
     * @param mixed $restorepath
     * @return void
     */
    private function restorefile($contextid, $queuedataid, $filename, $restorepath) {
        $fs = get_file_storage();

        $pathhash = $fs->get_pathname_hash($contextid, 'mod_offlinequiz', 'queuedata', $queuedataid, '/', $filename);
        $file = $fs->get_file_by_hash($pathhash);
        $file->copy_content_to($restorepath);
    }
    /**
     * log an error
     * @param mixed $queuedata
     * @param mixed $errorcode
     * @return void
     */
    private function log_error($queuedata, $errorcode) {
        global $DB;
        $queuedata->status = 'error';
        $queuedata->error = $errorcode;
        $DB->update_record('offlinequiz_queue_data', $queuedata);
    }
}
