'use strict';

var _toConsumableArray = require('@babel/runtime/helpers/toConsumableArray');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var orderBy = require('../../node_modules/lodash-es/orderBy.js');
var find = require('../../node_modules/lodash-es/find.js');
var filter = require('../../node_modules/lodash-es/filter.js');
var isEmpty = require('../../node_modules/lodash-es/isEmpty.js');
var isNil = require('../../node_modules/lodash-es/isNil.js');
require('core-js/modules/es.symbol.js');
require('core-js/modules/es.array.concat.js');
require('core-js/modules/es.array.fill.js');
require('core-js/modules/es.array.filter.js');
require('core-js/modules/es.array.includes.js');
require('core-js/modules/es.array.iterator.js');
require('core-js/modules/es.array.map.js');
require('core-js/modules/es.object.get-own-property-descriptor.js');
require('core-js/modules/es.object.get-own-property-descriptors.js');
require('core-js/modules/es.object.keys.js');
require('core-js/modules/es.object.to-string.js');
require('core-js/modules/es.set.js');
require('core-js/modules/es.string.includes.js');
require('core-js/modules/es.string.iterator.js');
require('core-js/modules/web.dom-collections.for-each.js');
require('core-js/modules/web.dom-collections.iterator.js');
var CardPropTypes = require('../../constants/CardPropTypes.js');
var cardUtilityFunctions = require('../../utils/cardUtilityFunctions.js');
var dayjs = require('../../utils/dayjs.js');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var _toConsumableArray__default = /*#__PURE__*/_interopDefault(_toConsumableArray);
var _defineProperty__default = /*#__PURE__*/_interopDefault(_defineProperty);

function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty__default.default(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }

/**
 * Generate fake values for my line chart
 * thresholds:  axis: PropTypes.oneOf(['x', 'y']),
        value: PropTypes.number,
        label: PropTypes.string,
        fillColor: PropTypes.string,
 * */
var generateSampleValues = function generateSampleValues(series, timeDataSourceId) {
  var _orderBy2, _orderBy3;
  var timeGrain = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'day';
  var timeRange = arguments.length > 3 ? arguments[3] : undefined;
  var thresholds = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
  // determine interval type
  var timeRangeType = timeRange !== null && timeRange !== void 0 && timeRange.includes('this') ? 'periodToDate' : 'rolling';
  // for month timeGrains, we need to determine whether to show 3 for a quarter or 12 for a year
  var timeRangeInterval = timeRange !== null && timeRange !== void 0 && timeRange.includes('Quarter') ? 'quarter' : timeRange;
  var count = 7;
  switch (timeGrain) {
    case 'hour':
      count = 24;
      break;
    case 'day':
      count = 7;
      break;
    case 'week':
      count = 4;
      break;
    case 'month':
      count = timeRangeInterval === 'quarter' ? 3 : 12;
      break;
    case 'quarter':
      count = 4;
      break;
    case 'year':
      count = 5;
      break;
    default:
      count = 7;
      break;
  }

  // This is a bit of a workaround for carbon-design-system/carbon-charts#1034 as they don't adjust the graph axes to include thresholds
  var highestThresholdValue = ((_orderBy2 = orderBy(thresholds.filter(function (threshold) {
    return threshold.axis === 'y';
  }), 'value', 'desc')) === null || _orderBy2 === void 0 || (_orderBy2 = _orderBy2[0]) === null || _orderBy2 === void 0 ? void 0 : _orderBy2.value) || 0;
  var lowestThresholdValue = ((_orderBy3 = orderBy(thresholds.filter(function (threshold) {
    return threshold.axis === 'y';
  }), 'value', 'acc')) === null || _orderBy3 === void 0 || (_orderBy3 = _orderBy3[0]) === null || _orderBy3 === void 0 ? void 0 : _orderBy3.value) || 0;
  var generateDatapoint = function generateDatapoint(index) {
    return isEmpty(thresholds) ? Math.random() * 100 : index % 2 === 0 ? highestThresholdValue + Math.random() * 10 : lowestThresholdValue - Math.random() * 10;
  };
  // number of each record to define
  var sampleValues = Array(count).fill(1);
  // ensure the series is actually an array since it can also be an object
  var seriesArray = Array.isArray(series) ? series : [series];
  return seriesArray.reduce(function (sampleData, _ref) {
    var dataSourceId = _ref.dataSourceId,
      dataFilter = _ref.dataFilter;
    var now = timeRangeType === 'periodToDate' // handle "this" intervals like "this week"
    ? dayjs.default().startOf(timeRangeInterval).subtract(1, timeGrain) : dayjs.default().subtract(count, timeGrain);
    var nextTimeStamp = now;
    sampleValues.forEach(function (value, index) {
      nextTimeStamp = nextTimeStamp.add(1, timeGrain);
      if (!isEmpty(dataFilter)) {
        // if we have a data filter, then we need a specific row for every filter
        sampleData.push(_objectSpread(_defineProperty__default.default(_defineProperty__default.default({}, timeDataSourceId, nextTimeStamp.valueOf()), dataSourceId, generateDatapoint(index)), dataFilter));
      } else {
        var existingData = find(sampleData, _defineProperty__default.default({}, timeDataSourceId, nextTimeStamp.valueOf()));
        if (existingData) {
          // add the additional dataSource to the existing datapoint
          existingData[dataSourceId] = generateDatapoint(index);
        } else {
          // otherwise we need explicit row
          sampleData.push(_defineProperty__default.default(_defineProperty__default.default({}, timeDataSourceId, nextTimeStamp.valueOf()), dataSourceId, generateDatapoint(index)));
        }
      }
    });
    return sampleData;
  }, []);
};

