import ElasticsearchService from '../../services/elasticsearch-service.js';
import path from 'path'
import fs from 'fs'
export default class ElasticsearchPublisher {

  constructor(config) {
    this.fs = fs;
    this.path = path;
    
    this.elasticsearch = new ElasticsearchService(config);
    this.config = config;
  }

  async publish(reportFilePath) {
    console.log("Running ElasticsearchPublisher");
    try {
      let logStr = this.fs.readFileSync(reportFilePath, 'utf8');
      if (!logStr) {
        throw new Error('Could not read logfile');
      }
      this.log = JSON.parse(logStr); 

      if (!this.log.allResults.meta || !this.log.allResults.meta.env) {
        throw new Error('Logfile is missing data');
      }
      this.log.allResults['performanceTestSlow'] = false;
      this.describes = this.log.allResults.describes;
      delete this.log.allResults.describes;
      let globalMeta = this.log.allResults.meta;

      let elasticsearchPublisher = this;
      let publishPromises = [];

      publishPromises.push(new Promise(async (resolve) => {
        await elasticsearchPublisher.indexGlobalResult(this.log.allResults);
        resolve();
      }));

      this.describes.forEach(describe => {    
        publishPromises.push(new Promise(async (resolve) => {
          await elasticsearchPublisher.indexDescrResult(describe, globalMeta);
          resolve();
        }));
      });

      if (this.log.groupedResults) {
        let groups = Object.keys(this.log.groupedResults);
        groups.forEach(group => {
          this.log.groupedResults[group]['performanceTestSlow'] = false;
          publishPromises.push(new Promise(async (resolve) => {
            await elasticsearchPublisher.indexGlobalResult(this.log.groupedResults[group], group);
            resolve();
          }));
        });
      }

      await Promise.all(publishPromises);

      if (this.config.deleteOldReports && this.config.keepMonthsHistoryNbr) {
        let d = new Date();
        d.setMonth(d.getMonth() - this.config.keepMonthsHistoryNbr);
        let delIndex = this.config.index + '-' +
          d.getFullYear() + '.' + (d.getMonth() + 1).toString().padStart(2, '0');
        if (await this.elasticsearch.indexExists(delIndex)) {
          await this.elasticsearch.deleteIndex(delIndex);
        }
      }
    } catch (error) {
      console.error('An error occured in ElasticsearchPublisher.publish, error:' + error);
      return;
    }
  }

