"use strict";

import {
  checkForAndDeleteUniqueObservationsFromLargerArray,
  extractSamplingRateFromDatastreamName,
  extractBuildingIdFromDatastreamName,
} from "./chartHelpers.mjs";

import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fetchData.mjs";

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

/**
 * Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
 * Rücklauf temperature [RL] (i.e., dT = VL - RL) for a single building
 *
 * @param {Array} observationsPlusMetadataArr A 1*2 array, where the first element is an array made up of two arrays of observations and the second element is an array of two metadata objects
 * @returns {Array} An array made up of timestamps + values for dT
 */
const calculateVorlaufMinusRuecklaufTemperatureObservations = function (
  observationsPlusMetadataArr
) {
  // Extract Vorlauf temperature, Ruecklauf temperature; first element of array
  const [[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]] =
    observationsPlusMetadataArr;

  // Compare the lengths of the observations arrays for VL and RL,
  // delete the unique observation(s), if necessary
  const [vorlaufTemperatureObsFinalArr, ruecklaufTemperatureObsFinalArr] =
    vorlaufTemperatureObsArr.length === ruecklaufTemperatureObsArr.length
      ? [vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]
      : checkForAndDeleteUniqueObservationsFromLargerArray(
          vorlaufTemperatureObsArr,
          ruecklaufTemperatureObsArr
        );

  // Extract the temperature values
  const vorlaufTemperatureValues = vorlaufTemperatureObsFinalArr.map(
    (vlTempObs) => vlTempObs[1]
  );
  const ruecklaufTemperatureValues = ruecklaufTemperatureObsFinalArr.map(
    (rlTempObs) => rlTempObs[1]
  );

  // Check that the final observation arrays have equal lengths
  if (
    vorlaufTemperatureObsFinalArr.length !==
    ruecklaufTemperatureObsFinalArr.length
  ) {
    throw new Error(
      "The two observations arrays (VL and RL) have different lengths"
    );
  }
  // The final observation arrays now have equal length, we need only use one of them for looping
  // Resulting array contains the following pairs (timestamp + dT)
  else {
    return vorlaufTemperatureObsFinalArr.map((vlTempObsFinal, i) => {
      // Use timestamp from VL, since is equal to that of RL
      const timestamp = vlTempObsFinal[0];

      // Case 1: One of the observation values is `null`,
      // no need to calculate temperature difference
      // Case 2: Neither of the observation values is `null`,
      // calculate temperature difference
      return vorlaufTemperatureValues[i] === null ||
        ruecklaufTemperatureValues[i] === null
        ? [timestamp, null]
        : [
            timestamp,
            vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
          ];
    });
  }
};

/**
 * Create synthetic metadata for the temperature difference, dT, between Vorlauf temperature [VL] and
 * Rücklauf temperature [RL] (i.e., dT = VL - RL) of a single building
 *
 * @param {Array} observationsPlusMetadataArr A 1*2 array, where the first element is an array made up of two arrays of observations and the second element is an array of two metadata objects
 * @returns {Object} A metadata object for dT, made up of three properties: description, name and unit of measurement
 */
