var icons = IconFactory();
var ellipsisActions;
var dataTableOptions = {
    lengthMenu: [[10, 25, 50], [10, 25, 50]],
    stateSave: true
};
function EllipsisControl(options) {
    if (!ellipsisActions) ellipsisActions = EllipsisActionsFactory();
    if (!(this instanceof EllipsisControl)) return new EllipsisControl(options);

    let settings = options ?? {};
    let commands = settings.commands ?? [];

    this.registerAction = (action, key = null) => {
        if (action) commands[key ?? action.defaultKey] = action;
        return this;
    };

    //traditional HTML tables call init to create the menu on all rows
    //actions and data will be loaded from select elements
    this.init = (table, buttonCssFunction) => {
        $(table)
            .find('.actions-column')
            .each(function (_, td) {
                let data = ellipsisActions.getLegacyData(td);
                let css = typeof buttonCssFunction === 'function'
                    ? buttonCssFunction(data, data.formData)
                    : null;
                render(data, td, data.formData, css);
            });
        return $(table);
    };

    //called from DataTables' createdRow event
    this.render = render;

    function render(actions, td, formData, buttonClass) {
        if (!actions) return;
        $(td)
            .append(createDropdownButton(buttonClass))
            //wire up events
            .on('show.bs.dropdown', { actions, formData }, (e) => {
                //add the items before it opens (show/shown)
                let links = ellipsisActions.buildLinks(commands, e.data.actions, e.data.formData, settings);
                var owned = [];
                var btn = $(td).find('.ellipsis-button');
                $.each(links, (index, item) => {
                    $(item).attr('id',
                        btn.attr('id') + '-item-' + (index + 1));
                    owned.push(item);
                });
                btn
                    .attr('aria-owns', owned.map(item => $(item).attr('id')).join(' '))
                    .next('.ellipsis-menu')
                    .html(owned.map(toListItem));
            })
            .on('hidden.bs.dropdown', () => {
                //remove the items after it closes (hide/hidden)
                $(td)
                    .find('.ellipsis-button')
                    .removeAttr('aria-owns')
                    .next('.ellipsis-menu')
                    .html('');
            });
    }

    function toListItem($el) {
        return $('<li aria-role="presentation" />').append($el);
    }

    function createDropdownButton(cssClasses) {
        var id = ellipsisActions.menuCount();
        return [
            icons.applyIcon(icons.ellipsis,
                $('<button type="button" class="btn ellipsis-button" />')
                    .addClass(cssClasses ?? 'BodyB3Black')
                    .attr('data-toggle', 'dropdown')
                    .attr('aria-label', settings.label ?? 'Actions')
                    .attr('aria-haspopup', true)
                    .attr('aria-controls', 'ellipsis-menu-' + id + '-items')
                    .attr('id', 'ellipsis-menu-' + id)),
            $('<ul />')
                .addClass('ellipsis-menu dropdown-menu submenuExpanded')
                .attr('aria-role', 'menu')
                .attr('id', 'ellipsis-menu-' + id + '-items')
        ];
    }
}

function IconFactory() {
    var _iconCache = [];
    const icons = {
        open: 'content/icons/view.svg',
        resubmit: 'content/icons/arrow-counterclockwise.svg',
        complete: 'content/icons/tickmark.svg',
        close: 'content/icons/close.svg',
        replace: 'content/icons/replace.svg',
        upload: 'content/icons/upload.svg',
        ellipsis: 'content/icons/eclipse3dots.svg',
        bell: 'content/icons/bell.svg',
        applyIcon: (url, el) => {
            if (!url) {
                console.log('invalid icon');
                return;
            }
            if (_iconCache[url]) {
                if (el) {
                    $(_iconCache[url]).appendTo(el);
                }
            } else {
                var target = url.startsWith('/') || url.startsWith('http')
                    ? url
                    : $('#rootUrl').val() + url;
                $.ajax({
                    url: target,
                    dataType: 'html',
                    global: false,
                    success: (d) => {
                        _iconCache[url] = d;
                        if (el) {
                            $(d).appendTo(el);
                        }
                    },
                }).fail(e => console.log(e));
            }
            return el;
        },
    };
    //pre-load to prevent race on ellipsis'd table init
    icons.applyIcon(icons.ellipsis);
    return icons;
}

