'use strict';

angular.module('xos.ceilometerDashboard', [
  'ngResource',
  'ngCookies',
  'ngLodash',
  'ui.router',
  'xos.helpers',
  'ngAnimate',
  'chart.js',
  'ui.bootstrap.accordion'
])
.config(($stateProvider, $urlRouterProvider) => {
  $stateProvider
  .state('ceilometerDashboard', {
    url: '/',
    template: '<ceilometer-dashboard></ceilometer-dashboard>'
  })
  .state('samples', {
    url: '/:name/:tenant/samples',
    template: '<ceilometer-samples></ceilometer-samples>'
  });
  $urlRouterProvider.otherwise('/');
})
.config(function($httpProvider){
  $httpProvider.interceptors.push('NoHyperlinks');
})
.run(function($rootScope){
  $rootScope.stateName = 'ceilometerDashboard';
  $rootScope.$on('$stateChangeStart', (event, toState) => {
    $rootScope.stateName = toState.name;
  })
})
.service('Ceilometer', function($http, $q){

  this.getMappings = () => {
    let deferred = $q.defer();

    $http.get('/xoslib/xos-slice-service-mapping/')
    .then((res) => {
      deferred.resolve(res.data)
    })
    .catch((e) => {
      deferred.reject(e);
    });

    return deferred.promise;
  }

  this.getMeters = (params) => {
    let deferred = $q.defer();

    $http.get('/xoslib/meters/', {cache: true, params: params})
    // $http.get('../meters_mock.json', {cache: true})
    .then((res) => {
      deferred.resolve(res.data)
    })
    .catch((e) => {
      deferred.reject(e);
    });

    return deferred.promise;
  }

  this.getSamples = (name, tenant) => {
    let deferred = $q.defer();

    $http.get(`/xoslib/metersamples/`, {params: {meter: name, tenant: tenant}})
    .then((res) => {
      deferred.resolve(res.data)
    })
    .catch((e) => {
      deferred.reject(e);
    });

    return deferred.promise;
  }

  this.getStats = (options) => {
    let deferred = $q.defer();

    $http.get('/xoslib/meterstatistics/', {cache: true, params: options})
    // $http.get('../stats_mock.son', {cache: true})
    .then((res) => {
      deferred.resolve(res.data);
    })
    .catch((e) => {
      deferred.reject(e);
    });

    return deferred.promise;
  };

  // hold dashboard status (opened service, slice, resource)
  this.selectedService = null;
  this.selectedSlice = null;
  this.selectedResource = null;
})
.directive('ceilometerDashboard', function(lodash){
  return {
    restrict: 'E',
    scope: {},
    bindToController: true,
    controllerAs: 'vm',
    templateUrl: 'templates/ceilometer-dashboard.tpl.html',
    controller: function(Ceilometer){

      this.showStats = false;

      // this open the accordion
      this.accordion = {
        open: {}
      };

      /**
      * Open the active panel base on the service stored values
      */
      this.openPanels = () => {
        if(Ceilometer.selectedService){
          this.accordion.open[Ceilometer.selectedService] = true;
          if(Ceilometer.selectedSlice){
            this.loadSliceMeter(Ceilometer.selectedSlice, Ceilometer.selectedService);
            this.selectedSlice = Ceilometer.selectedSlice;
            if(Ceilometer.selectedResource){
              this.selectedResource = Ceilometer.selectedResource;
            }
          }
        }
      }

      /**
      * Load the list of service and slices
      */

      this.loadMappings = () => {
        this.loader = true;
        Ceilometer.getMappings()
        .then((services) => {

          // rename thing in UI
          services.map((service) => {
            if(service.service === 'service_ONOS_vBNG'){
              service.service = 'ONOS_FABRIC';
            }
            if(service.service === 'service_ONOS_vOLT'){
              service.service = 'ONOS_CORD';
            }

            service.slices.map(s => {
              if(s.slice === 'mysite_onos_volt'){
                s.slice = 'ONOS_CORD';
              }
              if(s.slice === 'mysite_onos_vbng'){
                s.slice = 'ONOS_FABRIC';
              }
              if(s.slice === 'mysite_vbng'){
                s.slice = 'mysite_vRouter';
              }
            });

            return service;
          });
          // end rename thing in UI
          
          this.services = services;
          this.openPanels();
        })
        .catch(err => {
          this.error = (err.data && err.data.detail) ? err.data.detail : 'An Error occurred. Please try again later.';
        })
        .finally(() => {
          this.loader = false;
        });
      };

      this.loadMappings();

      /**
      * Load the list of a single slice
      */
     
      this.loadSliceMeter = (slice, service_name) => {

        Ceilometer.selectedSlice = null;
        Ceilometer.selectedService = null;
        Ceilometer.selectedResources = null;

        // visualization info
        this.loader = true;
        this.error = null;
        this.ceilometerError = null;

        Ceilometer.getMeters({tenant: slice.project_id})
        .then((sliceMeters) => {
          this.selectedSlice = slice.slice;
          this.selectedTenant = slice.project_id;

          // store the status
          Ceilometer.selectedSlice = slice;
          Ceilometer.selectedService = service_name;

          // rename things in UI
          sliceMeters.map(m => {
            m.resource_name = m.resource_name.replace('mysite_onos_vbng', 'ONOS_FABRIC');
            m.resource_name = m.resource_name.replace('mysite_onos_volt', 'ONOS_CORD');
            m.resource_name = m.resource_name.replace('mysite_vbng', 'mysite_vRouter');
            return m;
          });
          // end rename things in UI

          this.selectedResources = lodash.groupBy(sliceMeters, 'resource_name');

          // hacky
          if(Ceilometer.selectedResource){
            this.selectedMeters = this.selectedResources[Ceilometer.selectedResource];
          }
        })
        .catch(err => {

          // this means that ceilometer is not yet ready
          if(err.status === 503){
            return this.ceilometerError = err.data.detail.specific_error;
          }

          this.error = (err.data && err.data.detail.specific_error) ? err.data.detail.specific_error : 'An Error occurred. Please try again later.';
        })
        .finally(() => {
          this.loader = false;
        });
      };

      /**
      * Select Meters for a resource
      *
      * @param Array meters The list of selected resources
      * @returns void
      */
      this.selectedMeters = null;
      this.selectMeters = (meters, resource) => {
        this.selectedMeters = meters;

        Ceilometer.selectedResource = resource;
        this.selectedResource = resource;
      }

    }
  };
})
.directive('ceilometerSamples', function(lodash, $stateParams){
  return {
    restrict: 'E',
    scope: {},
    bindToController: true,
    controllerAs: 'vm',
    templateUrl: 'templates/ceilometer-samples.tpl.html',
    controller: function(Ceilometer) {

      // console.log(Ceilometer.selectResource);

      this.chartColors = [
        '#286090',
        '#F7464A',
        '#46BFBD',
        '#FDB45C',
        '#97BBCD',
        '#4D5360',
        '#8c4f9f'
      ];

      this.chart = {
        series: [],
        labels: [],
        data: []
      }

      Chart.defaults.global.colours = this.chartColors;
      
      this.chartType = 'line';

      if($stateParams.name && $stateParams.tenant){
        this.name = $stateParams.name;
        this.tenant = $stateParams.tenant;
        // TODO rename tenant in project_id
      }
      else{
        throw new Error('Missing Name and Tenant Params!');
      }

      /**
      * Goes trough the array and format date to be used as labels
      *
      * @param Array data
      * @returns Array a list of labels
      */

      this.getLabels = (data) => {
        return data.reduce((list, item) => {
          let date = new Date(item.timestamp);
          list.push(`${date.getHours()}:${(date.getMinutes()<10?'0':'') + date.getMinutes()}:${date.getSeconds()}`);
          return list;
        }, []);
      };

      /**
      * Goes trough the array and return a flat array of values
      *
      * @param Array data
      * @returns Array a list of values
      */

      this.getData = (data) => {
        return data.reduce((list, item) => {
          list.push(item.volume);
          return list;
        }, []);
      }

      /**
      * Add a samples to the chart
      *
      * @param string resource_id
      */
      this.chartMeters = [];
      this.addMeterToChart = (project_id) => {
        this.chart['labels'] = this.getLabels(lodash.sortBy(this.samplesList[project_id], 'timestamp'));
        this.chart['series'].push(project_id);
        this.chart['data'].push(this.getData(lodash.sortBy(this.samplesList[project_id], 'timestamp')));
        this.chartMeters.push(this.samplesList[project_id][0]); //use the 0 as are all samples for the same resource and I need the name
        lodash.remove(this.sampleLabels, {id: project_id});
      }

      this.removeFromChart = (meter) => {
        this.chart.data.splice(this.chart.series.indexOf(meter.project_id), 1);
        this.chart.series.splice(this.chart.series.indexOf(meter.project_id), 1);
        this.chartMeters.splice(lodash.findIndex(this.chartMeters, {project_id: meter.project_id}), 1);
        this.sampleLabels.push({
          id: meter.project_id,
          name: meter.resource_name || meter.project_id
        })
      };

      /**
      * Format samples to create a list of labels and ids
      */
     
      this.formatSamplesLabels = (samples) => {

        return lodash.uniq(samples, 'project_id')
        .reduce((labels, item) => {
          labels.push({
            id: item.project_id,
            name: item.resource_name || item.project_id
          });

          return labels;
        }, []);
      }


      /**
      * Load the samples and format data
      */

      this.showSamples = () => {
        this.loader = true;
        // Ceilometer.getSamples(this.name, this.tenant) //fetch one
        Ceilometer.getSamples(this.name) //fetch all
        .then(res => {

          // rename things in UI
          res.map(m => {
            m.resource_name = m.resource_name.replace('mysite_onos_vbng', 'ONOS_FABRIC');
            m.resource_name = m.resource_name.replace('mysite_onos_volt', 'ONOS_CORD');
            m.resource_name = m.resource_name.replace('mysite_vbng', 'mysite_vRouter');
            return m;
          });
          // end rename things in UI

          // setup data for visualization
          this.samplesList = lodash.groupBy(res, 'project_id');
          this.sampleLabels = this.formatSamplesLabels(res);
          
          // add current meter to chart
          this.addMeterToChart(this.tenant);

        })
        .catch(err => {
          this.error = err.data.detail;
        })
        .finally(() => {
          this.loader = false;
        });
      };

      this.showSamples();
    }
  }
})
  // NOTE reading this on demand for a single
.directive('ceilometerStats', function(){
  return {
    restrict: 'E',
    scope: {
      name: '=name',
      tenant: '=tenant'
    },
    bindToController: true,
    controllerAs: 'vm',
    templateUrl: 'templates/ceilometer-stats.tpl.html',
    controller: function($scope, Ceilometer) {

      this.getStats = (tenant) => {
        this.loader = true;
        Ceilometer.getStats({tenant: tenant})
        .then(res => {
          res.map(m => {
            m.resource_name = m.resource_name.replace('mysite_onos_vbng', 'ONOS_FABRIC');
            m.resource_name = m.resource_name.replace('mysite_onos_volt', 'ONOS_CORD');
            m.resource_name = m.resource_name.replace('mysite_vbng', 'mysite_vRouter');
            return m;
          });
          this.stats = res;
        })
        .catch(err => {
          this.error = err.data;
        })
        .finally(() => {
          this.loader = false;
        });
      };

      $scope.$watch(() => this.name, (val) => {
        if(val){
          this.getStats(this.tenant);
        }
      });
    }
  }
});