const createVorlaufMinusRuecklaufTemperatureMetadata = function (
  observationsPlusMetadataArr
) {
  // Extract metadata; second element of array, note that we skip the first element
  const [, [metadataVorlauf, metadataRuecklauf]] = observationsPlusMetadataArr;

  // From Vorlauf metadata, extract `name` and `unitOfMeasurement`
  const {
    name: datastreamNameVorlauf,
    unitOfMeasurement: unitOfMeasurementVorlauf,
  } = metadataVorlauf;

  // From Ruecklauf metadata, extract `name`
  const { name: datastreamNameRuecklauf } = metadataRuecklauf;

  // Extract the phenomenon names from the Datastream names
  const phenomenonNameVorlauf = extractPhenomenonNameFromDatastreamName(
    datastreamNameVorlauf
  );
  const phenomenonNameRuecklauf = extractPhenomenonNameFromDatastreamName(
    datastreamNameRuecklauf
  );

  // Create our custom datastream description text
  // The resulting datastream description string has two `temperature` substrings;
  // replace the first occurence with an empty string
  const descriptionTempDifference =
    `Computed dT: ${phenomenonNameVorlauf} minus ${phenomenonNameRuecklauf}`.replace(
      "temperature",
      ""
    );

  // Create an array of the VL datastream name, this array has only one element.
  // We need this structure since the functions `extractBuildingIdFromDatastreamName`
  // and `extractSamplingRateFromDatastreamName` expect an array of datastream names
  const datastreamNameVorlaufArr = [datastreamNameVorlauf];

  // Extract the building ID from the datastream name
  const buildingId = extractBuildingIdFromDatastreamName(
    datastreamNameVorlaufArr
  );

  // Extract the sampling rate from the datastream name
  const samplingRate = extractSamplingRateFromDatastreamName(
    datastreamNameVorlaufArr
  );

  // Create our custom datastream name text
  const nameTempDifference = `${buildingId} / dT Temperature difference (VL-RL) DS:${samplingRate}`;

  return {
    descriptionTempDifference,
    nameTempDifference,
    unitOfMeasurementVorlauf,
  };
};

/**
 * Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
 * Rücklauf temperature [RL] (i.e., dT = VL - RL). In addition, create synthetic metadata
 * for the temperature difference
 * @async
 * @param {String} baseUrl Base URL of the STA server
 * @param {Object} urlParams The URL parameters to be sent together with the GET request
 * @param {Array} buildingSamplingRateNestedArr A N*1 array (where N >= 1) containing a nested array of buildings & sampling rates as strings, i.e. [["101", "15min"]] or [["101", "15min"], ["102", "60min"]] or [["101", "15min"], ["102", "60min"], ["225", "60min"]], etc
 * @returns {Promise} A promise that contains a 1*2 array (the first element is an array that contans N Observations arrays; and the second element is an array of N Datastream metadata objects) when fulfilled
 */
export const calculateVorlaufMinusRuecklaufTemperature = async function (
  baseUrl,
  urlParams,
  buildingSamplingRateNestedArr
) {
  try {
    // Arrays to store our results
    const combinedObservationsArr = [];
    const combinedMetadataArr = [];

    const buildingDataPointSamplingRateNestedTwiceArr =
      buildingSamplingRateNestedArr.map((bldgSmplngRate) => {
        // The building ID is the first element, sampling rate is second element
        return [
          [bldgSmplngRate[0], "vl", bldgSmplngRate[1]],
          [bldgSmplngRate[0], "rl", bldgSmplngRate[1]],
        ];
      });

    // Note: We have to use a for/of loop here due to the asynchronous nature of our code
    for (const bldgDataPtSamplingRateNestedArr of buildingDataPointSamplingRateNestedTwiceArr) {
      const observationsPlusMetadata =
        await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
          baseUrl,
          urlParams,
          bldgDataPtSamplingRateNestedArr
        );

      // dT observations (timestamp + value)
      const vorlaufMinusRuecklaufTemperatureObs =
        calculateVorlaufMinusRuecklaufTemperatureObservations(
          observationsPlusMetadata
        );

      // dT metadata
      const {
        descriptionTempDifference,
        nameTempDifference,
        unitOfMeasurementVorlauf,
      } = createVorlaufMinusRuecklaufTemperatureMetadata(
        observationsPlusMetadata
      );

      // The datastream object that we return needs to have these property names
      const description = descriptionTempDifference;
      const name = nameTempDifference;
      const unitOfMeasurement = unitOfMeasurementVorlauf;

      // Add the observations and metadata to our arrays
      combinedObservationsArr.push(vorlaufMinusRuecklaufTemperatureObs);
      combinedMetadataArr.push({ description, name, unitOfMeasurement });
    }

    return [combinedObservationsArr, combinedMetadataArr];
  } catch (err) {
    console.error(err);
  }
};