function EllipsisActionsFactory() {
    let helpers = (function () {
        var count = 0;
        function menuCount() {
            return ++count;
        }
        function buildLinks(allowed, requested, formData, settings) {
            var wrapper = [];
            for (var key in requested) {
                if (allowed[key]) {
                    if (typeof allowed[key].render === 'function') {
                        wrapper.push(allowed[key]
                            .render(requested[key], allowed[key], formData, settings));
                    } else {
                        console.log(key + '.render is not a function');
                    }
                }
            }
            return wrapper;
        }
        function getLegacyData(el) {
            let data = [];
            $(el)
                .find('.action-list option')
                .each((_, o) => {
                    let key = $(o).text().trim();
                    let value = $(o).val();
                    data[key] = value;
                });
            let formData = {};
            $(el)
                .find('.form-data option')
                .each((_, o) => {
                    let key = $(o).text().trim();
                    let value = $(o).val();
                    formData[key] = value;
                });
            data.formData = formData;
            return data;
        }
        function buildAjaxLink(url, action, formData, settings) {
            var a = buildMenuLink(url, action);
            a.on('click', {
                url,
                action,
                formData,
                settings
            }, (e) => {
                if (typeof e.data.action.click === 'function') {
                    return e.data.action.click(e, {
                        url: e.data.url,
                        action: e.data.action,
                        formData: e.data.formData,
                        settings: e.data.settings
                    });
                } else if (e.data.action.click) {
                    console.log('invalid click handler');
                }
                e.preventDefault();
                if (e.data.action.confirmText && !confirm(e.data.action.confirmText)) {
                    console.log('canceled');
                    return;
                }
                $.ajax({
                    url: e.data.url,
                    global: false,
                    beforeSend: BeginRequest,
                    complete: globalUNBlockUI,
                    success: (response) => {
                        if (typeof e.data.action.success === 'function') {
                            e.data.action.success(response);
                        } else if (e.data.action.success) {
                            console.log('invalid success handler');
                        }
                        if (e.data.settings.replaceContainer) {
                            $(e.data.settings.replaceContainer).html(response);
                        }
                    },
                }).fail((e) => console.log(e));
            });
            return a;
        }
        function buildMenuLink(href, action) {
            var a = $('<a />')
                .addClass(action.css)
                .attr('href', href)
                .attr('aria-role', 'menuitem');
            a.append(action.label);
            if (action.icon) {
                a.append(icons
                    .applyIcon(action.icon, $('<div aria-hidden="true" />'))
                    .addClass(action.iconCss));
            }
            return a;
        }

        return { buildLinks, buildAjaxLink, buildMenuLink, getLegacyData, menuCount };
    })();
    const gridDropdownClasses =
        'ellipsis-item dropdown-item BodyB4Blue1 submenuitem d-inline-flex align-items-center';
    const iconWrapperClasses = 'ml-3 tmhp-svg-icon-l ellipsis-item-icon';

    function createAction(
        label, //link text
        icon, //svg URL
        confirmText = null, //if supplied, will prompt user with a confirm dialog
        success = null, //function; called after a successful ajax request
        click = null, //function; called instead of internal ajax call
        render = null //function; replaces internal render method. overrides all of the above
    ) {
        return {
            click,
            confirmText,
            css: gridDropdownClasses,
            defaultKey: label.toLowerCase().replace(/\s/g, ''),
            icon,
            iconCss: iconWrapperClasses,
            label,
            render: render ?? helpers.buildAjaxLink,
            success,
            formData: [],
        };
    }

    function copyAction(action) {
        var result = createAction(
            action.label,
            action.icon,
            action.confirmText,
            action.success,
            action.click,
            action.render);
        result.defaultKey = action.defaultKey;
        return result;
    }

    let Common = (function () {
        var open = createAction('Open', icons.open);
        open.click = (e, data) => {
            e.preventDefault();
            globalBlockUI();

            var url = location.origin + data.url + '&WindowId=' + getUniqueWindowId();

            // Check that the domain is valid.
            if (isTrustedDomain(url)) {
                location.href = url;
            }
            else {
                console.error("Invalid domain: " + url);
            }
        };

        var link = createAction('Open', icons.open);
        link.defaultKey = 'link';
        link.render = helpers.buildMenuLink;

        var close = createAction('Close', icons.close);
        close.css = close.css.replace('Blue1', 'Red');

        var cancel = createAction('Cancel', icons.close);
        cancel.css = close.css;

        var _delete = createAction(
            'Delete',
            icons.close,
            'Are you sure you want to remove the record?',
            (d) => recordUpdated(d, 'Record Successfully Deleted')
        );
        _delete.css = close.css;

        return { open, close, link, cancel, delete: _delete };
    })();

    let Screening = (function () {
        //there's no need for all of this variation in how the actions are defined
        //I'm attmepting to illustrate the options while the control is still under review
        //Once more of the existing actions are defined I'll clean this up. GVO 221202 TODO

        var upload = createAction('upload', icons.upload);
        upload.label = 'Contractor Manually Completed';
        upload.click = (e, data) => {
            e.preventDefault();
            var formTarget = JSON.parse(data.url);
            $('<input type=file />')
                .on('change', {
                    formIndex: data.settings.formIndex,
                    url: formTarget.url,
                    method: formTarget.method,
                    formData: { ...data.formData },
                    actionData: { ...data.action.formData },
                    container: data.settings.replaceContainer,
                }, (e) => {
                    if (!e.data.formIndex) {
                        console.log('no form index specified');
                    }

                    var thisForm = e.data.formIndex
                        && e.data.formIndex > -1
                        && $('form').length >= e.data.formIndex
                        ? new FormData($('form')[e.data.formIndex])
                        : new FormData();

                    thisForm.append(data.settings.uploadFieldName, $(e.target)[0].files[0]);

                    for (var key in e.data.formData) {
                        thisForm.append(key, e.data.formData[key]);
                    }
                    for (key in e.data.actionData) {
                        if (!thisForm.get(key))
                            thisForm.append(key, e.data.actionData[key]);
                    }
                    $.ajax({
                        url: e.data.url,
                        global: false,
                        beforeSend: globalBlockUI,
                        complete: globalUNBlockUI,
                        type: e.data.method,
                        data: thisForm,
                        processData: false,
                        contentType: false,
                        success: (response) => {
                            if (!e.data.container) {
                                console.log('no update container specified');
                                return;
                            }
                            $(e.data.container).html(response);
                        },
                    }).fail((e) => console.log(e));
                }).click();
        };

        var complete = createAction(
            'Complete',
            icons.replace,
            'Are you sure you want to accept the request?',
            (d) => recordUpdated(d, 'Request Successfully Approved')
        );
        complete.label = 'Contractor Verification Completed';

        var remove = createAction(
            'Remove',
            icons.close,
            'Are you sure you want to remove the DCN?',
            (d) => recordUpdated(d, 'DCN Successfully Removed')
        );
        remove.label = 'Remove DCN';
        remove.css = remove.css.replace('Blue1', 'Red');

        var resubmit = createAction('Resubmit', icons.resubmit);
        resubmit.confirmText = 'Are you sure you want to resubmit the request?';
        resubmit.success = (d) => recordUpdated(d, 'Request Successfully Resubmitted');

        var ignore = {
            defaultKey: 'ignore',
            css: gridDropdownClasses,
            iconCss: iconWrapperClasses,
            label: 'Not Required',
            confirmText: 'Are you sure you want to ignore the request?',
            success: (d) => recordUpdated(d, 'Request Successfully Ignored'),
            icon: icons.replace,
            render: helpers.buildAjaxLink,
        };

        var notRequired = createAction('Not Required', icons.replace, 'Are you sure you want to ignore the request?', (d) => recordUpdated(d, 'Request Successfully Ignored'));

        function recordUpdated(content, message) {
            if (!content.indexOf
                || content.indexOf('field-validation-error') > -1
                || content.indexOf('validation-summary-errors') > -1) {
                if (!content.indexOf) {
                    console.log('JSON or no content');
                }
                return;
            }
            showSuccessMessage(message);
        }

        return { resubmit, complete, remove, ignore, upload, notRequired };
    })();

    return {
        copyAction,
        createAction,
        Screening,
        Common,
        getLegacyData: helpers.getLegacyData,
        buildLinks: helpers.buildLinks,
        menuCount: helpers.menuCount
    };
};
