const couchbase = require("couchbase");
const TestEnv = require('./test-env');
const searchIndexDef = {
  "name": "ft-search",
  "type": "fulltext-index",
  "params": {
    "mapping": {
      "default_mapping": {
        "enabled": true,
        "dynamic": true
      },
      "default_type": "_default",
      "default_analyzer": "standard",
      "default_datetime_parser": "dateTimeOptional",
      "default_field": "_all",
      "store_dynamic": false,
      "index_dynamic": true
    },
    "store": {
      "indexType": "scorch",
      "kvStoreName": ""
    },
    "doc_config": {
      "mode": "type_field",
      "type_field": "type",
      "docid_prefix_delim": "",
      "docid_regexp": ""
    }
  },
  "sourceType": "couchbase",
  "sourceName": "fieldtec-test",
  "sourceParams": {},
  "planParams": {
    "maxPartitionsPerPIndex": 171,
    "numReplicas": 0
  },
  "uuid": ""
};

let bucket;

function sleep(ms){
  return new Promise(resolve=>{
    setTimeout(resolve,ms);
  });
}

function bucketExists(bucketName, manager){
  return new Promise((resolve, reject) => {
    manager.listBuckets(function(err, buckets) {
      if (err) {
        logger.error('Error listing buckets:');
        logger.error(err);
        reject(err);
        return;
      }
      if (!buckets) {
        resolve(false);
        return;
      }
      let bucketFound = false;
      for (let i = 0; i < buckets.length; i++) {
        if (buckets[i].name === bucketName) {
          bucketFound = true;
          break;
        }
      }
      resolve(bucketFound);
    });
  });
}

function removeBucket(bucketName, manager){
  return new Promise((resolve, reject) => {
    manager.removeBucket(bucketName, (err) => {
      if (err) {
        reject(err);
      } else {
        resolve ();
      }
    });
  });
}

function createBucket(bucketName, manager){
  return new Promise((resolve, reject) => {
    manager.createBucket(bucketName, {ramQuotaMB: 256}, (err) => {
      if (err) {
        reject(err);
      } else {
        resolve ();
      }
    });
  });
}

function upsert(document){
  return new Promise((resolve, reject) => {
    bucket.upsert(document.documentId, document, function (err, res) {
      if(err){
        return reject(err);
      }
      resolve(res);
    });
  });
}

async function init() {
  // access cb cluster
  const testConfig = TestEnv.testConfig;
  const config = testConfig.dataServices.couchbase;

  const cluster = new couchbase.Cluster(config.host);
  cluster.authenticate(config.userName, config.password);
  const bucketName = config.bucket;
  const manager = cluster.manager(config.userName, config.password);
  cluster.authenticate(config.userName, config.password);
  // removing and recreating bucket so we'll have it fresh every time
  const exists = await bucketExists(bucketName, manager);
  if (exists) {
    try{
      await removeBucket(bucketName, manager);
        // wait for cb to complete creating bucket
      await sleep(5 * 1000);
    }catch (e) {
      logger.warn(`Unable to remove bucket: ${bucketName}`);
      logger.warn(e);
    }
  }
  try{
    await createBucket(bucketName, manager);
    await sleep(5 * 1000);
  }catch (e) {
    logger.error(`Unable to create bucket: ${bucketName}`);
    logger.error(e);
    throw e;
  }
  // prepare bucket
  bucket = cluster.openBucket(bucketName);
  bucket.operationTimeout = 5 * 1000;
  manager.searchIndexManager();
  await new Promise((resolve, reject)=>{
    bucket.on('connect', () => {
      resolve();
    });
    bucket.on('error', (err) => {
      reject(err);
    });
  });
  await createIndexes(bucketName);
  // create search index
  const searchIndexManager = manager.searchIndexManager();
  searchIndexDef.sourceName = bucketName;
  await new Promise((resolve, reject) => {
    searchIndexManager.createIndex(searchIndexDef, (err) => {
      if(err){
        logger.error(err);
        reject(err);
      } else {
        resolve();
      }
    });
  });
  await sleep(3 * 1000);
}

function N1Q1Query(query){
  const N1qlQuery = couchbase.N1qlQuery;
  const statement = N1qlQuery.fromString(query);
  statement.consistency(N1qlQuery.Consistency.REQUEST_PLUS);
  return statement;
}

async function query(str){
  return new Promise((resolve, reject) => {
    const query = N1Q1Query(str);
    bucket.query(query, function (err, res) {
      if(err){
        if(err.code === 4300){ // index already exists
          resolve();
          return;
        }
        return reject(err);
      }
      resolve(res);
    });
  });
}

async function createIndexes(bucketName) {
  const createIndexQueries = [
    'CREATE INDEX systemHeaderDeleted on `' + bucketName + '`(systemHeader.deleted);',
    'CREATE INDEX templateId on `' + bucketName + '`(systemHeader.templateId);',
    'CREATE INDEX keyIds on `' + bucketName + '`(systemHeader.keyIds);',
    'CREATE INDEX systemType on `' + bucketName + '`(systemHeader.systemType);',
    'CREATE INDEX versionId on `' + bucketName + '`(systemHeader.versionId);',
    'CREATE INDEX documentId on `' + bucketName + '`(documentId);'
  ];
  for (const i of createIndexQueries) {
    try{
      await query(i);
      await sleep(500);
    }catch(err){
      logger.error(err);
    }
  }
}

module.exports = {
  init,
  upsert
};


