/*
 *  Copyright 2024 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * Tool to display the result of all relevant AccessController for the current selection
 */
Ext.define('Ametys.plugins.coreui.profiles.ContextPermissionsTool', {
    extend: "Ametys.plugins.coreui.rights.RightContextSelectionTool",
    
    statics : {
        /**
         * @private
         * Renderer for the assignment cells, which draws an icon representing the assignment
         * @param {Object} value The data value for the current cell
         * @param {Object} metaData A collection of metadata about the current cell
         * @param {Ext.data.Model} record The record for the current row
         * @return {String} The HTML string to be rendered
         */
        _renderAssignment: function(value, metaData, record)
        {
            var value = value || {};
            var glyph = Ametys.plugins.coreui.profiles.ContextPermissionsTool._computeGlyph(value.accessResult);
            
            var tooltip = Ametys.plugins.coreui.profiles.ProfilesGridHelper.computeExplanation(value.accessExplanations);
            metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(tooltip)}" data-qclass="uitool-context-permissions-tooltip"`; // surround with quote to avoid breaking rendering
            
            return '<span class="a-grid-access-result-glyph ' + glyph + '"/>';
        },
        
        /**
         * @private
         * Gets a list of css classes based on an access result.
         * @param accessResult a string representing an access result
         * @return {String} The HTML string to be rendered
         */
        _computeGlyph: function (accessResult)
        {
            switch (accessResult) 
            {
                case Ametys.plugins.coreui.rights.AccessResult.ANONYMOUS_ALLOWED:
                case Ametys.plugins.coreui.rights.AccessResult.ANY_CONNECTED_ALLOWED:
                case Ametys.plugins.coreui.rights.AccessResult.USER_ALLOWED:
                case Ametys.plugins.coreui.rights.AccessResult.GROUP_ALLOWED:
                    return "ametysicon-check-1 allowed";
                case Ametys.plugins.coreui.rights.AccessResult.ANONYMOUS_DENIED:
                case Ametys.plugins.coreui.rights.AccessResult.ANY_CONNECTED_DENIED:
                case Ametys.plugins.coreui.rights.AccessResult.USER_DENIED:
                case Ametys.plugins.coreui.rights.AccessResult.GROUP_DENIED:
                    return "ametysicon-cross-1 denied";
                case Ametys.plugins.coreui.rights.AccessResult.UNKNOWN:
                default:
                    // Undetermined
                    return "ametysicon-minus-symbol unknown disabled";
            }
        }
    },
    
    createPanel: function() {
        return Ext.create('Ext.container.Container', {
            layout: {
                type: 'card'
            },
            cls: 'uitool-context-permissions',
            items: [
                {
                    itemId: 'invalid-selection-card',
                    xtype: 'component',
                    cls: 'a-panel-text-empty',
                    border: false,
                    html: ''
                }, 
                this._createAssignmentsGrid()
            ]
        });
    },
    
    /**
     * @private
     * Create a grid displaying all the assignments of a user
     * @param {Object[]} a list of object representing the profiles
     * @returns {Ext.grid.Panel} the assignment grid panel
     */
    _createAssignmentsGrid: function() {
        this._store = Ext.create('Ametys.plugins.coreui.profiles.PermissionTargetStore', this._getStoreCfg());
        
        this._assignmentsGrid = Ext.create("Ametys.plugins.coreui.profiles.PermissionTargetGrid", {
            dockedItems: [
                {
                    xtype: 'component',
					tpl: '{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_HEADER_CONTEXT_TPL}} <strong data-qtip="{contextId}">{[Ext.String.escapeHtml(values.contextLabel)]}</strong><tpl if="contextType"> ({contextType})</tpl>',
					ui: 'tool-hintmessage'
                }, {
                    xtype: 'component',
                    html: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_HEADER_HINT}}",
                    ui: 'tool-hintmessage',
                    listeners: {
                        afterrender: function(me) {
                            // Register the new tip with an element's ID
                            Ext.tip.QuickTipManager.register({
                                glyphIcon: "ametysicon-sign-question",
                                target: me.getId(), // Target button's ID
                                text: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_HEADER_HINT_TOOLTIP}}",
                                inribbon: false
                            });
                        },
                        destroy: function(me) {
                            Ext.tip.QuickTipManager.unregister(me.getId());
                        }
                    }
                },
                this._getToolbarCfg()
            ],

            itemId: 'context-permissions-grid',
            store: this._store,
            
            enableColumnHide: false, // disable column hiding by the user. Column visibility is handled by the right filter
            
            stateful: true,
            stateId: this.self.getName() + "$assignment-grid",
        });
        
        // reset column visibility
        // column are stateful, but the visibility is linked to the right filter and not the user interaction
        Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
            column.setVisible(true);
        }, this);
        
        return this._assignmentsGrid;
    },
    
    /**
     * @private
     * Get the store configuration for the assignments grid
     * @param {Object[]} a list of object representing all the existing profiles
     * @returns {Object} the store configuration
     */
    _getStoreCfg: function()
    {
        return {
            proxy: {
                type: 'ametys',
                role: this.getServerRole(),
                serverId : this.getServerId(),
                methodName: 'getContextPermissions',
                methodArguments: ['context', 'convertor'],
                reader: {
                    type: 'json',
                    rootProperty: 'data'
                }
            },
            
            listeners: {
                scope: this,
                'beforeload': function(store, operation)
                {
                    // Get the parameters, or create empty object if needed
                    var parameters = operation.getParams() || {};
                    
                    var targets = this.getCurrentSelectionTargets();
                    
                    // Add the method arguments
                    parameters = Ext.apply(parameters, {
                        context: this.getContextId(targets[0]),
                        convertor: this.getConvertorId()
                    });
                    // Set the parameters
                    operation.setParams(parameters);
                },
                'metachange': function(store, meta) {
                    let columns = Ametys.plugins.coreui.profiles.ProfilesGridHelper.getColumnsFromPermissions(meta.permissions, Ametys.plugins.coreui.profiles.ContextPermissionsTool._renderAssignment);
                    
                    this._assignmentsGrid.reconfigure(store, this._assignmentsGrid.getInitialConfig('columns').concat(columns));
                }
            }
        }
    },
    
    _getToolbarCfg: function()
    {
        return {
           dock: 'top',
           xtype: 'toolbar',
           layout: {
               type: 'hbox',
               align: 'middle'
           },
           border: false,
           defaultType: 'textfield',
           items: [
               {
                   xtype: 'component',
                   html: '{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_FILTERS}}'
               }, {
                   xtype: 'edition.right',
                   itemId: 'profile-filter',
                   name: 'profile-filter',
                   cls: 'ametys',
                   allowBlank: true,
                   includeReader: true,
                   multiple: false,
                   stacked: "false",
                   flex: 1,
                   maxWidth: 400,
                   emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_RIGHT_FILTER}}",
                   listeners: {change: Ext.bind(this._filterByRight, this)}
               }, {
                   itemId: 'user-group-filter',
                   name: 'user-group-filter',
                   cls: 'ametys',
                   flex: 1,
                   maxWidth: 400,
                   emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_USERGROUP_FILTER}}",
                   listeners: {change: Ext.Function.createBuffered(this._filterByUserOrGroup, 500, this)}
               }, {
                   xtype: 'button',
                   // Clear filter
                   tooltip: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_CLEAR_USERGROUP_FILTER}}",
                   handler: Ext.bind (this._clearFilter, this, ['user-group-filter'], false),
                   iconCls: 'a-btn-glyph ametysicon-eraser11 size-16',
                   cls: 'a-btn-light'
               }
           ]
       }
    },
    
    /**
     * @private
     * Filters the columns by right
     * @param {Ametys.form.widget.Right} field The right field
     */
    _filterByRight: function(field)
    {
        Ext.suspendLayouts();
        var rightId = field.getValue();
        if (Ext.isEmpty(rightId))
        {
            Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
                column.setVisible(true);
            }, this);
            this._store.removeFilter('emptyProfile');
        }
        else
        {
            // Computes the columns to let visible
            var matchingColumns = [];

            // Hide the others (except the first column)
            Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
                let rights = column.getInitialConfig("rights");
                if (index == 0)
                {
                    // Do nothing on label column
                }
                else if (rights == undefined || Ext.Array.contains(rights, rightId))
                {
                    column.setVisible(true);
                    matchingColumns.push(column.dataIndex);
                }
                else
                {
                    column.setVisible(false);
                }
            }, this);

            this._store.addFilter([{
                id: 'emptyProfile',
                filterFn: function(record)
                {
                    for (var dataIndex of matchingColumns)
                    {
                        var assignment = record.get(dataIndex)
                        if (assignment != undefined && assignment.accessResult != Ametys.plugins.coreui.rights.AccessResult.UNKNOWN)
                        {
                            return true;
                        }
                    }
                    return false;
                }
            }]);
        }
        Ext.resumeLayouts(true);
    },
    
    /**
     * @private
     * Filters the records by their user login/label or group id/label
     * @param {Ext.form.field.Text} field The text field
     */
    _filterByUserOrGroup: function(field)
    {
        this._store.removeFilter('userOrGroup');
        
        var text = Ext.String.escapeRegex(field.getRawValue());
        if (text.length == 0)
        {
            return;
        }
        
        var fn = function(record, text)
        {
            var regExp = new RegExp('.*' + text + '.*', 'i');
            return regExp.test(record.get('login')) 
                    || regExp.test(record.get('groupId'))
                    || regExp.test(record.get('sortableLabel'));
        };
        
        this._store.addFilter({
            id: 'userOrGroup',
            filterFn: Ext.bind(fn, this, [text], 1)
        });
    },
    
    /**
     * @private
     * Clear the current text filter
     * @param {String} id The input id
     */
    _clearFilter: function(id)
    {
        this.getContentPanel().down("#"+id).reset();
    },
    
    setNoSelectionMatchState: function (message)
    {
        var mainPanel = this.getContentPanel()
        mainPanel.setActiveItem('invalid-selection-card');
        mainPanel.getComponent('invalid-selection-card').setHtml(message);
        // call parent to ensure normal operation
        this.callParent(arguments);
    },
    
    refresh: function (manual)
    {
        this.showRefreshing();
        
        var targets = this.getCurrentSelectionTargets(); // [][0] == undefined

        if (targets.length > 1
            || targets.length == 0 && Ametys.message.MessageBus.getCurrentSelectionMessage().getTargets().length != 0)
        {
            // if selection multiple or not empty, do nothing
            this.showUpToDate(true);
            return;
        }
        
        this.getContentPanel().setActiveItem('context-permissions-grid');

        var contextInfo = this.getContextInfo(targets[0]);

        this._assignmentsGrid.getDockedComponent(0).setData({contextLabel: contextInfo.label, contextId: this.getContextId(targets[0]), contextType: contextInfo.type});

        var me = this
        this._store.load(function() {me.showRefreshed()});
    },
});