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

use theme_boost_union\smartmenu;
use theme_boost_union\smartmenu_item;

/**
 * Theme Boost Union - Test data generator.
 *
 * @package    theme_boost_union
 * @copyright  2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}
 * @author     Mark Johnson <mark.johnson@catalyst-eu.net>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class theme_boost_union_generator extends component_generator_base {
    /**
     * Generate a smart menu.
     *
     * An array of one or more locations are required, which must match the smartmenu:LOCATION_* constants.
     * All other properties are optional.
     *
     * @param array $data
     * @return stdClass
     */
    public function create_smartmenu(array $data): \stdClass {
        global $DB;
        $location = $data['location'] ?? [];
        if (empty($location)) {
            throw new Exception('A Smart menu location must be specified.');
        }
        $validlocations = array_keys(smartmenu::get_locations());
        if (!empty(array_diff($location, $validlocations))) {
            throw new Exception('Invalid Smart menu location.');
        }
        $validdescriptions = array_keys(smartmenu::get_showdescription_options());
        $showdescription = $data['showdescription'] ?? smartmenu::DESC_NEVER;
        if (!in_array($showdescription, $validdescriptions)) {
            throw new Exception('Invalid showdescription.');
        }
        $validtypes = array_keys(smartmenu::get_types());
        $type = $data['type'] ?? smartmenu::TYPE_LIST;
        if (!in_array($type, $validtypes)) {
            throw new Exception('Invalid showdescription.');
        }
        $validmodes = array_keys(smartmenu::get_mode_options());
        $mode = $data['mode'] ?? smartmenu::MODE_SUBMENU;
        if (!in_array($mode, $validmodes)) {
            throw new Exception('Invalid mode.');
        }
        $validbehaviours = array_keys(smartmenu::get_moremenu_options());
        $moremenubehavior = $data['moremenubehavior'] ?? smartmenu::MOREMENU_DONOTCHANGE;
        if (!in_array($moremenubehavior, $validbehaviours)) {
            throw new Exception('Invalid moremenubehavior.');
        }

        $cardsize = null;
        $cardform = null;
        $cardoverflowbehavior = null;
        if ($type === smartmenu::TYPE_CARD) {
            $validcardsizes = array_keys(smartmenu::get_cardsize_options());
            $cardsize = $data['cardsize'] ?? smartmenu::CARDSIZE_SMALL;
            if (!in_array($cardsize, $validcardsizes)) {
                throw new Exception('Invalid cardsize.');
            }
            $validcardforms = array_keys(smartmenu::get_cardform_options());
            $cardform = $data['cardform'] ?? smartmenu::CARDFORM_SQUARE;
            if (!in_array($cardform, $validcardforms)) {
                throw new Exception('Invalid cardform.');
            }
            $validbehaviors = array_keys(smartmenu::get_cardoverflowbehaviour_options());
            $cardoverflowbehavior = $data['cardoverflowbehavior'] ?? smartmenu::CARDOVERFLOWBEHAVIOUR_NOWRAP;
            if (!in_array($cardoverflowbehavior, $validbehaviors)) {
                throw new Exception('Invalid cardoverflowbehavior.');
            }
        }
        [
            $roles,
            $rolecontext,
            $cohorts,
            $operator,
            $byadmin,
            $languages,
            $startdate,
            $enddate,
        ] = $this->parse_restrictions($data);

        $sortorder = $data['sortorder'] ?? $DB->count_records('theme_boost_union_menus') + 1;
        $record = (object)[
            'title' => $data['title'] ?? 'Smart menu ' . random_string(),
            'description' => $data['description'] ?? 'Smart menu description ' . random_string(),
            'description_format' => $data['description_format'] ?? FORMAT_HTML,
            'showdesc' => $showdescription,
            'sortorder' => $sortorder,
            'location' => json_encode($location),
            'type' => $type,
            'mode' => $mode,
            'cssclass' => $data['cssclass'] ?? null,
            'moremenubehavior' => $moremenubehavior,
            'cardsize' => $cardsize,
            'cardform' => $cardform,
            'cardoverflowbehavior' => $cardoverflowbehavior,
            'roles' => $roles,
            'rolecontext' => $rolecontext,
            'cohorts' => $cohorts,
            'operator' => $operator,
            'byadmin' => $byadmin,
            'languages' => $languages,
            'start_date' => $startdate,
            'end_date' => $enddate,
            'visible' => $data['visible'] ?? 1,
        ];
        $record->id = $DB->insert_record('theme_boost_union_menus', $record);

        // Purge the smart menus cache.
        $cache = \cache::make('theme_boost_union', 'smartmenus');
        $cache->delete($record->id);
        $cache->delete(smartmenu::CACHE_MENUSLIST);

        return $record;
    }

    /**
     * Generate a Smart menu item.
     *
     * The ID of the parent menu must be specified. All other properties are optional.
     *
     * @param array $data
     * @return stdClass
     */
    public function create_smartmenu_item(array $data): \stdClass {
        global $DB;

        if (!$DB->record_exists('theme_boost_union_menus', ['id' => $data['menu']])) {
            throw new Exception('Menu not found with id ' . $data['menu']);
        }

        $sortorder = $data['sortorder'] ?? $DB->count_records('theme_boost_union_menus') + 1;

        $validtypes = array_keys(smartmenu_item::get_types());
        $type = $data['type'] ?? smartmenu_item::TYPESTATIC;
        if (!in_array($type, $validtypes)) {
            throw new Exception('Invalid type.');
        }

        $url = null;
        if ($type === smartmenu_item::TYPESTATIC) {
            if (empty($data['url'])) {
                throw new Exception('URL is required when type is static.');
            }
            $url = $data['url'];
        }

        $email = null;
        if ($type === smartmenu_item::TYPEMAILTO) {
            if (empty($data['email'])) {
                throw new Exception('Email is required when type is mailto.');
            }
            $email = $data['email'];
        }

        $category = json_encode($data['category'] ?? []);
        $categorysubcats = $data['category_subcats'] ?? false;
        $enrolmentrole = json_encode($data['enrolmentrole'] ?? []);
        $completionstatus = json_encode($data['completionstatus'] ?? []);
        $daterange = json_encode($data['daterange'] ?? []);
        $customfields = json_encode($data['customfields'] ?? new stdClass());
        $listsort = null;
        $displayfield = null;
        $textcount = null;
        if ($type == smartmenu_item::TYPEDYNAMIC) {
            $validsorts = array_keys(smartmenu_item::get_listsort_options());
            $listsort = $data['listsort'] ?? smartmenu_item::LISTSORT_FULLNAME_ASC;
            if (!in_array($listsort, $validsorts)) {
                throw new Exception('Invalid listsort.');
            }
            $validdisplayfields = array_keys(smartmenu_item::get_displayfield_options());
            $displayfield = $data['displayfield'] ?? smartmenu_item::FIELD_FULLNAME;
            if (!in_array($displayfield, $validdisplayfields)) {
                throw new Exception('Invalid displayfield.');
            }
            $textcount = $data['textcount'] ?? null;
        }

        $validmodes = [
            smartmenu_item::MODE_INLINE,
            smartmenu_item::MODE_SUBMENU,
        ];
        $mode = $data['mode'] ?? smartmenu_item::MODE_INLINE;
        if (!in_array($mode, $validmodes)) {
            throw new Exception('Invalid mode.');
        }

        $validdisplays = array_keys(smartmenu_item::get_display_options());
        $display = $data['display'] ?? smartmenu_item::DISPLAY_SHOWTITLEICON;
        if (!in_array($display, $validdisplays)) {
            throw new Exception('Invalid display.');
        }

        $validtargets = array_keys(smartmenu_item::get_target_options());
        $target = $data['target'] ?? smartmenu_item::TARGET_SAME;
        if (!in_array($target, $validtargets)) {
            throw new Exception('Invalid target.');
        }

        $validtextpositions = array_keys(smartmenu_item::get_textposition_options());
        $textposition = $data['textposition'] ?? smartmenu_item::POSITION_BELOW;
        if (!in_array($textposition, $validtextpositions)) {
            throw new Exception('Invalid text position.');
        }

        [
            $roles,
            $rolecontext,
            $cohorts,
            $operator,
            $byadmin,
            $languages,
            $startdate,
            $enddate,
        ] = $this->parse_restrictions($data);

        $record = (object)[
            'title' => $data['title'] ?? 'Smart menu item ' . random_string(),
            'menu' => $data['menu'],
            'type' => $type,
            'sortorder' => $sortorder,
            'url' => $url,
            'email' => $email,
            'category' => $category,
            'category_subcats' => $categorysubcats,
            'enrolmentrole' => $enrolmentrole,
            'completionstatus' => $completionstatus,
            'daterange' => $daterange,
            'customfields' => $customfields,
            'listsort' => $listsort,
            'displayhiddencourses' => $data['displayhiddencourses'] ?? 0,
            'hiddencoursesort' => $data['hiddencoursesort'] ?? 0,
            'displayfield' => $displayfield,
            'textcount' => $textcount,
            'mode' => $mode,
            'menuicon' => $data['menuicon'] ?? null,
            'display' => $display,
            'tooltip' => $data['tooltip'] ?? null,
            'target' => $target,
            'cssclass' => $data['cssclass'] ?? null,
            'imagealt' => $data['imagealt'] ?? null,
            'textposition' => $textposition,
            'textcolor' => $data['textcolor'] ?? null,
            'backgroundcolor' => $data['backgroundcolor'] ?? null,
            'desktop' => $data['desktop'] ?? 0,
            'tablet' => $data['tablet'] ?? 0,
            'mobile' => $data['mobile'] ?? 0,
            'roles' => $roles,
            'rolecontext' => $rolecontext,
            'byadmin' => $byadmin,
            'cohorts' => $cohorts,
            'operator' => $operator,
            'languages' => $languages,
            'start_date' => $startdate,
            'end_date' => $enddate,
            'visible' => $data['visible'] ?? 1,
        ];
        $record->id = $DB->insert_record('theme_boost_union_menuitems', $record);

        // Purge the smart menu items cache for this item.
        $itemcache = \cache::make('theme_boost_union', 'smartmenu_items');
        $itemcache->delete($record->id);

        // Purge the smart menus cache for the parent menu.
        $menucache = \cache::make('theme_boost_union', 'smartmenus');
        $menucache->delete($data['menu']);

        return $record;
    }

    /**
     * Parse the provided restriction fields to ensure they are valid and appropriately encoded.
     *
     * @param array $data
     * @return array
     * @throws Exception
     */
    protected function parse_restrictions(array $data): array {
        $roles = $data['roles'] ?? [];
        $rolecontext = null;
        if (!empty($roles)) {
            $validcontexts = array_keys(smartmenu::get_rolecontext_options());
            $rolecontext = $data['rolecontext'] ?? smartmenu::ANYCONTEXT;
            if (!in_array($rolecontext, $validcontexts)) {
                throw new Exception('Invalid rolecontext.');
            }
        }
        $cohorts = $data['cohorts'] ?? [];
        $operator = null;
        if (!empty($cohorts)) {
            $validoperators = array_keys(smartmenu::get_operator_options());
            $operator = $data['operator'] ?? smartmenu::ANY;
            if (!in_array($operator, $validoperators)) {
                throw new Exception('Invalid operator.');
            }
        }
        $validbyadmins = array_keys(smartmenu::get_byadmin_options());
        $byadmin = $data['byadmin'] ?? smartmenu::BYADMIN_ALL;
        if (!in_array($byadmin, $validbyadmins)) {
            throw new Exception('Invalid byadmin.');
        }
        $languages = $data['languages'] ?? [];
        $startdate = $data['start_date'] ?? 0;
        $enddate = $data['end_date'] ?? 0;
        return [
            json_encode($roles),
            $rolecontext,
            json_encode($cohorts),
            $operator,
            $byadmin,
            json_encode($languages),
            $startdate,
            $enddate,
        ];
    }

    /**
     * Generate a flavour.
     *
     * @param array $data Must contain 'title'. Can optionally contain 'applytocategories' (comma-separated category idnumbers).
     * @return stdClass The created flavour record.
     * @throws Exception
     */
    public function create_flavour(array $data): \stdClass {
        global $DB;

        // Validate required fields.
        if (empty($data['title'])) {
            throw new Exception('Flavour title must be specified.');
        }

        // Prepare the record.
        $record = (object)[
            'title' => $data['title'],
            'description' => $data['description'] ?? '',
            'description_format' => FORMAT_HTML,
            'applytocohorts' => $data['applytocohorts'] ?? 0,
            'applytocohorts_ids' => null,
            'applytocategories' => $data['applytocategories'] ?? 0,
            'applytocategories_ids' => null,
            'look_backgroundimagepos' => $data['look_backgroundimagepos'] ?? null,
            'look_brandcolor' => $data['look_brandcolor'] ?? null,
            'look_bootstrapcolorsuccess' => $data['look_bootstrapcolorsuccess'] ?? null,
            'look_bootstrapcolorinfo' => $data['look_bootstrapcolorinfo'] ?? null,
            'look_bootstrapcolorwarning' => $data['look_bootstrapcolorwarning'] ?? null,
            'look_bootstrapcolordanger' => $data['look_bootstrapcolordanger'] ?? null,
            'look_aicoladministration' => $data['look_aicoladministration'] ?? null,
            'look_aicolassessment' => $data['look_aicolassessment'] ?? null,
            'look_aicolcollaboration' => $data['look_aicolcollaboration'] ?? null,
            'look_aicolcommunication' => $data['look_aicolcommunication'] ?? null,
            'look_aicolcontent' => $data['look_aicolcontent'] ?? null,
            'look_aicolinteractivecontent' => $data['look_aicolinteractivecontent'] ?? null,
            'look_aicolinterface' => $data['look_aicolinterface'] ?? null,
            'look_navbarcolor' => $data['look_navbarcolor'] ?? null,
            'look_rawscss' => $data['look_rawscss'] ?? null,
            'look_rawscsspre' => $data['look_rawscsspre'] ?? null,
        ];

        // Handle category IDs if provided.
        if (!empty($data['applytocategories_ids'])) {
            $record->applytocategories = 1;
            $categoryids = [];
            foreach (explode(',', $data['applytocategories_ids']) as $idnumber) {
                $idnumber = trim($idnumber);
                if (empty($idnumber)) {
                    continue;
                }
                $categoryid = $DB->get_field('course_categories', 'id', ['idnumber' => $idnumber]);
                if (!$categoryid) {
                    throw new Exception('Category not found with idnumber: ' . $idnumber);
                }
                $categoryids[] = $categoryid;
            }
            $record->applytocategories_ids = json_encode($categoryids);
        }

        // Handle cohort IDs if provided.
        if (!empty($data['applytocohorts_ids'])) {
            $record->applytocohorts = 1;
            $cohortids = [];
            foreach (explode(',', $data['applytocohorts_ids']) as $idnumber) {
                $idnumber = trim($idnumber);
                if (empty($idnumber)) {
                    continue;
                }
                $cohortid = $DB->get_field('cohort', 'id', ['idnumber' => $idnumber]);
                if (!$cohortid) {
                    throw new Exception('Cohort not found with idnumber: ' . $idnumber);
                }
                $cohortids[] = $cohortid;
            }
            $record->applytocohorts_ids = json_encode($cohortids);
        }

        // Insert the record.
        $record->id = $DB->insert_record('theme_boost_union_flavours', $record);

        // Return the created record.
        return $record;
    }
}
