/*-
 * ~~~~~~licensing~~~~~~
 * entaxy-cicd-plugin
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */

var EntaxyCICD;
(function (EntaxyCICD) {
    EntaxyCICD._module.component('entaxyCiCdObjects', {
        bindings: {
            storage: '<',
            project: '<',
            version: '<'
        },
        template:
        ` 
          <pf-toolbar class="entaxy-toolbar" config="$ctrl.toolbarConfig">
            <actions>
              <button type="button" class="btn btn-default" ng-repeat="action in $ctrl.actions"
                    ng-click="action.actionFn()">
                {{action.name}}
              </button>
            </actions>
          </pf-toolbar>
          <div class="entaxy-cicd-objects-table-container">
            <pf-table-view class="entaxy-cicd-table"
                          config="$ctrl.tableConfig"
                          columns="$ctrl.tableColumns"
                          dt-options="$ctrl.dtOptions"
                          items="$ctrl.viewedItems">
            </pf-table-view>
          </div>
        `,
        controller: EntaxyCICDObjectsController
    })
        .name;

    function EntaxyCICDObjectsController(workspace, $scope, $uibModal, entaxyCICDService, $q, $route) {
        'ngInject';

        let ctrl = this;

        ctrl.tableConfig = {
            selectionMatchProp: 'id',
            onCheckBoxChange: (item) => changeSelected(item)
        };

        function changeSelected(item) {
            if (ctrl.selected[item.id]) {
                delete ctrl.selected[item.id];
            } else {
                ctrl.selected[item.id] = item;
            }
        }

        ctrl.toolbarConfig = {
            filterConfig: {
                fields: [
                    {
                        id: 'displayName',
                        title: 'Id / Name',
                        placeholder: 'Filter by Id or Name...',
                        filterType: 'text'
                    },
                    {
                        id: 'type',
                        title: 'Type',
                        placeholder: 'Filter by Type...',
                        filterType: 'select',
                        filterValues: []
                    },
                    {
                        id: 'status',
                        title: 'Status',
                        placeholder: 'Filter by Status...',
                        filterType: 'select',
                        filterValues: []
                    },
                    {
                        id: 'scope',
                        title: 'Scope',
                        placeholder: 'Filter by Scope...',
                        filterType: 'select',
                        filterValues: []
                    },
                    {
                        id: 'required',
                        title: 'Required',
                        placeholder: 'Filter by Required...',
                        filterType: 'select',
                        filterValues: ['yes', 'no']
                    },
                    {
                        id: 'ignored',
                        title: 'Ignored',
                        placeholder: 'Filter by Ignored...',
                        filterType: 'select',
                        filterValues: ['yes', 'no']
                    }
                ],
                appliedFilters: [],
                onFilterChange: filterChange
            },
            isTableView: true
        };

        ctrl.tableColumns = [
            { header: 'Status', itemField: 'status', templateFn: (value) => getObjectStatusTemplate(value) },
            { header: 'Id / Name', itemField: 'displayName' },
            { header: 'Type', itemField: 'type' },
            { header: 'Scope', itemField: 'scope' },
            { header: 'Required', itemField: 'required',
                templateFn: (value) => EntaxyCICD.getStatusTemplate(value) },
            { header: 'Ignored', itemField: 'ignored',
                templateFn: (value) => EntaxyCICD.getStatusTemplate(value) },
            { header: 'Id', itemField: 'id' }
        ];

        function getObjectStatusTemplate(value) {
            switch (value) {
                case 'INCLUDED':
                    return '<span class="pficon pficon-ok" data-toggle="tooltip" title="Included"></span>';
                case 'IGNORED':
                    return '<span class="pficon pficon-arrow" data-toggle="tooltip" title="Ignored"></span>';
                case 'REQUIRED':
                    return '<span class="pficon pficon-warning-triangle-o" data-toggle="tooltip" title="Required"></span>';
                case 'GHOST':
                    return '<span class="pficon pficon-unknowm" data-toggle="tooltip" title="Ghost"></span>';
                case 'DELETED':
                    return '<span class="pficon pficon-error-circle-o" data-toggle="tooltip" title="Deleted"></span>';
            }
        }

        ctrl.dtOptions = {
            order: [[1, "desc"]],
            columnDefs: [
                { "targets": [0], "sortable": false },
                { "targets": [7], "class": "hide" }
            ]
        };

        function filterChange(filters) {
            ctrl.viewedItems = Entaxy.applyFilters(ctrl.items, filters, matchesFilter);
            ctrl.toolbarConfig.filterConfig.resultsCount = ctrl.viewedItems.length;
        };

        function matchesFilter(item, filter) {
            let match = true;
            if (filter.id === 'status') {
                match = item.status === filter.value;
            } else if (filter.id === 'displayName') {
                match = item.displayName.toLowerCase().match(filter.value.toLowerCase()) !== null;
            } else if (filter.id === 'type') {
                match = item.type === filter.value;
            } else if (filter.id === 'scope') {
                match = item.scope === filter.value;
            } else if (filter.id === 'required') {
                match = filter.value === 'yes' ? item.required === true : item.required === false;
            } else if (filter.id === 'ignored') {
                match = filter.value === 'yes' ? item.ignored === true : item.ignored === false;
            }
            return match;
        };

        ctrl.items = [];
        ctrl.viewedItems = [];

        ctrl.$onInit = function() {

            ctrl.selected = {};

            let primaryActions = [
                { name: 'Add Objects', actionFn: () => ctrl.showAddObjectsModal() },
                { name: 'Add Resources', actionFn: () => ctrl.showAddResourcesModal() }
            ];

            ctrl.toolbarConfig.actionsConfig = {
                primaryActions: primaryActions,
                actionsInclude: true
            };

            ctrl.actions = [
                { name: 'Include Selected', actionFn: ctrl.includeSelected },
                { name: 'Ignore Selected', actionFn: ctrl.ignoreSelected },
                { name: 'Remove Selected', actionFn: ctrl.removeSelected },
                { name: 'Add Related', actionFn: ctrl.addRelatedObjects }
            ];

            entaxyCICDService.setTypesToFilterConfig(ctrl.toolbarConfig.filterConfig.fields);

            entaxyCICDService.setScopesToFilterConfig(ctrl.toolbarConfig.filterConfig.fields);

            let filterConfigFieldStatus = ctrl.toolbarConfig.filterConfig.fields.find(field => field.id === 'status');
            entaxyCICDService.executeOperation('readProjectItemStatuses', [])
            .then(result => {
                filterConfigFieldStatus.filterValues = JSON.parse(result);
            });
        }

        $scope.$watch('$ctrl.version', function () {
            populateTable();
        });

        function populateTable() {
            ctrl.selected = {};
            entaxyCICDService.executeOperation('listProjectItems', [ctrl.storage, ctrl.project, ctrl.version])
            .then(result => {
                let items = JSON.parse(result);
                ctrl.items = items.map(item => {
                    if (!item.displayName || item.displayName.trim().length == 0) {
                        item.displayName = item.id;
                    }
                    return item;
                });
                filterChange(ctrl.toolbarConfig.filterConfig.appliedFilters);
            });
        }

        ctrl.showAddObjectsModal = function (objects) {
            $uibModal.open({
                component: 'entaxyCiCdAddObjectsModal',
                resolve: {
                    project: () => ctrl.project,
                    objects: () => objects,
                    excludedObjectIds: () => ctrl.items.length > 0 ? ctrl.items.map(item => item.id) : []
                },
                size: 'xl',
                backdrop: 'static'
            })
            .result.then(objectsMap => {
                addObjects(objectsMap);
            },
            reason => {
                if (reason && reason !== Entaxy.MODAL_CANCEL_REASON.ESCAPE_KEY_PRESS) {
                    Entaxy.notificationError(reason);
                }
            });
        }

        function addObjects(objectsMap) {
            let objectsIds = Object.keys(objectsMap);
            if (objectsIds.length > 0) {
                let promises = [];
                objectsIds.forEach(objectId => {
                    promises.push(
                        entaxyCICDService.executeOperation('addObject',
                            [ctrl.storage, ctrl.project, ctrl.version, objectId])
                    );
                });
                $q.all(promises).then(result => {
                    addRelatedObjects(objectsMap, objectsIds, result);

                    Entaxy.notificationSuccess('Operation Succeeded');
                    populateTable();
                }).catch(error => {
                    Entaxy.notificationError(error);
                });
            }
        }

        function addRelatedObjects(objectsMap, objectsIds, result, showNotification) {
            let objectsTree = [];
            let relations = {};
            let addedItems = ctrl.items.length > 0 ? ctrl.items.map(item => item.id) : [];
            for (let i = 0; i < objectsIds.length; i++) {
                let relatedObjects = JSON.parse(result[i]);
                if (relatedObjects.length > 0) {
                    let objectsToAdd = relatedObjects.filter(object =>
                        !objectsIds.includes(object.fullId) && !addedItems.includes(object.fullId));

                    if (objectsToAdd.length > 0) {
                        let sublist = objectsToAdd.map(object => {
                            addRelation(relations, object.fullId, objectsIds[i]);

                            return {
                                name: object.fullId,
                                fullId: object.fullId,
                                displayName: object.displayName,
                                additionalInfo: object.type,
                                parent: objectsIds[i],
                                disableRowExpansion: true
                            };
                        });
                        objectsTree.push({
                            name: objectsIds[i],
                            displayName: objectsMap[objectsIds[i]].displayName,
                            additionalInfo: objectsMap[objectsIds[i]].type,
                            isExpanded: true,
                            sublist: sublist.sort(Entaxy.compareBy('displayName'))
                        });
                    }
                }
            }
            if (objectsTree.length > 0) {
                setRelated(relations, objectsTree);
                ctrl.showAddObjectsModal(objectsTree);
            } else {
                if (showNotification) {
                    Entaxy.notificationInfo('There are no related objects that are not included');
                }
            }
        }

        function addRelation(relations, relatedObjectId, objectId) {
            if (relations[relatedObjectId]) {
                relations[relatedObjectId].push(objectId);
            } else {
                relations[relatedObjectId] = [objectId];
            }
        }

        function setRelated(relations, objectsTree) {
            Object.keys(relations).forEach(key => {
                if (relations[key].length > 1) {
                    relations[key].forEach(objectId => {
                        let object = objectsTree.find(object => object.name === objectId);
                        let relatedObject = object.sublist.find(object => object.name === key);
                        relatedObject.relatedTo = relations[key];
                    })
                }
            });
        }

        ctrl.addRelatedObjects = function () {
            let objectsIds = Object.keys(ctrl.selected);
            if (objectsIds.length == 0) {
                Entaxy.notificationInfo('At least one item should be selected');
                return;
            }

            let objectsMap = {};
            let promises = [];
            objectsIds.forEach(objectId => {
                if (ctrl.selected[objectId].type === 'OBJECT') {
                    objectsMap[objectId] = {
                        displayName: ctrl.selected[objectId].displayName,
                        type: ctrl.selected[objectId].runtimeType
                    };
                    promises.push(
                        entaxyCICDService.executeOperation('getRelatedObjects', [objectId])
                    );
                }
            });
            if (promises.length > 0) {
                $q.all(promises).then(result => {
                    addRelatedObjects(objectsMap, Object.keys(objectsMap), result, true);
                }).catch(error => {
                    Entaxy.notificationError(error);
                });
            } else {
                Entaxy.notificationInfo('Selected items should have OBJECT type');
            }
        }

        ctrl.showAddResourcesModal = function () {
            $uibModal.open({
                component: 'entaxyResourceViewerModal',
                resolve: {
                    config: {
                        resourceProvider: 'schemas',
                        folderSelectionEnabled: true,
                        multipleSelectionEnabled: true,
                        providersSelectionEnabled: true
                    }
                },
                size: 'xl',
                backdrop: 'static'
            })
            .result.then(locations => {
                addResources(locations);
            },
            reason => {
                if (reason && reason !== Entaxy.MODAL_CANCEL_REASON.ESCAPE_KEY_PRESS) {
                    Entaxy.notificationError(reason);
                }
            });
        }

        function addResources(locations) {
            let promises = [];
            let failedResources = [];
            locations.forEach(location => {
                promises.push(
                    entaxyCICDService.executeOperation('addResource',
                        [ctrl.storage, ctrl.project, ctrl.version, location])
                    .catch(error => {
                        failedResources.push(location);
                        EntaxyCICD.log.error(error);
                    })
                );
            });
            $q.all(promises).then(result => {
                if (failedResources.length > 0) {
                    let result = locations.map(location => {
                        return {
                            id: location,
                            failed: failedResources.includes(location)
                        };
                    });
                    let tableHtml = Entaxy.createStatusResultTable(result, 'Location');
                    let mainMessage = 'Errors occurred while adding resources';
                    Entaxy.notificationWithDetails(Entaxy.NOTIFICATION_TYPE.WARNING, mainMessage, tableHtml,
                        Entaxy.configuration[Entaxy.CONFIGURATION_KEYS.NOTIFICATION_TIMEOUT_TABLE]);
                } else {
                    Entaxy.notificationSuccess('Operation Succeeded');
                }
                $route.reload();
            }).catch(error => {
                Entaxy.notificationError(error);
                EntaxyCICD.log.error(error);
            });
        }

        ctrl.includeSelected = function () {
            processOperationSelected('addItem', 'Selected items were successfully included into the project');
        }

        ctrl.ignoreSelected = function () {
            processOperationSelected('ignoreItem', 'Selected items were successfully ignored from the project');
        }

        ctrl.removeSelected = function () {
            const operationName = 'removeItems';
            const successMessage = 'Selected items were successfully removed from the project';
            let selectedIds = Object.keys(ctrl.selected);
            if (selectedIds.length > 0) {
                entaxyCICDService
                    .executeOperation(operationName, [ctrl.storage, ctrl.project, ctrl.version, selectedIds])
                .then(result => {
                    let unremovedItems = JSON.parse(result);
                    if (unremovedItems && Object.keys(unremovedItems).length > 0) {
                        let message = 'There are items that cannot be removed ' +
                            'since they are required by some of remaining items';
                        let table = EntaxyCICD.createTableFromUnremovedItems(unremovedItems);
                        Entaxy.notificationWithDetails(Entaxy.NOTIFICATION_TYPE.INFO, message, table, 30000);
                    } else {
                        Entaxy.notificationSuccess(successMessage);
                    }
                    $route.reload();
                }).catch(error => {
                    Entaxy.notificationError(error);
                    EntaxyCICD.log.error(error);
                });
            }
        }

        function processOperationSelected(operationName, successMessage) {
            let selectedIds = Object.keys(ctrl.selected);
            if (selectedIds.length > 0) {
                let promises = [];
                selectedIds.forEach(id => {
                    promises.push(
                        entaxyCICDService.executeOperation(operationName,
                            [ctrl.storage, ctrl.project, ctrl.version, id])
                    );
                });
                $q.all(promises).then(result => {
                    Entaxy.notificationSuccess(successMessage ? successMessage : 'Operation succeeded');
                    $route.reload();
                }).catch(error => {
                    Entaxy.notificationError(error);
                    EntaxyCICD.log.error(error);
                });
            }
        }
    }
    EntaxyCICDObjectsController.$inject = ['workspace', '$scope', '$uibModal', 'entaxyCICDService', '$q', '$route'];

})(EntaxyCICD || (EntaxyCICD = {}));
