text: 'use strict';
  
  /**
   * Module dependencies.
   */
  
  // Public node modules.
  const _ = require('lodash');
  const request = require('request');
  
  // Purest strategies.<
  const Purest = require('purest');
  
  /**
   * Connect thanks to a third-party provider.
   *
   *
   * @param {String}    provider
   * @param {String}    access_token
   *
   * @return  {*}
   */
  
  exports.connect = (provider, query) => {
    const access_token = query.access_token || query.code || query.oauth_token;
  
    return new Promise((resolve, reject) => {
      if (!access_token) {
        return reject(null, {
          message: 'No access_token.',
        });
      }
  
      // Get the profile.
      getProfile(provider, query, async (err, profile) => {
        if (err) {
          return reject(err);
        }
  
        // We need at least the mail.
        if (!profile.email) {
          return reject([
            {
              message: 'Email was not available.',
            },
            null,
          ]);
        }
  
        try {
          const users = await strapi.query('user', 'users-permissions').find({
            email: profile.email,
          });
  
          const advanced = await strapi
            .store({
              environment: '',
              type: 'plugin',
              name: 'users-permissions',
              key: 'advanced',
            })
            .get();
  
          if (
            _.isEmpty(_.find(users, { provider })) &&
            !advanced.allow_register
          ) {
            return resolve([
              null,
              [{ messages: [{ id: 'Auth.advanced.allow_register' }] }],
              'Register action is actualy not available.',
            ]);
          }
  
          const user = _.find(users, { provider });
  
          if (!_.isEmpty(user)) {
            return resolve([user, null]);
          }
  
          if (
            !_.isEmpty(_.find(users, user => user.provider !== provider)) &&
            advanced.unique_email
          ) {
            return resolve([
              null,
              [{ messages: [{ id: 'Auth.form.error.email.taken' }] }],
              'Email is already taken.',
            ]);
          }
  
          // Retrieve default role.
          const defaultRole = await strapi
            .query('role', 'users-permissions')
            .findOne({ type: advanced.default_role }, []);
  
          // Create the new user.
          const params = _.assign(profile, {
            provider: provider,
            role: defaultRole.id,
          });
  
          const createdUser = await strapi
            .query('user', 'users-permissions')
            .create(params);
  
          return resolve([createdUser, null]);
        } catch (err) {
          reject([null, err]);
        }
      });
    });
  };
  
  /**
   * Helper to get profiles
   *
   * @param {String}   provider
   * @param {Function} callback
   */
  
  const getProfile = async (provider, query, callback) => {
    const access_token = query.access_token || query.code || query.oauth_token;
  
    const grant = await strapi
      .store({
        environment: '',
        type: 'plugin',
        name: 'users-permissions',
        key: 'grant',
      })
      .get();
  
    switch (provider) {
      case 'discord': {
        const discord = new Purest({
          provider: 'discord',
          config: {
            discord: {
              'https://discordapp.com/api/': {
                __domain: {
                  auth: {
                    auth: { bearer: '[0]' },
                  },
                },
                '{endpoint}': {
                  __path: {
                    alias: '__default',
                  },
                },
              },
            },
          },
        });
        discord
          .query()
          .get('users/@me')
          .auth(access_token)
          .request((err, res, body) => {
            if (err) {
              callback(err);
            } else {
              // Combine username and discriminator because discord username is not unique
              var username = `${body.username}#${body.discriminator}`;
              callback(null, {
                username: username,
                email: body.email,
              });
            }
          });
        break;
      }
      case 'facebook': {
        const facebook = new Purest({
          provider: 'facebook',
        });
  
        facebook
          .query()
          .get('me?fields=name,email')
          .auth(access_token)
          .request((err, res, body) => {
            if (err) {
              callback(err);
            } else {
              callback(null, {
                username: body.name,
                email: body.email,
              });
            }
          });
        break;
      }
      case 'google': {
        const config = {
          google: {
            'https://www.googleapis.com': {
              __domain: {
                auth: {
                  auth: { bearer: '[0]' },
                },
              },
              '{endpoint}': {
                __path: {
                  alias: '__default',
                },
              },
              'oauth/[version]/{endpoint}': {
                __path: {
                  alias: 'oauth',
                  version: 'v3',
                },
              },
            },
          },
        };
        const google = new Purest({ provider: 'google', config });
  
        google
          .query('oauth')
          .get('tokeninfo')
          .qs({ access_token })
          .request((err, res, body) => {
            if (err) {
              callback(err);
            } else {
              callback(null, {
                username: body.email.split('@')[0],
                email: body.email,
              });
            }
          });
        break;
      }
      case 'github': {
        const github = new Purest({
          provider: 'github',
          defaults: {
            headers: {
              'user-agent': 'strapi',
            },
          },
        });
  
        request.post(
          {
            url: 'https://github.com/login/oauth/access_token',
            form: {
              client_id: grant.github.key,
              client_secret: grant.github.secret,
              code: access_token,
            },
          },
          (err, res, body) => {
            github
              .query()
              .get('user')
              .auth(body.split('&')[0].split('=')[1])
              .request((err, res, body) => {
                if (err) {
                  callback(err);
                } else {
                  callback(null, {
                    username: body.login,
                    email: body.email,
                  });
                }
              });
          }
        );
        break;
      }
      case 'microsoft': {
        const microsoft = new Purest({
          provider: 'microsoft',
          config: {
            microsoft: {
              'https://graph.microsoft.com': {
                __domain: {
                  auth: {
                    auth: { bearer: '[0]' },
                  },
                },
                '[version]/{endpoint}': {
                  __path: {
                    alias: '__default',
                    version: 'v1.0',
                  },
                },
              },
            },
          },
        });
  
        microsoft
          .query()
          .get('me')
          .auth(access_token)
          .request((err, res, body) => {
            if (err) {
              callback(err);
            } else {
              callback(null, {
                username: body.userPrincipalName,
                email: body.userPrincipalName,
              });
            }
          });
        break;
      }
      case 'twitter': {
        const twitter = new Purest({
          provider: 'twitter',
          key: grant.twitter.key,
          secret: grant.twitter.secret,
        });
  
        twitter
          .query()
          .get('account/verify_credentials')
          .auth(access_token, query.access_secret)
          .qs({ screen_name: query['raw[screen_name]'], include_email: 'true' })
          .request((err, res, body) => {
            if (err) {
              callback(err);
            } else {
              callback(null, {
                username: body.screen_name,
                email: body.email,
              });
            }
          });
        break;
      }
      case 'instagram': {
        const instagram = new Purest({
          provider: 'instagram',
          key: grant.instagram.key,
          secret: grant.instagram.secret,
        });
  
        instagram
          .query()
          .get('users/self')
          .qs({ access_token })
          .request((err, res, body) => {
            if (err) {
              callback(err);
            } else {
              callback(null, {
                username: body.data.username,
                email: `${body.data.username}@strapi.io`, // dummy email as Instagram does not provide user email
              });
            }
          });
        break;
      }
      default:
        callback({
          message: 'Unknown provider.',
        });
        break;
    }
  };
  