/**
 * Generate fake data to fill table columns for the preview mode of the table in the dashboard
 * @param {*} columns
 */
var generateTableSampleValues = function generateTableSampleValues(id, columns) {
  var sampleValues = Array(100).fill(1);
  return sampleValues.map(function (item, index) {
    return {
      id: "sample-values-".concat(id, "-").concat(index),
      values: columns.reduce(function (obj, column) {
        obj[column.dataSourceId] = column.type === 'TIMESTAMP' ? 'xx/xx/xxxx xx:xx' : '--'; // eslint-disable-line no-param-reassign
        return obj;
      }, {})
    };
  });
};
/** *
 * timestamp of current value
 * index of current value
 * ticks: array of current ticks or a single string with part of the date
 * interval: the type of interval formatting to use
 * locale: the current locale,
 * previousTickTimestamp
 * shouldDisplayGMT: boolean should we localize the time to the browser timezone
 */
var formatGraphTick = function formatGraphTick(timestamp, index, ticks, interval) {
  var locale = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 'en';
  var previousTickTimestamp = arguments.length > 5 ? arguments[5] : undefined;
  var shouldDisplayGMT = arguments.length > 6 ? arguments[6] : undefined;
  dayjs.default.locale(locale);
  var currentTimestamp = shouldDisplayGMT ? dayjs.default.utc(timestamp) : dayjs.default.tz(timestamp);
  var sameDay = dayjs.default(previousTickTimestamp).isSame(currentTimestamp, 'day');
  var sameMonth = dayjs.default(previousTickTimestamp).isSame(currentTimestamp, 'month');
  var sameYear = dayjs.default(previousTickTimestamp).isSame(currentTimestamp, 'year');

  // This works around a bug in moment where some Chinese languages are missing the day indicator
  // https://github.com/moment/moment/issues/5350
  var dailyFormat = !locale.includes('zh') ? 'MMM DD' : 'MMMDD日';
  var fullFormat = !locale.includes('zh') ? 'MMM DD YYYY' : 'MMMDD日 YYYY';
  if (interval === 'hour' && index === 0) {
    return ticks.length > 1 ? currentTimestamp.format(dailyFormat) : currentTimestamp.format("".concat(dailyFormat, " HH:mm"));
  }
  if (interval === 'hour' && index !== 0 && !sameDay) {
    return currentTimestamp.format(dailyFormat);
  }
  if (interval === 'hour') {
    return currentTimestamp.format('HH:mm');
  }
  if ((interval === 'day' || interval === 'week') && sameDay) {
    return ''; // if we're on the day and week and the same day then skip
  }
  if ((interval === 'day' || interval === 'week') && index === 0) {
    return currentTimestamp.format(dailyFormat);
  }
  if ((interval === 'day' || interval === 'week') && index !== 0) {
    return currentTimestamp.format(dailyFormat);
  }
  if (interval === 'month' && index === 0) {
    return currentTimestamp.format('MMM YYYY');
  }
  if (interval === 'month' && sameMonth) {
    return ''; // don't repeat same month
  }
  if (interval === 'month' && !sameYear) {
    return currentTimestamp.format('MMM YYYY');
  }
  if (interval === 'month' && sameYear) {
    return currentTimestamp.format('MMM');
  }
  if (interval === 'year' && index === 0) {
    return currentTimestamp.format('YYYY');
  }
  if (interval === 'year' && sameYear) {
    return ''; // if we're on the year boundary and the same year, then don't repeat
  }
  if (interval === 'year' && (!sameYear || index === 0)) {
    return currentTimestamp.format('YYYY');
  }
  return interval === 'minute' ? currentTimestamp.format('HH:mm') : currentTimestamp.format(fullFormat);
};

/**
 * Translates our raw data into a language the carbon-charts understand
 * @param {string} timeDataSourceId, the field that identifies the timestamp value in the data
 * @param {array} series, an array of lines to create in our chart
 * @param {array} values, the array of values from our data layer
 *
 * TODO: Handle empty data lines gracefully and notify the user of data lines that did not
 * match the dataFilter
 *
 * @returns {object} with a labels array and a datasets array
 */
