"use strict";

import {
  chartExportOptions,
  createCombinedTextDelimitedByComma,
  extractSamplingRateFromDatastreamName,
} from "./chartHelpers.mjs";

import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcess.mjs";

/**
 * Format a computed aggregation result to make it suitable for a column chart
 * @param {Array} calendarDatesMonthsStrArr An array of unique calendar dates strings (in "YYYY-MM-DD" fromat) or unique calendar months strings (in "YYYY-MM" format)
 * @param {Array} aggregatedValuesArr An array of aggregated values
 * @returns {Array} An array of formatted aggregation values suitable for use in a column chart
 */
const formatAggregationResultForColumnChart = function (
  calendarDatesMonthsStrArr,
  aggregatedValuesArr
) {
  if (!calendarDatesMonthsStrArr || !aggregatedValuesArr) return;

  // Create an array of Unix timestamp strings
  const timestampsArr = calendarDatesMonthsStrArr.map((calendarStr) =>
    new Date(calendarStr).getTime()
  );

  // Combine timestamp and value pairs
  // The timestamps array and values array have same lengths, use one for looping
  return timestampsArr.map((timestamp, i) => [
    timestamp,
    aggregatedValuesArr[i],
  ]);
};

/**
 * Creates an options object for each series drawn in a column chart
 * @param {Array} formattedAggregatedResultForColumnChart An array of formatted aggregated result array(s) from one or more datastreams
 * @param {Array} buildingIdsPhenomenonNamesArr An array of string(s) made up of building ID(s) + phenomenon name(s)
 * @returns {Array} An array made up of series options object(s)
 */
const createSeriesOptionsForColumnChart = function (
  formattedAggregatedResultForColumnChart,
  buildingIdsPhenomenonNamesArr
) {
  // Create an array of seriesOptions objects
  // Assumes that the observation array of arrays, phenomenon names array and phenomenon symbols array are of equal length
  // Use one of the arrays for looping
  if (
    formattedAggregatedResultForColumnChart.length !==
    buildingIdsPhenomenonNamesArr.length
  )
    throw new Error(
      "The observations array and phenomenon names array have different lengths"
    );

  return formattedAggregatedResultForColumnChart.map(
    (formattedAggResArray, i) => {
      return {
        name: `${buildingIdsPhenomenonNamesArr[i]}`,
        data: formattedAggResArray,
        turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
      };
    }
  );
};

/**
 * Creates a date string that is used as a header for a shared tooltip string for a column chart
 * @param {Number} pointXAxisValue The x-axis value (Unix timestamp) which is common for a set of data points
 * @param {String} aggregationInterval The aggregation interval as a string, either "daily" or "monthly"
 * @returns {String} A calendar date or calendar month string that is common for a set of data points
 */
const createHeaderDateString = function (pointXAxisValue, aggregationInterval) {
  if (aggregationInterval === "daily")
    return `${Highcharts.dateFormat("%A, %b %e, %Y", pointXAxisValue)}`;
  else if (aggregationInterval === "monthly")
    return `${Highcharts.dateFormat("%b %Y", pointXAxisValue)}`;
};

/**
 * Create a partial string for a column's chart title
 * @param {String} aggregationInterval The aggregation interval as a string, either "daily" or "monthly"
 * @param {String} aggregationType The aggregation type as a string, either "sum" or "average"
 * @returns {String} Partial string for chart title
 */
const createPartialTitleForColumnChart = function (
  aggregationInterval,
  aggregationType
) {
  // Case 1: No aggregation; return empty string
  if (!aggregationInterval && !aggregationType) return ``;

  // Case 2: Aggregation; capitalize the first characters
  return `${
    aggregationInterval.slice(0, 1).toUpperCase() + aggregationInterval.slice(1)
  } ${aggregationType.slice(0, 1).toUpperCase() + aggregationType.slice(1)}`;
};

