 function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { wasCreateLoginPayload } from '../../types/server-cleaners'

import {
  asMaybeUsernameError,


} from '../../types/types'
import { encrypt } from '../../util/crypto/crypto'

import { makeKeysKit } from './keys'
import { loginFetch } from './login-fetch'
import { makeSecretKit } from './login-secret'
import { hashUsername } from './login-selectors'
import { saveStash } from './login-stash'

import { makeUsernameKit } from './login-username'
import { makePasswordKit } from './password'
import { makeChangePin2Kit } from './pin2'








/**
 * Determines whether or not a username is available.
 */
export async function usernameAvailable(
  ai,
  username,
  challengeId
) {
  const userId = await hashUsername(ai, username)
  const request = {
    challengeId,
    userId
  }
  return await loginFetch(ai, 'POST', '/v2/login', request)
    .then(reply => false) // It's not available if we can hit it!
    .catch((error) => {
      if (asMaybeUsernameError(error) != null) return true
      throw error
    })
}

/**
 * Assembles all the data needed to create a new login.
 */
export async function makeCreateKit(
  ai,
  parentSessionKey,
  appId,
  opts
) {
  const { keyInfo, password, pin, username } = opts
  const { io } = ai.props

  // For crash errors:
  ai.props.log.breadcrumb('makeCreateKit', {})

  // Figure out login identity:
  const isRoot = parentSessionKey == null
  const loginId = io.random(32)
  const loginKey = io.random(32)
  const sessionKey = { loginId, loginKey }

  // Create the basic login object, but without any authentication methods:
  const login = {
    appId,
    lastLogin: new Date(),
    loginId,
    loginKey,
    isRoot,
    pendingVouchers: [],
    children: []
  }

  const secretKit = makeSecretKit(ai, login)
  let keysKit
  let parentBox
  let passwordKit
  let pin2Kit
  let usernameKit

  // Set up optional login methods:
  if (keyInfo != null) {
    keysKit = makeKeysKit(ai, sessionKey, [keyInfo])
  }
  if (parentSessionKey != null) {
    parentBox = encrypt(io, loginKey, parentSessionKey.loginKey)
  }
  if (password != null && username != null) {
    passwordKit = await makePasswordKit(ai, login, username, password)
  }
  if (pin != null) {
    pin2Kit = makeChangePin2Kit(ai, login, username, pin, true)
  }
  if (isRoot && username != null) {
    usernameKit = await makeUsernameKit(ai, login, username)
  }

  // Bundle everything:
  const kit = {
    loginId,
    server: {
      ...wasCreateLoginPayload({
        appId,
        loginId,
        parentBox
      }),
      ..._optionalChain([keysKit, 'optionalAccess', _ => _.server]),
      ..._optionalChain([passwordKit, 'optionalAccess', _2 => _2.server]),
      ..._optionalChain([pin2Kit, 'optionalAccess', _3 => _3.server]),
      ...secretKit.server,
      ..._optionalChain([usernameKit, 'optionalAccess', _4 => _4.server])
    },
    serverPath: '/v2/login/create',
    stash: {
      appId,
      loginId,
      parentBox,
      ..._optionalChain([keysKit, 'optionalAccess', _5 => _5.stash]),
      ..._optionalChain([passwordKit, 'optionalAccess', _6 => _6.stash]),
      ..._optionalChain([pin2Kit, 'optionalAccess', _7 => _7.stash]),
      ...secretKit.stash,
      ..._optionalChain([usernameKit, 'optionalAccess', _8 => _8.stash])
    }
  }
  return { kit, sessionKey }
}

/**
 * Creates a new login on the auth server.
 */
export async function createLogin(
  ai,
  accountOpts,
  opts
) {
  const { challengeId, now = new Date() } = accountOpts

  // For crash errors:
  ai.props.log.breadcrumb('createLogin', {})

  const { kit, sessionKey } = await makeCreateKit(ai, undefined, '', opts)
  const request = { challengeId, data: kit.server }
  await loginFetch(ai, 'POST', kit.serverPath, request)

  kit.stash.lastLogin = now
  await saveStash(ai, kit.stash )
  return sessionKey
}
