// *****************************************************************************
// Copyright 2013-2024 Aerospike, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// *****************************************************************************

'use strict'

import * as Aerospike from '../../lib/aerospike.js';

import type { ConfigOptions, Host, TLSInfo, BasePolicyOptions } from '../../lib/aerospike.js';

import type {Argv} from 'yargs';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

import * as fs from 'fs'; // semver is likely the default export, but it may have named exports as well
import * as path from 'path'; // semver is likely the default export, but it may have named exports as well
import { hostname } from 'os';

// *****************************************************************************
//  Options parsing
// *****************************************************************************

const parser: Argv = yargs(hideBin(process.argv))
  .usage('$0 [options]')
  .options({
    help: {
      type: 'boolean',
      describe: 'Display this message.'
    },
    host: {
      alias: 'h',
      type: 'string',
      default: null,
      describe: 'Aerospike database address.'
    },
    port: {
      alias: 'p',
      type: 'number',
      default: null,
      describe: 'Aerospike database port.'
    },
    totalTimeout: {
      alias: 't',
      type: 'number',
      default: 1000,
      describe: 'Timeout in milliseconds.'
    },
    log: {
      alias: 'l',
      type: 'number',
      default: Aerospike.log.WARN,
      describe: 'Log level [0-5]'
    },
    log_file: {
      alias: 'f',
      type: 'number',
      default: fs.openSync('test.log', 'a'),
      describe: 'Log file to redirect the log messages'
    },
    namespace: {
      alias: 'n',
      type: 'string',
      default: 'test',
      describe: 'Namespace for the keys.'
    },
    set: {
      alias: 's',
      type: 'string',
      default: 'demo',
      describe: 'Set for the keys.'
    },
    user: {
      alias: 'U',
      type: 'string',
      default: null,
      describe: 'Username to connect to a secure cluster'
    },
    password: {
      alias: 'P',
      type: 'string',
      default: null,
      describe: 'Password to connect to a secure cluster'
    },
    clusterName: {
      type: 'string',
      describe: 'Name of the cluster to join'
    },
    cafile: {
      type: 'string',
      describe: 'Path to a trusted CA certificate file'
    },
    keyfile: {
      type: 'string',
      describe: 'Path to the client\'s key for mutual auth'
    },
    keyfilePassword: {
      type: 'string',
      describe: 'Decryption password for the client\'s key file'
    },
    certfile: {
      type: 'string',
      describe: 'Path to the client\'s certificate chain file for mutual auth'
    },
    auth: {
      type: 'number',
      describe: 'Specify client authentication mode'
    },
    testMetrics: {
      type: 'boolean',
      describe: 'Specify whether or not to run advanced testing.'
    },
    testStrongConsistency: {
      type: 'boolean',
      describe: 'Specify whether or not to run advanced testing.',
      default: false
    },
    testDynamicConfig: {
      type: 'boolean',
      describe: 'Specify whether or not to run dynamic configuration tests.'
    },
    testMetricsKeyBusy: {
      type: 'boolean',
      describe: 'Specify whether or not to run metrics key busy tests.'
    },
    testTimeoutDelay: {
      type: 'boolean',
      describe: 'Specify whether or not to run timeout delay tests.'
    },
    testPreferRack: {
      type: 'boolean',
      describe: 'Specify whether or not to run advanced testing. Requires two datacenter XDR configuration.'
    },
    testMRT: {
      type: 'boolean',
      describe: 'Specify whether or not to run advanced MRT tests.'
    },
    testXDR: {
      type: 'boolean',
      describe: 'Specify whether or not to run advanced testing.'
    }
  });


let options: any
if (process.env.OPTIONS) {
  const rawOptions: string[] = process.env.OPTIONS.trim().split(' ')
  options = parser.parse(rawOptions)
} else {
  options = parser.argv
}

if (options.help === true) {
  parser.showHelp()
  process.exit(0)
}

// enable debug stacktraces
process.env.AEROSPIKE_DEBUG_STACKTRACES = process.env.AEROSPIKE_DEBUG_STACKTRACES || 'true'

import * as url from "node:url"
function testDir (): string {
  return path.resolve( __dirname , '..');
}

options.getConfig = function (): any {
  let omitHelperClient = true;
  if(options.testMetrics){
    omitHelperClient = false
  }
  
  const defaultPolicy: BasePolicyOptions = {
    totalTimeout: options.totalTimeout,
    maxRetries: 6
  }
  const config: ConfigOptions = {
    log: {
      level: options.log,
      file: options.log_file
    },
    policies: {
      apply: defaultPolicy,
      batch: defaultPolicy,
      info: defaultPolicy,
      operate: defaultPolicy,
      query: defaultPolicy,
      read: defaultPolicy,
      remove: defaultPolicy,
      scan: defaultPolicy,
      write: defaultPolicy
    },
    modlua: {
      userPath: testDir()
    },
    hosts: [{
      addr: "127.0.0.1",
      port: 3000,
    }]
  } as any;

  if (options.host !== null) {
    const host = {
      addr: options.host,
      port: options.port || 3000,
    } as Host;
    config.hosts = [host]
  } else if (process.env.AEROSPIKE_HOSTS) {
    config.hosts = process.env.AEROSPIKE_HOSTS
  }

  if (options.user !== null) {
    config.user = options.user
  }
  if (options.password !== null) {
    config.password = options.password
  }

  if (options.clusterName) {
    config.clusterName = options.clusterName
  }

  if (options.cafile) {
    config.tls = {
      enable: true,
      cafile: options.cafile,
      certfile: options.certfile,
      keyfile: options.keyfile,
      keyfilePassword: options.keyfilePassword
    } as TLSInfo;
  }

  if (options.auth) {
    config.authMode = options.auth
  }
  // Create a very large error threshold to disable circuit breaker
  // Ratio between the following must be 50/1 at most
  config.maxErrorRate = 100000
  config.errorRateWindow = 10000

  return {config, omitHelperClient: omitHelperClient}
}

export default options;