  async indexGlobalResult(globalLog, reportGroup = 'GENERAL') {
    try {
      if (this.config.disableDeductedWait) {
        globalLog.deductedWaits = 0;
      }
      let testDurationMs = globalLog.total_duration - globalLog.deductedWaits - globalLog.deductTime;
      let execution_link = '';
      if (globalLog.executionLink) {
        execution_link = globalLog.executionLink;
      }
      let extra_link = '';
      if (globalLog.extraLink) {
        extra_link = globalLog.extraLink;
      }
      let runner_external_link = '';
      if (globalLog.joburl) {
        runner_external_link = globalLog.joburl;
      }
      let startDate = new Date(globalLog.start);
      let weekday = startDate.getDay();
      let dayminute = (startDate.getHours() * 60) + startDate.getMinutes();
      let result = {
        logentrytype: 'testCycle',
        app: globalLog.meta.app,
        baseUrl: globalLog.meta.baseUrl,
        env: globalLog.meta.env,
        description: 'Testcycle execution on: ' + globalLog.meta.app + ' / of suite: ' + globalLog.meta.suite,
        start: globalLog.start,
        end: globalLog.end,
        duration: testDurationMs,
        suite: globalLog.meta.suite,
        deviation_percentage: 0,
        deviation_exceeded: false,
        warningTreshhold: (Math.round(this.config.performanceLimits.warningTreshholds['global'] * 100) / 100),
        execution_link: execution_link,
        runner_external_link: runner_external_link,
        extra_link: extra_link,
        testedVersion: globalLog.meta.testedVersion,
        failurecount: globalLog.total_failures,
        deductedfailurecount: globalLog.deducted_failures,
        color: 'blue',
        reportGroups: reportGroup,
        invalid_result: globalLog.meta.invalidateResult,
        weekday: weekday,
        dayminute: dayminute,
        totalDescribes: globalLog.totalDescribes,
        totalSteps: globalLog.totalSteps,
        totalDescribesFailed: globalLog.totalDescribesFailed,
        totalStepsFailed: globalLog.totalStepsFailed,
        describesSuccessRate: globalLog.describesSuccessRate,
        stepsSuccessRate: globalLog.stepsSuccessRate,
        describesMap: globalLog.describesMap
      };
      let baseline = {};
      if (reportGroup === 'GENERAL') {
        baseline = await this.elasticsearch.getBaseline('testCycle');
        if (!baseline) {
          await this.elasticsearch.setBaseline(result);
          baseline = result;
        }
      } else {
        let baselineIndex = this.config.index + '-' + reportGroup + '-' + 'baseline'
        baselineIndex = baselineIndex.toLowerCase();
        baseline = await this.elasticsearch.getBaseline('testCycle', false, baselineIndex);
        if (!baseline) {
          await this.elasticsearch.setBaseline(result, baselineIndex);
          baseline = result;
        }
      }

      let timeDiffPerc = ((100 * testDurationMs / baseline.duration) - 100);
      result.deviation_percentage = timeDiffPerc;
      if (testDurationMs < (this.config.performanceLimits.warningTreshholds['global'] * 1000) ||
        (timeDiffPerc > -this.config.performanceLimits.maxBaselineDeviationPercentages['global'] &&
          timeDiffPerc < this.config.performanceLimits.maxBaselineDeviationPercentages['global'])) {
        result.color = 'blue';
      } else if (timeDiffPerc > this.config.performanceLimits.maxBaselineDeviationPercentages['global']) {
        result.color = 'red';
        globalLog['performanceTestSlow'] = true;
        result.deviation_exceeded = true;
      } else if (timeDiffPerc < -Math.abs(this.config.performanceLimits.maxBaselineDeviationPercentages['global'])) {
        result.color = 'green';
      }
      await this.elasticsearch.index(result);
    } catch (error) {
      console.error('Error processing global log in elasticsearch publisher, error: ' + error);
      return null;
    }
  }

