'use strict';

/**
 * Module dependencies
 */

// Node.js core.
const fs = require('fs');
const path = require('path');

// Public node modules.
const _ = require('lodash');

/* eslint-disable prefer-template */
// Array of supported clients.
const CLIENTS = [
  'pg',
  'mysql',
  'mysql2',
  'sqlite3',
  'mariasql',
  'oracle',
  'strong-oracle',
  'mssql',
];

const defaultConfig = {
  host: 'localhost',
  charset: 'utf8',
};
/**
 * Knex hook
 */

module.exports = strapi => {
  // For each connection in the config register a new Knex connection.
  _.forEach(
    _.pickBy(strapi.config.connections, {
      connector: 'bookshelf',
    }),
    (connection, name) => {
      // Make sure we use the client even if the typo is not the exact one.
      switch (connection.settings.client) {
        case 'postgre':
        case 'postgres':
        case 'postgresql':
          connection.settings.client = 'pg';
          break;
        case 'sqlite':
          connection.settings.client = 'sqlite3';
          break;
        case 'maria':
        case 'mariadb':
          connection.settings.client = 'mariasql';
          break;
        case 'ms':
          connection.settings.client = 'mssql';
          break;
      }

      // Make sure the client is supported.
      if (!_.includes(CLIENTS, connection.settings.client)) {
        strapi.log.error(
          'The client `' +
            connection.settings.client +
            '` for the `' +
            name +
            '` connection is not supported.'
        );
        strapi.stop();
      }

      // Make sure the client is installed in the application
      // `node_modules` directory.
      let client;
      try {
        client = require(connection.settings.client);
      } catch (err) {
        strapi.log.error('The client `' + connection.settings.client + '` is not installed.');
        strapi.log.error(
          'You can install it with `$ npm install ' + connection.settings.client + ' --save`.'
        );
        strapi.stop();
      }

      const options = _.defaultsDeep(
        {
          client: connection.settings.client,
          connection: {
            host: _.get(connection.settings, 'host'),
            user: _.get(connection.settings, 'username') || _.get(connection.settings, 'user'),
            password: _.get(connection.settings, 'password'),
            database: _.get(connection.settings, 'database'),
            charset: _.get(connection.settings, 'charset'),
            schema: _.get(connection.settings, 'schema', 'public'),
            port: _.get(connection.settings, 'port'),
            socketPath: _.get(connection.settings, 'socketPath'),
            ssl: _.get(connection.settings, 'ssl', false),
            timezone: _.get(connection.settings, 'timezone', 'utc'),
            filename: _.get(connection.settings, 'filename', '.tmp/data.db'),
          },
          ...connection.options,
          debug: _.get(connection.options, 'debug', false),
          pool: {
            ..._.get(connection.options, 'pool', {}),
            min: _.get(connection.options, 'pool.min', 0),
          },
        },
        strapi.config.hook.settings.knex,
        defaultConfig
      );

      // Resolve path to the directory containing the database file.
      const fileDirectory = options.connection.filename
        ? path.dirname(path.resolve(strapi.config.appPath, options.connection.filename))
        : '';

      switch (options.client) {
        case 'mysql':
          options.connection.supportBigNumbers = true;
          options.connection.bigNumberStrings = true;
          options.connection.typeCast = (field, next) => {
            if (field.type == 'DECIMAL' || field.type === 'NEWDECIMAL') {
              var value = field.string();
              return value === null ? null : Number(value);
            }

            if (field.type == 'TINY' && field.length == 1) {
              let value = field.string();
              return value ? value == '1' : null;
            }
            return next();
          };
          break;
        case 'pg':
          client.types.setTypeParser(1700, 'text', parseFloat);

          if (_.isString(_.get(options.connection, 'schema'))) {
            options.pool = {
              ...options.pool,
              afterCreate: (conn, cb) => {
                conn.query(`SET SESSION SCHEMA '${options.connection.schema}';`, err => {
                  cb(err, conn);
                });
              },
            };
          } else {
            delete options.connection.schema;
          }
          break;
        case 'sqlite3':
          // Create the directory if it does not exist.
          try {
            fs.statSync(fileDirectory);
          } catch (err) {
            fs.mkdirSync(fileDirectory);
          }

          // Force base directory.
          // Note: it removes the warning logs when starting the administration in development mode.
          options.connection.filename = path.resolve(
            strapi.config.appPath,
            options.connection.filename
          );

          // Disable warn log
          // .returning() is not supported by sqlite3 and will not have any effect.
          options.log = {
            warn: () => {},
          };

          break;
      }

      // Finally, use the client via `knex`.
      // If anyone has a solution to use different paths for `knex` and clients
      // please drop us an email at support@strapi.io-- it would avoid the Strapi
      // applications to have `knex` as a dependency.
      try {
        // Try to require from local dependency.
        const connection = require('knex')(options);
        _.set(strapi, `connections.${name}`, connection);
      } catch (err) {
        strapi.log.error('Impossible to use the `' + name + '` connection...');
        strapi.log.warn(
          'Be sure that your client `' + name + '` are in the same node_modules directory'
        );
        strapi.log.error(err);
        strapi.stop();
      }
    }
  );
};
