define(
    ['jquery','core/log','core/templates','core/fragment','core/modal_save_cancel',
        'core/str','core/config', 'core/modal_events', 'core_table/dynamic', 'core/ajax'],
    function ($,log,templates,Fragment,ModalSaveCancel,Str,Config, ModalEvents, DyanamicTable, Ajax) {
        "use strict"; // jshint ;_;

    /*
    This file contains class and ID definitions.
    */

        log.debug('MiniLesson AiGen Config Maker: initialising');

        return{
            controls: {},
            itemdatas: [],
            itemcontrols: [],
            //pass in config, amd set up table
            init: function (uniqid) {
                //pick up opts from html
                var self = this;
                self.init_controls(uniqid);
                self.register_events(uniqid);
                self.set_data();
            },

            init_controls: function (uniqid) {
                //set up the controls
                var self = this;
                self.controls.selectgenerate = $('#' + uniqid + ' select[name="generatemethod"]');
                self.controls.aigenmakebtn = $('#' + uniqid + '_aigen_make_btn');
                self.controls.fieldmappings = $('#' + uniqid + ' #id_fieldmappings');
                self.controls.aigenmake_textarea = $('#' + uniqid + '_aigen_make_textarea');
            },

            register_events: function (uniqid) {
                //register events
                var self = this;

                //On clicking the make aigen button
                 self.controls.aigenmakebtn.on('click', function (e) {
                    var items = [];
                    var itemcontrols = $('.ml_aigen_item');
                    // Get the lesson title and description
                    var lessonTitle = $('#' + uniqid + '_ml_aigen_lesson_title input').val();
                    var lessonDescription = $('#' + uniqid + '_ml_aigen_lesson_description textarea').val();

                    //loop through each item
                    itemcontrols.each(function (index, itemcontrol) {
                        var itemdata = {};

                       //set the itemnumber
                        itemdata.itemnumber = Number($(itemcontrol).data('itemnumber'));

                        //get the prompt
                        var promptTextArea = $(itemcontrol).find('textarea[name="aigenprompt"]');
                        itemdata.prompt = promptTextArea.val();

                        //get the generate method
                        var generateMethodSelect = $(itemcontrol).find('select[name="generatemethod"]');
                        itemdata.generatemethod = generateMethodSelect.val();

                        //get the generate fields
                        var generateFieldsCheckboxes = $(itemcontrol).find('.aigen_fields-to-generate input[type="checkbox"]');
                        var generateFields = [];
                        generateFieldsCheckboxes.each(function () {
                            var generateField = {};
                            generateField.name = $(this).attr('name');
                            generateField.generate  = $(this).is(':checked') ? 1 : 0;
                            if (itemdata.generatemethod == "reuse" && generateField.generate) {
                                generateField.mapping = $(itemcontrol).find('select[name="' + generateField.name + '_mapping"]').val();
                            } else {
                                generateField.mapping = '';
                            }
                            generateFields.push(generateField);
                        });
                        itemdata.generatefields = generateFields;

                         //get the file areas
                        var generateFileareasCheckboxes = $(itemcontrol).find('.aigen_fileareas-to-generate input[type="checkbox"]');
                        var generateFileareas = [];
                        generateFileareasCheckboxes.each(function () {
                            var generateFilearea = {};
                            generateFilearea.name = $(this).attr('name');
                            generateFilearea.generate  = $(this).is(':checked') ? 1 : 0;
                            if (generateFilearea.generate) {
                                generateFilearea.mapping = $(itemcontrol).find('select[name="' + generateFilearea.name + '_mapping"]').val();
                            }
                            generateFileareas.push(generateFilearea);
                        });
                        itemdata.generatefileareas = generateFileareas;

                        //Get the overall image context for the item (if any) e.g "user_topic" - "A man and a boy are walking in a park"
                        var overallimagecontext = $(itemcontrol).find('select[name="overall_image_context"]').val();
                        itemdata.overallimagecontext = overallimagecontext;

                        //get the prompt field mappings div
                        var promptFields = [];
                        var mappingsSelects = $(itemcontrol).find('.aigen_promptfield-mappings select');
                        mappingsSelects.each(function () {
                            var promptField = {};
                            promptField.name = $(this).data('name');
                            promptField.mapping = $(this).val();
                            promptFields.push(promptField);
                        });
                        itemdata.promptfields = promptFields;

                        // Add the current itemdata to the items array
                        items.push(itemdata);

                    });// End of itemcontrols.each

                    var alldata = {};
                    alldata.lessonTitle = lessonTitle;
                    alldata.lessonDescription = lessonDescription;
                    alldata.items = items;

                    if (self.controls.fieldmappings.length) {
                        alldata[self.controls.fieldmappings.attr('name')] = JSON.parse(self.controls.fieldmappings.val());
                    }

                    self.controls.aigenmake_textarea.val(JSON.stringify(alldata, null, 2));
                    log.debug('AiGen make button clicked, items: ' + JSON.stringify(alldata, null,2));
                 });

                //On reload of the prompt field to contextdata mapping
                $(document).on('click','.aigen-reload-button', function (e, data) {
                    e.preventDefault();

                    //get the prompt text area
                    var promptTextArea = $(this).closest('.ml_aigen_item').find('textarea[name="aigenprompt"]');
                    var newprompt = promptTextArea.val();

                    //update the mappings div
                    var mappingsDiv = $(this).closest('.ml_aigen_item').find('.ml_aigen_mappings');
                    mappingsDiv.data('promptfields',self.extractFieldsFromString(newprompt).join(','));

                    //update the prompt fields mappings div
                    var mappingsdata = {
                        availablecontext: mappingsDiv.data('availablecontext').split(',').filter(element => element.trim() !== ""),
                        aigenpromptfields: mappingsDiv.data('promptfields').split(',').filter(element => element.trim() !== ""),
                    };
                    var promptfieldmappingsDiv = $(this).closest('.ml_aigen_item').find('.aigen_promptfield-mappings');
                    templates.render('mod_minilesson/aigenpromptfieldmappings',mappingsdata).then(
                        function (html,js) {
                            promptfieldmappingsDiv.html(html);
                        }
                    );// End of templates
                });

                //On change of the select, update the config
                self.controls.selectgenerate.on('change', function () {
                    self.regenerate_item_form(this);
                });

            },  // end of register_events

            regenerate_item_form: function (selectgenerateelement, itemdata = null, itemcontrol = null) {
                var self = this;
                //get the value of the select
                var selectedValue = $(selectgenerateelement).val();
                //log the value
                log.debug('Selected generate method: ' + selectedValue);
                //the prompt text area
                var promptTextArea = $(selectgenerateelement).closest('.ml_aigen_item').find('textarea[name="aigenprompt"]');
                var newprompt = promptTextArea.data(selectedValue + 'prompt');
                promptTextArea.val(newprompt);
                //If this is a reuse generate method the text area should be readonly
                if (selectedValue === 'reuse') {
                    promptTextArea.attr('readonly', true);
                } else {
                    promptTextArea.removeAttr('readonly');
                }

                //prepare the mappings div data
                var mappingsDiv = $(selectgenerateelement).closest('.ml_aigen_item').find('.ml_aigen_mappings');
                mappingsDiv.data('promptfields',self.extractFieldsFromString(newprompt).join(','));
                var mappingsdata = {
                    methodreuse: selectedValue === 'reuse',
                    aigenplaceholders: mappingsDiv.data('aigenplaceholders').split(',').filter(element => element.trim() !== ""),
                    availablecontext: mappingsDiv.data('availablecontext').split(',').filter(element => element.trim() !== ""),
                    aigenpromptfields: mappingsDiv.data('promptfields').split(',').filter(element => element.trim() !== ""),
                };

                //prepare the files areas div data
                var fileareasDiv = $(selectgenerateelement).closest('.ml_aigen_item').find('.ml_aigen_filearea_mappings');
                var fileareasData = {
                    methodreuse: selectedValue === 'reuse',
                    aigenplaceholders: self.splitDataField(fileareasDiv.data('aigenplaceholders')),
                    contextfileareas: self.splitDataField(fileareasDiv.data('contextfileareas')),
                    aigenfileareas: self.splitDataField(fileareasDiv.data('aigenfileareas')),
                    availablecontext: self.splitDataField(fileareasDiv.data('availablecontext')),
                };

                // Render mappings first, then render file areas after mappings has finished.
                templates.render('mod_minilesson/aigenmappings', mappingsdata)
                .then(function (html, js) {
                    log.debug('redoing mappingsdiv: ');
                    mappingsDiv.html(html);
                    // Chain the second render so it runs after the first is done
                    return templates.render('mod_minilesson/aigenfilemappings', fileareasData);
                })
                .then(function (html, js) {
                    log.debug('redoing fileareadata: ');
                    fileareasDiv.html(html);
                }).then(function () {
                // If we have itemdata, set the fields accordingly
                    if (itemdata && itemcontrol) {
                        log.debug('updating after regenerating form');
                        self.updateTheFields(itemdata, itemcontrol);
                    }
                });
            },

            updateTheFields: function (theitemdata, theitemcontrol) {

                //get the generate fields
                theitemdata.generatefields.forEach(function (generateField) {
                    var $generateCheckbox = $(theitemcontrol).find('.aigen_fields-to-generate input[type="checkbox"][name="' + generateField.name + '"]');
                    $generateCheckbox.prop('checked', generateField.generate);
                    if (generateField.generate) {
                        if (theitemdata.generatemethod == "reuse") {
                            var $mappingSelect = $(theitemcontrol).find('select[name="' + generateField.name + '_mapping"]');
                            $mappingSelect.val(generateField.mapping);
                        }
                    }
                });

                //set the file areas
                theitemdata.generatefileareas.forEach(function (generateFilearea) {
                    var $generateFileareasCheckbox = $(theitemcontrol).find('.aigen_fileareas-to-generate input[type="checkbox"][name="' + generateFilearea.name + '"]');
                    $generateFileareasCheckbox.prop('checked', generateFilearea.generate);
                    if (generateFilearea.generate) {
                        var $mappingSelect = $(theitemcontrol).find('select[name="' + generateFilearea.name + '_mapping"]');
                        $mappingSelect.val(generateFilearea.mapping);
                    }
                });

                //set the overall image context
                var overallimagecontext = $(theitemcontrol).find('select[name="overall_image_context"]');
                overallimagecontext.val(theitemdata.overallimagecontext);

                //set the prompt field mappings div
                theitemdata.promptfields.forEach(function (promptField) {
                    var $mappingsSelect = $(theitemcontrol).find('.aigen_promptfield-mappings select[data-name="' + promptField.name + '"]');
                    $mappingsSelect.val(promptField.mapping);
                });
            },

            set_data: function () {
                //set up the controls
                var self = this;
                try {
                    var textareavalue = self.controls.aigenmake_textarea.val();
                    var jsonvalue = JSON.parse(textareavalue);
                    jsonvalue.items.forEach(function (itemdata, index) {
                        var itemcontrol = $('.ml_aigen_item[data-itemnumber="' + index + '"]');

                        //set the prompt
                        var promptTextArea = $(itemcontrol).find('textarea[name="aigenprompt"]');
                        promptTextArea.val(itemdata.prompt);

                        //set the generate method
                        var generateMethodSelect = $(itemcontrol).find('select[name="generatemethod"]');
                        generateMethodSelect.val(itemdata.generatemethod);
                        promptTextArea.data(itemdata.generatemethod + 'prompt', itemdata.prompt);
                        log.debug('Setting generate method to: ' + itemdata.generatemethod);
                        // We need to do this to make sure the correct fields are on the page, before we set data to them.
                        log.debug('triggering');

                        // Regenerate the form and, once both renders finish, set the values for this item
                        self.regenerate_item_form(generateMethodSelect[0], itemdata, itemcontrol);

                    });
                } catch (error) {
                    log.debug('Invalid JSON');
                    log.debug(error);
                }
            },

            splitDataField: function (datafield) {
                if (!datafield || datafield.trim() === '') {
                    return [];
                } else {
                    return datafield.split(',').filter(element => element.trim() !== "")
                }
            },

            extractFieldsFromString: function (input) {
                const regex = new RegExp('\\{(\\w+)\\}', 'g'); // fresh regex every time
                return Array.from(input.matchAll(regex)).reduce(function (a,i) {
                    if (a.indexOf(i[1]) === -1) {
                        a.push(i[1]);
                    }
                    return a;
                }, []);
            },

            registerAiGenerateAction: function (wrapperselector) {
                var self = this;
                var wrapper = document.querySelector(wrapperselector);
                if (wrapper) {
                    wrapper.addEventListener('submit', function (e) {
                        if (e.target.elements.hasOwnProperty('templateid')) {
                            e.preventDefault();
                            ModalSaveCancel.create({
                                title: Str.get_string('aigenmodaltitle', 'mod_minilesson'),
                                body: self.callAiGenerateContextFormApi(e.target),
                                large: true,
                                removeOnClose: true
                            }).then(function (modal) {
                                modal.getRoot().on('submit ' + ModalEvents.save, function (e) {
                                    e.preventDefault();
                                    var form = this.querySelector('form');
                                    modal.setBody(self.callAiGenerateContextFormApi(form).then(function (html, js) {
                                        if (html === 'submitted') {
                                            modal.destroy();
                                            self.refreshTable(wrapper.dataset.tableuniqueid);
                                            document.querySelector('#page')?.scrollTo({ top : 0, behavior : 'smooth' });
                                            return ['', ''];
                                        }
                                        return [html, js];
                                    }));
                                });
                                modal.show();
                            });
                        }
                    });
                    self.checkProgressBar(wrapper.dataset.tableuniqueid);
                }
            },

            callAiGenerateContextFormApi: function (form) {
                return Fragment.loadFragment(
                    'mod_minilesson',
                    'aigen_contextform',
                    Config.contextid,
                    {
                        url: form.getAttribute('action'),
                        params: new URLSearchParams(new FormData(form)).toString()
                    }
                );
            },

            refreshTable: function (tableuniqueid) {
                var self = this;
                try {
                    var tableRoot = DyanamicTable.getTableFromId(tableuniqueid);
                    DyanamicTable.refreshTableContent(tableRoot).then(function () {
                        self.checkProgressBar(tableuniqueid);
                    });
                } catch (e) {
                    log.debug(e);
                }
            },

            checkProgressBar: function (tableuniqueid, pinginterval = 1) {
                var self = this;
                try {
                    var tableRoot = DyanamicTable.getTableFromId(tableuniqueid);
                } catch (e) {
                    log.debug(e);
                }
                var table = tableRoot.querySelector('table');
                if (!table) {
                    return;
                }
                var updateinterval = parseInt(table.getAttribute('data-updateinterval'));
                if (updateinterval > 0) {
                    pinginterval = updateinterval;
                }
                if (tableRoot) {
                    var idmap = {};
                    tableRoot.querySelectorAll('[data-region="progressbar"]')
                    .forEach(function (progressbar) {
                        var id = parseInt(progressbar.getAttribute('data-id'));
                        if (id > 0) {
                            idmap[id] = progressbar;
                        }
                    });
                    var ids = Object.keys(idmap).map(parseInt);
                    if (Object.keys(ids).length > 0) {
                        Ajax.call([{
                            methodname: 'mod_minilesson_update_progressbars',
                            args: {
                                contextid: Config.contextid,
                                ids: ids
                            }
                        }])[0].then(function (response) {
                            response.forEach(function (line) {
                                if (ids.indexOf(line.id) > -1) {
                                    var progressbarrow = idmap[line.id].closest('tr');
                                    line.columns.forEach(function (coldata, index) {
                                        if (coldata.update) {
                                            var columntd = progressbarrow.querySelector('td.c' + index);
                                            if (columntd) {
                                                columntd.innerHTML = coldata.data;
                                                columntd.dataset.column = coldata.column;
                                            }
                                        }
                                    });
                                }
                            });
                            setTimeout(function () {
                                self.checkProgressBar(tableuniqueid);
                            }, pinginterval * 1000);
                        });
                    }
                }
            },

            manageFilter: function () {
                var self = this;
                var allcheckbox = document.querySelector('.minilesson_template_tag_all');
                var othercheckbox = document.querySelectorAll('.minilesson_template_tag_others');
                var allChecks = [].concat(allcheckbox, Array.from(othercheckbox));

                allcheckbox.addEventListener('change', function () {
                    othercheckbox.forEach(function (cb) {
                        cb.disabled = allcheckbox.checked;
                    });
                });

                document.addEventListener('change', e => {
                    if (allChecks.includes(e.target)) {
                        const filters = allChecks.filter(function (cb) {
                            return cb.checked && !cb.disabled;
                        }).map(function (cb) {
                            return cb.value;
                        });
                        self.loadContents(filters);
                    }
                });
            },

            loadContents: function (filters) {
                var rootelement = document.querySelector('[data-region="mod_minilesson_aigentemplates"]');
                Fragment.loadFragment(
                    'mod_minilesson',
                    'templates',
                    M.cfg.contextid,
                    {
                        filters: JSON.stringify(filters),
                    }
                ).then(function (html, js) {
                    templates.replaceNodeContents(rootelement, html, js);
                });
            }
        };//end of return value
    }
);