/*-
 * #%L
 * thinkbig-ui-operations-manager
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * 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.
 * #L%
 */
/**
 * This Directive is wired in to the FeedStatusIndicatorDirective.
 * It uses the OverviewService to watch for changes and update after the Indicator updates
 */


(function() {

    var directive = function() {
        return {
            restrict: "EA",
            bindToController: {
                cardTitle: "@",
                refreshIntervalTime: "@"
            },
            controllerAs: 'vm',
            scope: true,
            templateUrl: 'js/jobs/job-details-template.html',
            controller: "JobDetailsDirectiveController",
            link: function($scope, element, attrs, controller) {

            }
        };
    }

    function JobDetailsDirectiveController($scope, $stateParams, $http, $interval, $timeout, $q, $mdDialog, JobData, AlertsService, StateService, IconService, AccessControlService) {
        var self = this;

        /**
         * Indicates that admin operations are allowed.
         * @type {boolean}
         */
        self.allowAdmin = false;

        this.pageName = 'jobs';
        //Page State
        this.refreshing = false;
        this.loading = true;
        this.showProgress = true;

        //Track active requests and be able to cancel them if needed
        this.activeJobRequests = []

        this.jobData = {};

        /**
         * {step1:data,step2:data}
         * @type {{}}
         */
        this.stepData = {};

        /**
         * [{{title:'',data:{}},{title:'',data:{}}...}]
         * @type {{}}
         */

        this.allSteps = [];

        this.jobTab = {title: 'JOB', content: self.jobData}

        //  this.selectedTab = this.jobTabs[0];
        this.tabMetadata = {
            selectedIndex: 0,
            bottom: false
        };

        this.next = nextTab;
        this.previous = previousTab;

        //Refresh Intervals
        this.refreshTimeout = null;
        this.jobData = {};
        this.jobExecutionId = null;

        this.showJobParameters = true;
        this.toggleJobParameters = toggleJobParameters;
        this.relatedJobs = [];
        this.relatedJob = null;
        this.changeRelatedJob = changeRelatedJob;

        this.init = function() {
            var executionId = $stateParams.executionId;
            self.jobExecutionId = executionId;
            this.relatedJob = self.jobExecutionId;
            loadJobData();
            //   loadRelatedJobs();
        }

        this.init();

        function nextTab() {
            self.tabMetadata.selectedIndex = Math.min(self.tabMetadata.selectedIndex + 1, 2);
        };
        function previousTab() {
            self.tabMetadata.selectedIndex = Math.max(self.tabMetadata.selectedIndex - 1, 0);
        };

        //Tab Functions


        function toggleJobParameters(name) {
            if (name == 'JobParameters') {
                self.showJobParameters = true;
            }
            else {
                self.showJobParameters = false;
            }
        }

        function selectFirstTab() {
            self.tabMetadata.selectedIndex = 0;
            // self.selectedTab = self.jobTabs[0];
        }

        function cancelLoadJobDataTimeout() {
            if (self.refreshTimeout != null) {
                $timeout.cancel(self.refreshTimeout);
                self.refreshTimeout = null;
            }
        }

        //Load Feeds

        function loadJobData(force) {
            cancelLoadJobDataTimeout();

            if (force || !self.refreshing) {

                if (force) {
                    angular.forEach(self.activeJobRequests, function(canceler, i) {
                        canceler.resolve();
                    });
                    self.activeJobRequests = [];
                }

                self.refreshing = true;
                var sortOptions = '';
                var canceler = $q.defer();
                var successFn = function(response) {

                    if (response.data) {
                        //transform the data for UI
                        transformJobData(response.data);
                        if (response.data.running == true || response.data.stopping == true) {
                            cancelLoadJobDataTimeout();
                            self.refreshTimeout = $timeout(function() {
                                loadJobData()
                            }, 1000);
                        }

                        if (self.loading) {
                            self.loading = false;
                        }
                    }

                    finishedRequest(canceler);

                }
                var errorFn = function(err) {
                    finishedRequest(canceler);
                }
                var finallyFn = function() {

                }
                self.activeJobRequests.push(canceler);
                self.deferred = canceler;
                var params = {'includeSteps': true}

                $http.get(JobData.LOAD_JOB_URL(self.jobExecutionId), {timeout: canceler.promise, params: params}).then(successFn, errorFn);
            }

            return self.deferred;

        }

        function finishedRequest(canceler) {
            var index = _.indexOf(self.activeJobRequests, canceler);
            if (index >= 0) {
                self.activeJobRequests.splice(index, 1);
            }
            enableTabAnimation();
            canceler.resolve();
            canceler = null;
            self.refreshing = false;
            self.showProgress = false;
        }

        function loadRelatedJobs(setExecutionId) {
            var successFn = function(response) {
                if (response.data) {
                    self.relatedJobs = response.data;
                    if (setExecutionId) {
                        self.relatedJob = setExecutionId;
                    }
                    updateRelatedJobIndex();
                }
            }
            var errorFn = function(err) {
            }

            //todo uncomment once related job are linked and working
            // $http.get(JobData.RELATED_JOBS_URL(self.jobExecutionId)).then(successFn, errorFn);
        }

        function updateRelatedJobIndex() {
            if (self.relatedJob) {
                angular.forEach(self.relatedJobs, function(job, i) {
                    if (job.jobExecutionId == self.relatedJob) {
                        self.relatedJobIndex = i;
                    }
                })
            }
        }

        function disableTabAnimation() {
            angular.element('.job-details-tabs').addClass('no-animation');
        }

        function enableTabAnimation() {
            if (self.enableTabAnimationTimeout) {
                $timeout.cancel(self.enableTabAnimationTimeout);
            }
            self.enableTabAnimationTimeout = $timeout(function() {
                angular.element('.job-details-tabs').removeClass('no-animation');
            }, 1000);

        }

        function changeRelatedJob(relatedJob) {
            //remove animation for load
            disableTabAnimation();
            loadJobExecution(relatedJob)
        }

        function mapToArray(map, obj, fieldName, removeKeys) {
            if (removeKeys == undefined) {
                removeKeys = [];
            }
            var arr = [];
            for (var key in map) {
                if (_.indexOf(removeKeys, key) == -1) {
                    if (map.hasOwnProperty(key)) {
                        arr.push({key: key, value: map[key]});
                    }
                }
            }
            obj[fieldName] = arr;
        }

        function assignParameterArray(obj) {
            if (obj) {
                if (obj.jobParameters) {

                    mapToArray(obj.jobParameters, obj, 'jobParametersArray')
                }
                else {
                    obj['jobParametersArray'] = [];
                }

                if (obj.executionContext) {
                    mapToArray(obj.executionContext, obj, 'executionContextArray', ['batch.stepType', 'batch.taskletType'])
                }
                else {
                    obj['executionContextArray'] = [];
                }

            }

        }

        function cssClassForDisplayStatus(displayStatus) {
            var cssStatus = {
                'success': ['COMPLETED', 'STARTING', 'STARTED', 'EXECUTING'],
                'error': ['FAILED'],
                'warn': ['STOPPING', 'STOPPED', 'WARNING'],
                'abandoned': ['ABANDONED'],
                'unknown': ['UNKNOWN']
            };
            var statusCssMap = {};
            _.each(cssStatus, function (arr, key) {
                _.each(arr, function (status, i) {
                    statusCssMap[status] = key;
                });
            });
            return statusCssMap[displayStatus];

        }

        function transformJobData(job) {
            assignParameterArray(job);
            job.name = job.jobName;
            job.running = false;
            job.stopping = false;
            job.exitDescription = job.exitStatus;
            if (job.exitDescription == undefined || job.exitDescription == '') {
                job.exitDescription = 'No description available.'
            }
            job.tabIcon = undefined;

            var iconStyle = IconService.iconStyleForJobStatus(job.displayStatus);
            var icon = IconService.iconForJobStatus(job.displayStatus);
            job.cssStatusClass = cssClassForDisplayStatus(job.displayStatus);

            if (job.status == "STARTED") {
                job.running = true;
            }
            if (job.status == 'STOPPING') {
                job.stopping = true;
            }
            job.statusIcon = icon;
            job.tabIconStyle = iconStyle;

            angular.extend(self.jobData, job);


            if (job.executedSteps) {
                job.executedSteps = _.chain(job.executedSteps).sortBy('startTime').sortBy('nifiEventId').value();

                angular.forEach(job.executedSteps, function(step, i) {
                    var stepName = "Step " + (i + 1);
                    if (self.stepData[stepName] == undefined) {
                        self.stepData[stepName] = {};
                        self.allSteps.push({title: stepName, content: self.stepData[stepName]})
                    }
                    angular.extend(self.stepData[stepName], transformStep(step));

                });
            }
        }

        function transformStep(step) {
            step.name = step.stepName;
            step.running = false;
            step.tabIcon = undefined;
            if (step.runTime == undefined && step.endTime && step.startTime) {
                step.runTime = step.endTime - step.startTime;
            }
            if (step.endTime == undefined && step.startTime) {
                step.running = true;
                if (step.runTime == undefined) {
                    step.runTime = new Date().getTime() - step.startTime;
                }
            }
            step.displayStatus = step.exitCode;

            if (step.exitDescription == undefined || step.exitDescription == '') {
                step.exitDescription = 'No description available.'
            }

            var style = IconService.iconStyleForJobStatus(step.displayStatus);
            var icon = IconService.iconForJobStatus(step.displayStatus);
            step.cssStatusClass = cssClassForDisplayStatus(step.displayStatus);
            step.statusIcon = icon;
            if (step.displayStatus == 'FAILED' || step.displayStatus == 'EXECUTING' || step.displayStatus == 'WARNING') {
                step.tabIconStyle = style;
                step.tabIcon = icon;
            }

            if (step.startTime == null || step.startTime == undefined) {
                step.disabled = true;
            }
            else {
                step.disabled = false;
            }

            assignParameterArray(step);
            return step;
        }

        function clearRefreshInterval() {
            if (self.refreshInterval != null) {
                $interval.cancel(self.refreshInterval);
                self.refreshInterval = null;
            }
        }

        function setRefreshInterval() {
            self.clearRefreshInterval();
            if (self.refreshIntervalTime) {
                self.refreshInterval = $interval(loadJobs, self.refreshIntervalTime);

            }
        }

        //Util Functions
        function capitalize(string) {
            return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
        }

        function updateJob(executionId, job) {
            clearErrorMessage(executionId);
            var existingJob = self.jobData;
            if (existingJob && executionId == job.executionId) {
                transformJobData(job);
            }
            else {
                disableTabAnimation();
                loadJobExecution(job.executionId);

            }
        }

        function loadJobExecution(executionId) {
            self.jobExecutionId = executionId;

            //reset steps
            var len = self.allSteps.length;
            while (len > 1) {
                self.allSteps.splice(len - 1, 1);
                len = self.allSteps.length;
            }
            //clear out all the steps
            angular.forEach(Object.keys(self.stepData), function (key, i) {
                delete self.stepData[key];
            });

            loadJobData(true);
            //  loadRelatedJobs(executionId);
        }

        function addJobErrorMessage(executionId, errMsg) {
            var existingJob = self.allData['JOB'];
            if (existingJob) {
                errMsg = errMsg.split('<br/>').join('\n');
                existingJob.errorMessage = errMsg;
            }
        }

        function clearErrorMessage(executionId) {
            var existingJob = self.jobData;
            if (existingJob) {
                existingJob.errorMessage = '';
            }
        }

        this.restartJob = function(event) {
            event.stopPropagation();
            event.preventDefault();
            var executionId = self.jobData.executionId;
            var xhr = JobData.restartJob(self.jobData.executionId, {includeSteps: true}, function(data) {
                        updateJob(executionId, data);
                        //  loadJobs(true);
                    }, function(errMsg) {
                        addJobErrorMessage(executionId, errMsg);
                    }
            );
        };

        this.stopJob = function(event) {
            event.stopPropagation();
            event.preventDefault();
            var executionId = self.jobData.executionId;
            JobData.stopJob(self.jobData.executionId, {includeSteps: true}, function(data) {
                updateJob(executionId, data)
                //  loadJobs(true);
            })
        };

        this.abandonJob = function(event) {
            event.stopPropagation();
            event.preventDefault();
            var executionId = self.jobData.executionId;
            JobData.abandonJob(self.jobData.executionId, {includeSteps: true}, function(data) {
                updateJob(executionId, data)
                //  loadJobs(true);
            })
        };

        this.failJob = function(event) {
            event.stopPropagation();
            event.preventDefault();
            var executionId = self.jobData.executionId;
            JobData.failJob(self.jobData.executionId, {includeSteps: true}, function(data) {
                updateJob(executionId, data)
                //  loadJobs(true);
            })
        };



        // Fetch allowed permissions
        AccessControlService.getAllowedActions()
                .then(function(actionSet) {
                    self.allowAdmin = AccessControlService.hasAction(AccessControlService.OPERATIONS_ADMIN, actionSet.actions);
                });
    }

    angular.module(MODULE_OPERATIONS).controller("JobDetailsDirectiveController", JobDetailsDirectiveController);
    angular.module(MODULE_OPERATIONS).directive("tbaJobDetails", directive);
})();
