import { Observable } from 'rx';
// import debug from 'debug';
import dedent from 'dedent';

import {
  getSocialProvider,
  getUsernameFromProvider
} from '../../server/utils/auth';
import { observeMethod, observeQuery } from '../../server/utils/rx';
import { wrapHandledError } from '../../server/utils/create-handled-error.js';

// const log = debug('fcc:models:userIdent');

export default function(UserIdent) {
  UserIdent.on('dataSourceAttached', () => {
    UserIdent.findOne$ = observeMethod(UserIdent, 'findOne');
  });
  // original source
  // github.com/strongloop/loopback-component-passport
  // find identity if it exist
  // if not redirect to email signup
  // if yes and github
  //   update profile
  //   update username
  //   update picture
  UserIdent.login = function(
    _provider,
    authScheme,
    profile,
    credentials,
    options,
    cb
  ) {
    const User = UserIdent.app.models.User;
    const AccessToken = UserIdent.app.models.AccessToken;
    const provider = getSocialProvider(_provider);
    options = options || {};
    if (typeof options === 'function' && !cb) {
      cb = options;
      options = {};
    }
    profile.id = profile.id || profile.openid;
    const query = {
      where: {
        provider: provider,
        externalId: profile.id
      },
      include: 'user'
    };

    if (provider === 'auth0') {

      const email = profile.emails[0].value;
      return User.findOne$({ where: { email } })
        .flatMap(user => {
          return user ?
            Observable.of(user) :
            User.create$({ email }).toPromise();
        })
        .flatMap(user => {
          if (!user) {
            throw wrapHandledError(
              new Error('could not find or create a user'),
              {
                message: dedent`
                  Oops... something is not right. We could not find or create a
                  user with that email.
                `,
                type: 'info',
                redirectTo: '/'
              }
            );
          }
          const createToken = observeQuery(
            AccessToken,
            'create',
            {
              userId: user.id,
              created: new Date(),
              ttl: user.constructor.settings.ttl
            }
          );
          const updateUser = user.update$({
            emailVerified: true,
            emailAuthLinkTTL: null,
            emailVerifyTTL: null
          });
          return Observable.combineLatest(
            Observable.of(user),
            createToken,
            updateUser,
            (user, token) => ({user, token})
          );
        })
        .subscribe(
          ({ user, token }) => cb(null, user, null, token),
          cb
        );

    } else {

      return UserIdent.findOne$(query)
        .flatMap(identity => {
          if (!identity) {
            throw wrapHandledError(
              new Error('user identity account not found'),
              {
                message: dedent`
                  New accounts can only be created using an email address.
                `,
                type: 'info',
                redirectTo: '/'
              }
            );
          }
          const modified = new Date();
          const user = identity.user();
          if (!user) {
            const username = getUsernameFromProvider(provider, profile);
            return observeQuery(
              identity,
              'updateAttributes',
              {
                isOrphaned: username || true
              }
            )
              .do(() => {
                throw wrapHandledError(
                  new Error('user identity is not associated with a user'),
                  {
                    type: 'info',
                    redirectTo: '/',
                    message: dedent`
    The user account associated with the ${provider} user ${username || 'Anon'}
    no longer exists.
                    `
                  }
                );
              });
          }

          // identity already exists
          // find user and log them in
          identity.credentials = credentials;
          const attributes = {
            // we no longer want to keep the profile
            // this is information we do not need or use
            profile: null,
            credentials: credentials,
            modified
          };
          const updateIdentity = observeQuery(
            identity,
            'updateAttributes',
            attributes
          );
          const createToken = observeQuery(
            AccessToken,
            'create',
            {
              userId: user.id,
              created: new Date(),
              ttl: user.constructor.settings.ttl
            }
          );
          return Observable.combineLatest(
            Observable.of(user),
            updateIdentity,
            createToken,
            (user, identity, token) => ({ user, identity, token })
          );
        })
        .subscribe(
          ({ user, identity, token }) => cb(null, user, identity, token),
          cb
        );
    }
  };
}

@@ -3010,63 +3010,159 @@
     
-              New accounts can only be created using an
+We cannot create new user accounts with the deprecated social sign in%0A    methods. If you already have an account with us, please sign in with your%0A   
  ema
@@ -3171,16 +3171,24 @@
  address
+ instead
 .%0A      

update the error messages (#17378)