/**
 * Create a full string for a column's chart title
 * @param {Array} datastreamNamesArr An array of datastream names as strings
 * @param {String} aggregationInterval The aggregation interval as a string, either "daily" or "monthly"
 * @param {String} aggregationType The aggregation type as a string, either "sum" or "average"
 * @returns {String} Full string for chart title
 */
const createFullTitleForColumnChart = function (
  phenomenonNamesArr,
  aggregationInterval,
  aggregationType
) {
  // Case 1: No aggregation; create a comma separated string of phenomenon names
  if (!aggregationInterval && !aggregationType)
    return `${createPartialTitleForColumnChart(
      aggregationInterval,
      aggregationType
    )}${createCombinedTextDelimitedByComma(phenomenonNamesArr)}`;

  // Case 2: Aggregation
  return `${createPartialTitleForColumnChart(
    aggregationInterval,
    aggregationType
  )} ${phenomenonNamesArr[0]}`;
};

/**
 * Draw a column chart using Highcharts library
 * @param {Array} formattedAggResultArraysForColumnChart An array made up of formatted aggregated result array(s) suitable for use in a column chart
 * @param {Object} extractedFormattedDatastreamProperties An object that contains arrays of formatted Datastream properties
 * @returns {undefined} undefined
 */
const drawColumnChartHighcharts = function (
  formattedAggResultArraysForColumnChart,
  extractedFormattedDatastreamProperties
) {
  // Arrays of datastream properties
  const {
    datastreamNamesArr,
    buildingIdsPhenomenonNamesArr,
    unitOfMeasurementSymbolsArr,
    aggregationInterval,
    aggregationType,
  } = extractedFormattedDatastreamProperties;

  // Create an array of phenomenon names
  const phenomenonNamesArr = datastreamNamesArr.map((datastreamName) =>
    extractPhenomenonNameFromDatastreamName(datastreamName)
  );

  // Create the array of series options object(s)
  const seriesOptionsArr = createSeriesOptionsForColumnChart(
    formattedAggResultArraysForColumnChart,
    buildingIdsPhenomenonNamesArr
  );

  // Assume that we will be comparing similar phenomena, so we can use the first phenomenon name
  const phenomenonName = phenomenonNamesArr[0];

  // Assume that we will be comparing similar phenomena, so we can use the first phenomenon symbol
  const unitOfMeasurementSymbol = unitOfMeasurementSymbolsArr[0];

  const textChartTitle = createFullTitleForColumnChart(
    phenomenonNamesArr,
    aggregationInterval,
    aggregationType
  );

  const textChartSubtitle = `Sampling rate(s): ${createCombinedTextDelimitedByComma(
    extractSamplingRateFromDatastreamName(datastreamNamesArr)
  )}`;

  Highcharts.chart("chart-column", {
    chart: {
      type: "column",
      zoomType: "x",
    },

    title: {
      text: textChartTitle,
    },

    subtitle: {
      text: textChartSubtitle,
    },

    xAxis: {
      type: "datetime",
      crosshair: true,
    },

    yAxis: {
      min: 0,
      title: {
        text: "Rainfall (mm)",
      },
    },

    tooltip: {
      formatter() {
        // Our tooltip is shared
        // this.x -- common for all points
        // this.points -- an array containing properties for each series
        // Note that our `reduce` method is in this format:
        // ((accumulator, currentValue, currentIndex) => {...}, initialValue)
        return this.points.reduce(
          (accumulator, point, i) =>
            `${accumulator} <br/> <span style="color:${point.color}">${
              point.series.name
            }</span>: <b>${point.y.toFixed(2)} ${
              unitOfMeasurementSymbolsArr[i]
            }</b>`,
          `${createHeaderDateString(this.x, aggregationInterval)}`
        );
      },
      shared: true,
    },

    plotOptions: {
      column: {
        pointPadding: 0.2,
        borderWidth: 0,
      },
    },

    exporting: chartExportOptions,

    series: seriesOptionsArr,
  });
};

export { formatAggregationResultForColumnChart, drawColumnChartHighcharts };
