import elasticSearch from 'elasticsearch'

export default class ElasticsearchService {

  constructor(config) {
    let elasticsearch = elasticSearch;
    this.config = config;

    let d = new Date();
    if (this.config.index) {
      this.dataIndex = this.config.index + '-' + d.getFullYear() + '.' + (d.getMonth() + 1).toString().padStart(2, '0');
      this.baselineIndex = this.config.index + '-' + 'baseline';
    }

    let host = this.config.protocol + '://' + this.config.username + ':' + this.config.password + '@' + this.config.host + ':' + this.config.port;
    let clientConfig = { hosts: [host] };
    if (this.config.ssl){
      clientConfig.ssl = this.config.ssl;
    }
    this.client = new elasticsearch.Client(clientConfig);
    this.createPending = [];
  }

  async health() {
    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.cluster.health({}, function (err, resp, status) {
          if (err) {
            console.log("HEALTH ERROR:");
            console.log(err);
          }
          if (resp) {
            console.log("HEALTH RESPONSE:");
            console.log(resp);
          }
          if (status) {
            console.log("HEALTH STATUS:");
            console.log(status);
          }
          resolve();
        });
      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async creating(index) {
    while (this.createPending.includes(index)) {
      // wait 1000 ms
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }

  async indexExists(index) {
    if (!index) {
      index = this.dataIndex;
    }
    if (this.createPending.includes(index)) {
      await this.creating();
      await new Promise(resolve => setTimeout(resolve, 1000));
      return true;
    }
    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.indices.exists({
          'index': index
        }, function (err, resp, status) {
          if (err) {
            console.log("INDEX EXISTS ERROR:");
            console.log(err);
          }
          if (resp) {
            resolve(resp);
          }
          if (status) {
            if (status == 404) {
              resolve(false);
            }
          }
          resolve();
        });

      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async getMapping(index) {
    if (!index) {
      index = this.dataIndex;
    }
    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.indices.getMapping({
          'index': index
        }, function (err, resp, status) {
          if (err) {
            console.log("GET MAPPING ERROR:");
            console.log(err);
          }
          if (resp) {
            console.log("GET MAPPING RESPONSE:");
            console.log(resp);
          }
          if (status) {
            console.log("GET MAPPING STATUS:");
            console.log(status);
          }
          resolve();
        });

      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async createIndex(index) {
    if (!index) {
      index = this.dataIndex;
    }
    if (this.createPending.includes(index)) {
      await this.creating();
      await new Promise(resolve => setTimeout(resolve, 1000));
      return true;
    } else {
      this.createPending.push(index);
    }
    let elasticsearchService = this;
    return new Promise(function (resolve) {
      try {
        elasticsearchService.client.indices.create({
          "index": index,
          "body": {
            "settings": {
              "index.refresh_interval": "10s",
              "number_of_shards": 1,
              "number_of_replicas": 3
            },
            "mappings": {
              "properties": {
                "logentrytype": { "type": "keyword" },
                "uuid": { "type": "keyword" },
                "description": { "type": "text" },
                "jirakey": { "type": "text" },
                "start": { "type": "date", "format": "epoch_millis" },
                "end": { "type": "date", "format": "epoch_millis" },
                "duration": { "type": "integer" },
                "warningTreshhold": {
                  "type": "scaled_float",
                  "scaling_factor": 100
                },
                "absoluteTreshhold": {
                  "type": "scaled_float",
                  "scaling_factor": 100
                },
                "deviation_percentage": { "type": "integer" },
                "deviation_exceeded": { "type": "boolean" },
                "execution_link": { "type": "text" },
                "runner_external_link": { "type": "text" }, //gitlab job url
                "extra_link": { "type": "text" },
                "app": { "type": "text" },
                "baseUrl": { "type": "text" },
                "env": { "type": "keyword" },
                "suite": { "type": "text" },
                "testedVersion": { "type": "text" },
                "failurecount": { "type": "integer" },
                "deductedfailurecount": { "type": "integer" },
                "color": { "type": "text" },
                "reportGroups": { "type": "text" },
                "invalid_result": { "type": "boolean" },
                "weekday": { "type": "integer" },
                "dayminute": { "type": "integer" },
                "totalDescribes": { "type": "integer" }, // total number of describes
                "totalSteps": { "type": "integer" }, // total number of steps
                "totalDescribesFailed": { "type": "integer" },  // total number of describes failed
                "totalStepsFailed": { "type": "integer" }, // total number of steps failed
                "describesSuccessRate": { "type": "integer" }, // success rate of describes in %
                "stepsSuccessRate": { "type": "integer" }, // sucess rate of steps in %
                "describesMap": {"type": "object"}
              }
            }
          }
        }, function (err, resp, status) {
          if (err) {
            console.log("ELEASTICSEARCH CREATE INDEX ERROR:");
            console.log(err);
          }
          // if (resp) {
          //     console.log("CREATE INDEX RESPONSE:");
          //     console.log(resp);
          // }
          if (status) {
            elasticsearchService.createPending.pop(index);
            if (status == 200) {
              resolve(true);
            } else {
              resolve(false);
            }
          }
          resolve();
        });

      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async deleteIndex(index) {
    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.indices.delete({
          'index': index
        }, function (err, resp, status) {
          if (err) {
            console.log("ELEASTICSEARCH DELETE INDEX ERROR:");
            console.log(err);
          }
          // if (resp) {
          //     console.log("DELETE INDEX RESPONSE:");
          //     console.log(resp);
          // }
          // if (status) {
          //     console.log("DELETE INDEX STATUS:");
          //     console.log(status);
          // }
          resolve();
        });

      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async index(data, index = false) {
    if (!index) {
      index = this.dataIndex;
    }
    if (!(await this.indexExists(index))) {
      await this.createIndex(index);
    }

    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.index({
          'index': index,
          'body': data
        }, function (err, resp) {
          if (err) {
            console.log("ELEASTICSEARCH INDEX ERROR:");
            console.error(err);
          }
          // if (resp) {
          //     console.log('Elasticsearch service, wrote to index: ' + index + ' RESPONSE: ');
          //     console.log(resp);
          // }
          resolve();
        });
      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async reIndex(sourceIndex, destinationIndex) {
    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.reindex({
          waitForCompletion: true,
          refresh: true,
          body: {
            source: {
              index: sourceIndex,
            },
            dest: {
              index: destinationIndex
            }
          }
        }, function (err, resp) {
          if (err) {
            console.log("ELEASTICSEARCH RE-INDEX ERROR:");
            console.error(err);
          }
          // if (resp) {
          //     console.log('RE-INDEX RESPONSE: ');
          //     console.log(resp);
          // }
          resolve();
        })
      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async getBaseline(logentrytype, uuid = false, index = false) {
    if (!index) {
      index = this.baselineIndex;
    }
    let client = this.client;
    let query = "logentrytype:\"" + logentrytype + "\"";
    if (uuid) {
      query = query + " AND uuid:\"" + uuid + "\"";
    }
    if (await this.indexExists(index)) {
      return new Promise(function (resolve) {
        let result = false;
        try {
          client.search({
            "index": index,
            "size": 1,
            "q": query,
            "sort": "start:desc"
          }, function (err, resp) {
            if (err) {
              console.log("ELEASTICSEARCH SEARCH BASELINE INDEX ERROR:");
              console.log(err);
            }
            if (resp) {
              if (resp.hits &&
                resp.hits.hits &&
                Array.isArray(resp.hits.hits) &&
                resp.hits.hits.length > 0 &&
                resp.hits.hits[0]._source &&
                resp.hits.hits[0]._source.logentrytype) {
                result = resp.hits.hits[0]._source;
              }
            }
            resolve(result);
          });

        } catch (err) {
          console.error(err)
          resolve(result);
        }
      });
    } else {
      return new Promise(function (resolve) {
        resolve(false);
      });
    }
  }

  async setBaseline(data, index = false) {
    if (!index) {
      index = this.baselineIndex;
    }
    if (!(await this.indexExists(index))) {
      await this.createIndex(index);
    }

    let client = this.client;
    return new Promise(function (resolve) {
      try {
        client.index({
          'index': index,
          'body': data
        }, function (err, resp) {
          if (err) {
            console.log("ELEASTICSEARCH SET BASELINE INDEX ERROR:");
            console.error(err);
          }
          // if (resp) {
          //     console.log("INDEX RESPONSE:");
          //     console.log(resp);
          // }
          resolve();
        });

      } catch (err) {
        console.error(err)
        resolve();
      }
    });
  }

  async search(index, size, query, sort, from = 0) {
    let client = this.client;
    return new Promise(function (resolve) {
      let result = false;
      try {
        client.search({
          "index": index,
          "size": size,
          "q": query,
          "sort": sort,
          "from": from
        }, function (err, resp) {
          if (err) {
            console.log("ELEASTICSEARCH searchEs ERROR:");
            console.log(err);
          }
          if (resp) {
            if (resp.hits &&
              resp.hits.hits &&
              Array.isArray(resp.hits.hits) &&
              resp.hits.hits.length > 0) {
              result = resp.hits.hits;
            } else {
              result = [];
            }
          }
          resolve(result);
        });

      } catch (err) {
        console.error(err)
        resolve(result);
      }
    });
  }

  async update(index, id, data) {
    let client = this.client;
    return new Promise(function (resolve) {
      let result = false;
      try {
        client.update({
          "index": index,
          "id": id,
          "body": {
            "doc": data
          }
        }, function (err, resp) {
          if (err) {
            console.log("ELEASTICSEARCH update ERROR:");
            console.log(err);
          }
          if (resp) {
            if (resp.hits &&
              resp.hits.hits &&
              Array.isArray(resp.hits.hits) &&
              resp.hits.hits.length > 0) {
              result = resp.hits.hits;
            } else {
              result = [];
            }
          }
          resolve(result);
        });

      } catch (err) {
        console.error(err)
        resolve(result);
      }
    });
  }
}