var formatChartData = function formatChartData() {
  var timeDataSourceId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'timestamp';
  var series = arguments.length > 1 ? arguments[1] : undefined;
  var values = arguments.length > 2 ? arguments[2] : undefined;
  // Generate a set of unique timestamps for the values
  var timestamps = _toConsumableArray__default.default(new Set(values.map(function (val) {
    return val[timeDataSourceId];
  })));
  var data = [];

  // Series is the different groups of datasets
  // ensure is actually is an array since proptypes allow for an object, too.
  var seriesArray = Array.isArray(series) ? series : [series];
  seriesArray.forEach(function (_ref2) {
    var dataSourceId = _ref2.dataSourceId,
      _ref2$dataFilter = _ref2.dataFilter,
      dataFilter = _ref2$dataFilter === void 0 ? {} : _ref2$dataFilter,
      label = _ref2.label,
      _ref2$disableDataFilt = _ref2.disableDataFilter,
      disableDataFilter = _ref2$disableDataFilt === void 0 ? false : _ref2$disableDataFilt;
    timestamps.forEach(function (timestamp) {
      // First filter based on on the dataFilter
      var filteredData = !disableDataFilter ? filter(values, dataFilter) : values;
      if (!isEmpty(filteredData)) {
        // have to filter out null values from the dataset, as it causes Carbon Charts to break
        filteredData.filter(function (dataItem) {
          // only allow valid timestamp matches
          return !isNil(dataItem[timeDataSourceId]) && dataItem[timeDataSourceId] === timestamp;
        }).forEach(function (dataItem) {
          // Check to see if the data Item actually exists in this timestamp before adding to data (to support sparse data in the values)
          if (!isNil(dataItem[dataSourceId])) {
            data.push({
              date: dayjs.default.tz(dataItem[timeDataSourceId]).toDate(),
              value: dataItem[dataSourceId],
              group: label,
              dataSourceId: dataSourceId
            });
          }
        });
      }
    });
  });
  return data;
};

/**
 * Formats and maps the colors to their corresponding datasets in the carbon charts tabular data format
 * @param {Array} series an array of dataset group classifications
 * @returns {Object} colors - formatted
 */
var formatColors = function formatColors(series) {
  var colors = {
    scale: {}
  };
  if (Array.isArray(series)) {
    series.forEach(function (dataset, index) {
      colors.scale[dataset.label] = dataset.color || CardPropTypes.CHART_COLORS[index % CardPropTypes.CHART_COLORS.length];
    });
  } else {
    colors.scale[series.label] = series.color || CardPropTypes.CHART_COLORS[0];
  }
  return colors;
};

/**
 * Determines the dot stroke color (the border of the data point)
 * @param {string} datasetLabel --> map to group property
 * @param {string} label --> map to key property
 * @param {Object} data --> map to value property
 * @param {string} originalStrokeColor --> map to defaultStrokeColor. Default setting from carbon charts
 * @returns {string} stroke color
 */
var applyStrokeColor = function applyStrokeColor(alertRanges) {
  return function (datasetLabel, label, data, originalStrokeColor) {
    if (!isNil(data)) {
      var matchingAlertRange = cardUtilityFunctions.findMatchingAlertRange(alertRanges, data);
      return (matchingAlertRange === null || matchingAlertRange === void 0 ? void 0 : matchingAlertRange.length) > 0 ? matchingAlertRange[0].color : originalStrokeColor;
    }
    return originalStrokeColor;
  };
};

/**
 * Determines the dot fill color based on matching alerts
 * @param {string} datasetLabel --> map to group property
 * @param {string} label --> map to key property
 * @param {Object} data --> map to value property
 * @param {string} originalFillColor --> map to defaultFillColor property. Default setting from carbon charts
 * @returns {string} fill color
 */
var applyFillColor = function applyFillColor(alertRanges) {
  return function (datasetLabel, label, data, originalFillColor) {
    if (!isNil(data)) {
      var matchingAlertRange = cardUtilityFunctions.findMatchingAlertRange(alertRanges, data);
      return (matchingAlertRange === null || matchingAlertRange === void 0 ? void 0 : matchingAlertRange.length) > 0 ? matchingAlertRange[0].color : originalFillColor;
    }
    return originalFillColor;
  };
};

/**
 * Determines if the dot is filled based on matching alerts
 * @param {string} datasetLabel --> map to group property
 * @param {string} label --> map to key property
 * @param {Object} data --> map to value property
 * @param {Boolean} isFilled  --> map to defaultFilled property. Default setting from carbon charts
 * @returns {Boolean}
 */
var applyIsFilled = function applyIsFilled(alertRanges) {
  return function (datasetLabel, label, data, isFilled) {
    if (!isNil(data)) {
      var matchingAlertRange = cardUtilityFunctions.findMatchingAlertRange(alertRanges, data);
      return (matchingAlertRange === null || matchingAlertRange === void 0 ? void 0 : matchingAlertRange.length) > 0 ? true : isFilled;
    }
    return isFilled;
  };
};

exports.applyFillColor = applyFillColor;
exports.applyIsFilled = applyIsFilled;
exports.applyStrokeColor = applyStrokeColor;
exports.formatChartData = formatChartData;
exports.formatColors = formatColors;
exports.formatGraphTick = formatGraphTick;
exports.generateSampleValues = generateSampleValues;
exports.generateTableSampleValues = generateTableSampleValues;
