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

/**
 * Control and manage booking dates.
 *
 * @package mod_booking
 * @copyright 2025 Wunderbyte GmbH <info@wunderbyte.at>
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace mod_booking\option\fields;

use mod_booking\local\certificate_conditions\option_conditions_info;
use mod_booking\booking_option;
use mod_booking\booking_option_settings;
use mod_booking\option\fields_info;
use mod_booking\option\field_base;
use mod_booking\singleton_service;
use mod_booking\utils\wb_payment;
use tool_certificate\certificate as toolCertificate;
use MoodleQuickForm;
use stdClass;

/**
 * Class to handle one property of the booking_option_settings class.
 *
 * @copyright Wunderbyte GmbH <info@wunderbyte.at>
 * @author Georg Maißer‚‚
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class certificate extends field_base {
    /**
     * This ID is used for sorting execution.
     * @var int
     */
    public static $id = MOD_BOOKING_OPTION_FIELD_CERTIFICATE;

    /**
     * Some fields are saved with the booking option...
     * This is normal behaviour.
     * Some can be saved only post save (when they need the option id).
     * @var int
     */
    public static $save = MOD_BOOKING_EXECUTION_NORMAL;

    /**
     * This identifies the header under which this particular field should be displayed.
     * @var string
     */
    public static $header = MOD_BOOKING_HEADER_CERTIFICATE;

    /**
     * An int value to define if this field is standard or used in a different context.
     * @var array
     */
    public static $fieldcategories = [MOD_BOOKING_OPTION_FIELD_STANDARD];

    /**
     * Additionally to the classname, there might be others keys which should instantiate this class.
     * @var array
     */
    public static $alternativeimportidentifiers = [];

    /**
     * This is an array of incompatible field ids.
     * @var array
     */
    public static $incompatiblefields = [];

    /**
     * List the expiration fields to be stored in json files.
     *
     * @var array
     */
    public static $certificatedatekeys = [
        'expirydateabsolute',
        'expirydaterelative',
        'expirydatetype',
        'certificaterequiresotheroptions',
        'certificaterequiredoptionsmode',
    ];


    /**
     * This function interprets the value from the form and, if useful...
     * ... relays it to the new option class for saving or updating.
     * @param stdClass $formdata
     * @param stdClass $newoption
     * @param int $updateparam
     * @param ?mixed $returnvalue
     * @return array
     */
    public static function prepare_save_field(
        stdClass &$formdata,
        stdClass &$newoption,
        int $updateparam,
        $returnvalue = null
    ): array {

        if (!class_exists('tool_certificate\certificate')) {
            return [];
        }

        $instance = new certificate();
        $changes = [];
        $key = fields_info::get_class_name(static::class);
        $value = $formdata->{$key} ?? null;
        $mockdata = new stdClass();
        $mockdata->id = $formdata->id;

        if (!empty($value)) {
            booking_option::add_data_to_json($newoption, $key, $formdata->{$key});
        } else {
            booking_option::remove_key_from_json($newoption, $key);
        }

        $certificatechanges = $instance->check_for_changes($formdata, $instance, $mockdata, $key, $value);
        if (!empty($certificatechanges)) {
            $changes[$key] = $certificatechanges;
        };

        // Add expiration key to json.
        $keys = self::$certificatedatekeys;
        // Process each field and save it to json ONLY if certificate has been selected.
        foreach ($keys as $key) {
            $valueexpirydate = $formdata->{$key} ?? null;

            if (!empty($valueexpirydate) && !empty($value)) {
                booking_option::add_data_to_json($newoption, $key, $formdata->{$key});
            } else {
                booking_option::remove_key_from_json($newoption, $key);
            }

            $certificatechanges = $instance->check_for_changes($formdata, $instance, $mockdata, $key, $value);
            if (!empty($certificatechanges) && !empty($value)) {
                $changes[$key] = $certificatechanges;
            };
        }

        option_conditions_info::save_tagged_conditions_from_option_form((array)$formdata);

        // We can return an warning message here.
        return ['changes' => $changes];
    }

    /**
     * Instance form definition
     * @param MoodleQuickForm $mform
     * @param array $formdata
     * @param array $optionformconfig
     * @param array $fieldstoinstanciate
     * @param bool $applyheader
     * @return void
     *
     */
    public static function instance_form_definition(
        MoodleQuickForm &$mform,
        array &$formdata,
        array $optionformconfig,
        $fieldstoinstanciate = [],
        $applyheader = true
    ) {

        if (
            !class_exists('tool_certificate\certificate')
            || (empty(get_config('booking', 'certificateon')))
        ) {
            return;
        }
        if ($applyheader) {
                fields_info::add_header_to_mform($mform, self::$header);
        }
        // Check if PRO version is activated.
        if (wb_payment::pro_version_is_activated()) {
            global $DB;
            if (empty(get_config('booking', 'certificateoptions'))) {
                // Standardfunctionality to add a header to the mform (only if its not yet there).
                $records = $DB->get_records('tool_certificate_templates', []);
                $selection = [0 => 'no certificate selected'];
                foreach ($records as $record) {
                    $selection[$record->id] = $record->name;
                }

                $mform->addElement('autocomplete', 'certificate', get_string('certificate', 'mod_booking'), $selection, []);
                $mform->setType('certificate', PARAM_INT);

                toolCertificate::add_expirydate_to_form($mform);

                $bookingoptions = [
                'tags' => false,
                'multiple' => true,
                'noselectionstring' => get_string('choose...', 'mod_booking'),
                'ajax' => 'mod_booking/form_booking_options_selector',
                'valuehtmlcallback' => function ($value) {
                    global $OUTPUT;
                    if (empty($value)) {
                        return get_string('choose...', 'mod_booking');
                    }
                    $optionsettings = singleton_service::get_instance_of_booking_option_settings((int)$value);
                    $instancesettings = singleton_service::get_instance_of_booking_settings_by_cmid($optionsettings->cmid);

                    $details = (object)[
                        'id' => $optionsettings->id,
                        'titleprefix' => $optionsettings->titleprefix,
                        'text' => $optionsettings->text,
                        'instancename' => $instancesettings->name,
                    ];
                    return $OUTPUT->render_from_template(
                        'mod_booking/form_booking_options_selector_suggestion',
                        $details
                    );
                },
                ];

                $mform->addElement(
                    'autocomplete',
                    'certificaterequiresotheroptions',
                    get_string('certificaterequiresotheroptions', 'mod_booking'),
                    [],
                    $bookingoptions
                );
                $mform->addHelpButton('certificaterequiresotheroptions', 'certificaterequiresotheroptions', 'mod_booking');

                $mform->addElement(
                    'advcheckbox',
                    'certificaterequiredoptionsmode',
                    get_string('certificaterequiredoptionsmode', 'mod_booking'),
                    get_string('certificaterequiresone', 'mod_booking'),
                    [],
                    [0, 1]
                );
                $mform->addHelpButton(
                    'certificaterequiredoptionsmode',
                    'certificaterequiredoptionsmode',
                    'mod_booking'
                );
                $mform->setDefault('certificaterequiredoptionsmode', 0);
                $mform->hideIf('certificaterequiredoptionsmode', 'certificaterequiresotheroptions', 'eq', '');
            } else {
                option_conditions_info::add_static_info_to_mform($mform, $formdata);
            }
        } else {
            // If PRO version is not activated, we don't show the certificate field.
            // We can add a static text to inform the user.
            $mform->addElement(
                'static',
                'nolicenseforcertificate',
                get_string('licensekeycfg', 'mod_booking'),
                get_string('licensekeycfgdesc', 'mod_booking')
            );
        }
    }

    /**
     * This function adds error keys for form validation.
     * @param array $data
     * @param array $files
     * @param array $errors
     * @return array
     */
    public static function validation(array $data, array $files, array &$errors) {
        return $errors;
    }


    /**
     * Function to set the Data for the form.
     *
     * @param stdClass $data
     * @param booking_option_settings $settings
     *
     * @return void
     *
     */
    public static function set_data(stdClass &$data, booking_option_settings $settings) {

        if (!class_exists('tool_certificate\certificate')) {
            return;
        }
        // Add expiration key to set_data.
        $keys = self::$certificatedatekeys;
        // Process each field and save it to set_data.
        foreach ($keys as $key) {
            $valueexpirydate = $data->{$key} ?? null;

            if (!empty($valueexpirydate) && !empty($data->importing)) {
                $data->{$key} = $data->{$key} ?? booking_option::get_value_of_json_by_key((int) $data->id, $key) ?? 0;
            } else {
                $data->{$key} = booking_option::get_value_of_json_by_key((int) $data->id, $key) ?? 0;
            }
        }
        $key = fields_info::get_class_name(static::class);
        // Normally, we don't call set data after the first time loading.
        if (!empty($data->importing)) {
            $data->{$key} = $data->{$key} ?? booking_option::get_value_of_json_by_key((int) $data->id, $key) ?? 0;
        } else {
            $data->{$key} = booking_option::get_value_of_json_by_key((int) $data->id, $key) ?? 0;
        }

        // Load tagged conditions for the option.
        if (!empty($data->id)) {
            $taggedconditionids = option_conditions_info::get_tagged_condition_ids_for_option((int)$data->id);
            $data->taggedconditions = $taggedconditionids;
        }
    }

    /**
     * Return values for bookingoption_updated event.
     *
     * @param array $changes
     *
     * @return array
     *
     */
    public function get_changes_description(array $changes): array {
        if (!class_exists('tool_certificate\certificate')) {
            return[];
        }

        global $DB;

        $oldvalue = (int) $changes['oldvalue'] ?? 0;
        $newvalue = (int) $changes['newvalue'] ?? 0;

        $oldvaluestr = '';
        $newvaluestr = '';
        $fieldnamestring = get_string($changes['fieldname'], 'mod_booking');

        // Actual value changes has been displayed for certificate only.
        // For changes in expiration dates or its type - only general message will be shown.
        switch ($changes['formkey']) {
            case 'certificate':
                if (!empty($oldvalue)) {
                    $certname = $DB->get_field('tool_certificate_templates', 'name', ['id' => $oldvalue]);
                    $oldvaluestr = get_string(
                        'changesinentity',
                        'mod_booking',
                        (object) ['id' => $oldvalue, 'name' => ($certname ?? '')]
                    );
                }
                if (!empty($newvalue)) {
                    $certname = $DB->get_field('tool_certificate_templates', 'name', ['id' => $newvalue]);
                    $newvaluestr = get_string(
                        'changesinentity',
                        'mod_booking',
                        (object) ['id' => $newvalue, 'name' => ($certname ?? '')]
                    );
                }
                break;
            case 'expirydateabsolute':
                $fieldnamestring = get_string('changesinexpirydateabsolute', 'mod_booking');
                break;
            case 'expirydaterelative':
                $fieldnamestring = get_string('changesinexpirydaterelative', 'mod_booking');
                break;
            case 'expirydatetype':
                $fieldnamestring = get_string('changesinexpirydatetype', 'mod_booking');
                break;
        }

        $infotext = get_string('changeinfochanged', 'mod_booking', $fieldnamestring);

        if (empty($oldvaluestr) && empty($newvaluestr)) {
            $returnarray['info'] = $infotext;
        } else {
            $returnarray = [
            'oldvalue' => $oldvaluestr,
            'newvalue' => $newvaluestr,
            'fieldname' => get_string($changes['fieldname'], 'mod_booking'),
            ];
        }

        return $returnarray;
    }
}
