import moment from 'moment';
import { StatsNode } from 'web-client/utilities/video-stats/stats-node';
import { StatsPoint } from 'web-client/utilities/video-stats/stats-point';

export class TimeSeries {

  constructor() {
    this._points = [];
  }

  get points() {
    return this._points;
  }

  addPoint(statsPoint) {
    this._points.push(statsPoint);
  }

  getPoint(timeStamp) {
    return this._points.find(point => point.timeStamp === timeStamp);
  }

  setPoints(points) {
    this._points = points;
  }

  listChildNodeLabelsFor(selector) {
    if (this._points.length === 0) {
      return [];
    }

    let parentNode = this._points[0].getNodeFor(selector);
    if (!parentNode) {
      return [];
    }

    return Object.keys(parentNode.nodes);
  }

  query({ startTime, endTime, metric, filter, respectTimeZones }) {
    let points = [];

    if (startTime || endTime) {
      points = this._points.filter(point => {
        let time = respectTimeZones ? moment.utc(point.timeStamp) : moment(point.timeStamp);

        // '[]' uses inclusivity for date range start/end
        return time.isBetween(startTime, endTime, null, '[]');
      });
    }

    let selector = [metric];

    if (filter) {
      let filterSelector = [metric, filter.key];

      let datasets = this.listChildNodeLabelsFor(filterSelector).map(key => {
        let items = this.queryNodesForSelector(points, [...filterSelector, key]);
        let sum = items.reduce((total, i) => i.value + total,  0);

        return { key, items, sum };
      });

      return datasets.sortBy('sum')
        .reverse()
        .splice(0, filter.limit);
    }

    let items = this.queryNodesForSelector(points, selector);
    let sum = items.reduce((total, i) => i.value + total,  0);
    return [{ key: metric, items, sum }];
  }

  queryNodesForSelector(points, selector) {
    let result = points.map(point => {
      let node = point.getNodeFor(selector);

      return {
        timeStamp: point.timeStamp,
        value: node ? node.value : 0
      };
    });

    return result;
  }
}

export function parseTimeSeriesData(data) {
  let aggregateTimeSeries = {};
  if (!data) {
    return aggregateTimeSeries;
  }

  data.forEach(metric => {
    let timeSeries = aggregateTimeSeries[metric.aggregation];
    if (!timeSeries) {
      timeSeries = new TimeSeries();
    }

    metric.metric_data.forEach(pointData => {
      let point = timeSeries.getPoint(pointData.time_interval);
      if (!point) {
        point = new StatsPoint(pointData.time_interval);
        timeSeries.addPoint(point);
      }

      let metricNode = new StatsNode(metric.metric, pointData.value, 'metric');
      point.setChildNodeFor([''], metricNode);
    });

    Object.keys(metric.filter_data).forEach(filterLabel => {
      let filterPoints = metric.filter_data[filterLabel];

      filterPoints.forEach(pointData => {
        let point = timeSeries.getPoint(pointData.time_interval);

        let filterNode = point.getNodeFor([metric.metric, filterLabel]);
        if (!filterNode) {
          filterNode = new StatsNode(filterLabel, 0, 'filter');
        }

        Object.keys(pointData.value).forEach(subFilterLabel => {
          let subFilterValue = pointData.value[subFilterLabel];
          let subFilterNode = new StatsNode(subFilterLabel, subFilterValue, filterLabel);

          filterNode.setChildNode(subFilterNode);
        });

        point.setChildNodeFor([metric.metric], filterNode);
      });
    });

    // To this point, the timeseries is in reverse-chronological order
    // reverse for graphing convenience
    timeSeries.points.reverse();

    aggregateTimeSeries[metric.aggregation] = timeSeries;
  });

  return aggregateTimeSeries;
}
