// Import Mongo
import MongoDB, { MongoClient } from 'mongodb';

// Import dotenv
import * as dotenv from 'dotenv';

// Import state
import dbState from './dbState';

/*------------------------------------------------------------------------*/
/*                             Initialization                             */
/*------------------------------------------------------------------------*/

/**
 * Get a copy of the db collection class with
 *   Mongo/Amazon DocDB wrapper with functions that are
 *   equally compatible with both
 * @author Gabe Abrams
 * @param [opts] object containing all arguments
 * @param [opts.schemaVersion=1] the version of the schema. Whenever you
 *   change your schema, increment this number and dce-mango will automatically
 *   re-build and re-provision your collections
 * @param [dbName=env.DB_NAME ?? 'mango-store'] the name of the database
 *   to set up/use
 * @param [dbInfo=env vars] either include an object
 *   with a mongo-style url: { url } or include an object
 *   with pieces of a mongo-style url:
 *   { user, pass, host, [options] } where options
 *   is optional.
 *   Otherwise, dce-mango will use environment variables instead:
 *   either env.DB_URL or env.DB_USER + DB_PASS + DB_HOST must
 *   be included, optionally with an included DB_OPTIONS. You can also use
 *   mongo env vars: env.MONGO_URL or env.MONGO_USER + MONGO_PASS + MONGO_HOST
 *   and optionally MONGO_OPTIONS
 * @returns collection class that references the given database
 */
const initMango = (
  opts: {
    schemaVersion: number,
    dbName?: string,
    dbInfo?: (
      | {
        url: string,
      }
      | {
        user: string,
        pass: string,
        host: string,
        options?: string,
      }
    ),
  },
) => {
  // Pull environment variables from .env file
  dotenv.config();

  // TODO: should we have overwrite functionality?
  if (dbState.isInitialized) {
    return;
  }
  dbState.isInitialized = true;

  // Get opts and set defaults
  const { schemaVersion } = opts;
  const dbName = (opts.dbName ?? (process.env.DB_NAME ?? 'mango-store'));
  const dbInfo = (opts.dbInfo ?? process.env) as any;

  // Get db info as parts
  const url = (dbInfo.url ?? dbInfo.DB_URL ?? dbInfo.MONGO_URL);
  const user = (dbInfo.user ?? dbInfo.DB_USER ?? dbInfo.MONGO_USER);
  const pass = (dbInfo.pass ?? dbInfo.DB_PASS ?? dbInfo.MONGO_PASS);
  const host = (dbInfo.host ?? dbInfo.DB_HOST ?? dbInfo.MONGO_HOST);
  const options = (dbInfo.options ?? dbInfo.DB_OPTIONS ?? dbInfo.MONGO_OPTIONS);

  // Make sure we have enough info to get a db url
  if (
    !url
    && (!user || !pass || !host)
  ) {
    // eslint-disable-next-line no-console
    console.log('DCE-MANGO: No database information included. Either include a database url or include all of the following: user, pass, host. Fatal error. Now exiting.');
    process.exit(1);
  }

  // Get db url
  const dbURL = (
    url
    ?? `mongodb://${user}:${pass}@${host}/${dbName}${(options) ? '?' : ''}${(options) ?? ''}`
  );

  // Create a schema version tag
  dbState.schemaVersionTag = `version_${schemaVersion}`;

  // Promise that resolves with collection names
  dbState.initDB = (async () => {
    // Connect the mongo client
    let client: MongoDB.MongoClient;
    try {
      const potentialClient = await MongoClient.connect(dbURL);

      // Make sure the client is defined
      if (!potentialClient) {
        throw new Error('Database returned an empty client');
      }

      // Save the client
      client = potentialClient;
    } catch (err) {
      // This is a fatal error
      // eslint-disable-next-line no-console
      console.log('We could not connect to the database. This is a fatal error! Now exiting.');
      // eslint-disable-next-line no-console
      console.log(err);
      process.exit(1);
    }

    // Get the database and its collections
    let collections;
    try {
      // Use client to get database
      dbState.db = client.db(dbName);
      dbState.client = client;

      // Get the list of collections
      collections = await dbState.db.listCollections().toArray();
    } catch (err) {
      // This is a fatal error
      // eslint-disable-next-line no-console
      console.log('We could not list the collections in the database. This is a fatal error! Now exiting.');
      // eslint-disable-next-line no-console
      console.log(err);
      process.exit(1);
    }

    // Return the list of collection names
    return collections.map((collection: { name: string }) => {
      return collection.name;
    });
  })();
};

export default initMango;