  async indexDescrResult(describe, globalMeta) {

    try {
      if (this.config.disableDeductedWait) {
        describe.deductedWaits = 0;
      }
      let testDurationMs = describe._duration - describe.deductedWaits - describe.deductTime;
      let reportGroups = 'NONE';
      if (describe.meta.reportGroups) {
        reportGroups = describe.meta.reportGroups;
      }
      let execution_link = '';
      if (this.log.allResults.executionLink) {
        execution_link = this.log.allResults.executionLink;
      }
      let extra_link = '';
      if (this.log.allResults.extraLink) {
        extra_link = this.log.allResults.extraLink;
      }
      let runner_external_link = '';
      if (this.log.allResults.joburl) {
        runner_external_link = this.log.allResults.joburl;
      }
      let startDate = new Date(describe.start);
      let weekday = startDate.getDay();
      let dayminute = (startDate.getHours() * 60) + startDate.getMinutes();
      let result = {
        logentrytype: 'testCaseExecution',
        uuid: describe.meta.uuid,
        jirakey: describe.jirakey,
        app: globalMeta.app,
        baseUrl: globalMeta.baseUrl,
        env: globalMeta.env,
        description: describe.description,
        start: describe.start,
        end: describe.end,
        duration: testDurationMs,
        suite: globalMeta.suite,
        deviation_percentage: 0,
        deviation_exceeded: false,
        warningTreshhold: (Math.round(this.config.performanceLimits.warningTreshholds['DESCR duration'] * 100) / 100),
        execution_link: execution_link,
        runner_external_link: runner_external_link,
        extra_link: extra_link,
        testedVersion: this.log.allResults.meta.testedVersion,
        failurecount: describe.failures,
        deductedfailurecount: describe.deductedfailures,
        color: 'blue',
        reportGroups: reportGroups,
        weekday: weekday,
        dayminute: dayminute,
        totalDescribes: describe.totalDescribes,
        totalSteps: describe.totalSteps,
        totalDescribesFailed: describe.totalDescribesFailed,
        totalStepsFailed: describe.totalStepsFailed,
        describesSuccessRate: describe.describesSuccessRate,
        stepsSuccessRate: describe.stepsSuccessRate,
        describesMap: describe.describesMap
      };

      let baseline = await this.elasticsearch.getBaseline('testCaseExecution', describe.meta.uuid);
      if (!baseline) {
        await this.elasticsearch.setBaseline(result);
        baseline = result;
      }

      let timeDiffPerc = ((100 * testDurationMs / baseline.duration) - 100);
      result.deviation_percentage = timeDiffPerc;
      if (testDurationMs < (this.config.performanceLimits.warningTreshholds['DESCR duration'] * 1000) ||
        (timeDiffPerc > -this.config.performanceLimits.maxBaselineDeviationPercentages['DESCR duration'] &&
          timeDiffPerc < this.config.performanceLimits.maxBaselineDeviationPercentages['DESCR duration'])) {
        result.color = 'blue';
      } else if (timeDiffPerc > this.config.performanceLimits.maxBaselineDeviationPercentages['DESCR duration']) {
        result.color = 'red';
        result.deviation_exceeded = true;
      } else if (timeDiffPerc < -Math.abs(this.config.performanceLimits.maxBaselineDeviationPercentages['DESCR duration'])) {
        result.color = 'green';
      }

      await this.elasticsearch.index(result);

      let publishPromises = [];
      let elasticsearchPublisher = this;

      describe.tests.forEach(test => {
        if (test.meta.esReportDetail && ((test.meta.stepId && test.meta.stepId !== "") || (test.meta.zephyrStep && test.meta.zephyrStep !== ""))) {
          if ((test.meta.stepId == null || test.meta.stepId == "") && !(test.meta.zephyrStep == null || test.meta.zephyrStep == "")) {
            test.meta["stepId"] = test.meta.zephyrStep;
          }
          if (test.state !== "passed") {
            if (describe.deductedfailures) {
              test["failure"] = 0;
              test["deductedFailure"] = 1;
            } else {
              test["failure"] = 1;
              test["deductedFailure"] = 0;
            }
          } else {
            test["failure"] = 0;
            test["deductedFailure"] = 0;
          }
          test["execution_link"] = execution_link;
          test["extra_link"] = extra_link;
          test["runner_external_link"] = runner_external_link;
          publishPromises.push(new Promise(async (resolve) => {
            await elasticsearchPublisher.indexStepResult(test, describe.meta.uuid, globalMeta);
            resolve();
          }));
        }
      });

      await Promise.all(publishPromises);


    } catch (error) {
      console.error('Error processing describe log in elasticsearch publisher: ' + describe.description + ' error: ' + error);
      return null;
    }
  }

  async indexStepResult(step, uuid, globalMeta) {

    try {
      if (this.config.disableDeductedWait) {
        step.deductedWaits = 0;
      }
      let testDurationMs = step._duration - step.deductedWaits - step.deductTime;
      let startDate = new Date(step.start);
      let weekday = startDate.getDay();
      let dayminute = (startDate.getHours() * 60) + startDate.getMinutes();
      let endDate = new Date(step.end);

      let result = {
        logentrytype: 'testStepExecution',
        uuid: uuid + "-" + step.meta.stepId,
        jirakey: "",
        app: globalMeta.app,
        baseUrl: globalMeta.baseUrl,
        env: globalMeta.env,
        description: step.fullTitle,
        start: startDate.getTime(),
        end: endDate.getTime(),
        duration: testDurationMs,
        suite: globalMeta.suite,
        deviation_percentage: 0,
        deviation_exceeded: false,
        warningTreshhold: 0,
        execution_link: step.execution_link,
        runner_external_link: step.runner_external_link,
        extra_link: step.extra_link,
        testedVersion: this.log.allResults.meta.testedVersion,
        failurecount: step.failure,
        deductedfailurecount: step.deductedFailure,
        color: '',
        reportGroups: '',
        weekday: weekday,
        dayminute: dayminute,
        totalDescribes: step.totalDescribes,
        totalSteps: step.totalSteps,
        totalDescribesFailed: step.totalDescribesFailed,
        totalStepsFailed: step.totalStepsFailed,
        describesSuccessRate: step.describesSuccessRate,
        stepsSuccessRate: step.stepsSuccessRate,
        describesMap: step.describesMap
      };

      await this.elasticsearch.index(result);
    } catch (error) {
      console.error('Error processing step log in elasticsearch publisher: ' + step.fullTitle + ' error: ' + error);
      return null;
    }
  }
}
