 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 { wasChangeUsernamePayload } from '../../types/server-cleaners'
import { encrypt } from '../../util/crypto/crypto'
import { utf8 } from '../../util/encoding'

import { applyKits } from './login'
import { hashUsername } from './login-selectors'

import { makePasswordKit } from './password'
import { makeChangePin2IdKit } from './pin2'
import { makeChangeRecovery2IdKit } from './recovery2'

export async function changeUsername(
  ai,
  accountId,
  opts
) {
  const { loginTree } = ai.props.state.accounts[accountId]

  function makeKits(login) {
    const out = [makeChangeUsernameKit(ai, login, opts)]
    for (const child of login.children) {
      out.push(...makeKits(child))
    }
    return out
  }

  const kits = await Promise.all(makeKits(loginTree))
  await applyKits(ai, loginTree, kits)
}

/**
 * Figures out which changes are needed to change a username,
 * and combines the necessary kits.
 */
export async function makeChangeUsernameKit(
  ai,
  login,
  opts
) {
  const { password, username } = opts
  const { isRoot, loginId, passwordAuth } = login

  // Validate our input:
  if (passwordAuth != null && password == null) {
    throw new Error('A password is required to change the username')
  }

  const pin2Kit = makeChangePin2IdKit(login, username)
  const recovery2Kit = makeChangeRecovery2IdKit(login, username)
  let passwordKit
  let usernameKit

  if (password != null) {
    passwordKit = await makePasswordKit(ai, login, username, password)
  }
  if (isRoot) {
    usernameKit = await makeUsernameKit(ai, login, username)
  }

  // Stop if we have no changes:
  if (
    passwordKit == null &&
    pin2Kit == null &&
    recovery2Kit == null &&
    usernameKit == null
  ) {
    return
  }

  return {
    loginId,
    server: {
      ..._optionalChain([passwordKit, 'optionalAccess', _ => _.server]),
      ..._optionalChain([pin2Kit, 'optionalAccess', _2 => _2.server]),
      ..._optionalChain([recovery2Kit, 'optionalAccess', _3 => _3.server]),
      ..._optionalChain([usernameKit, 'optionalAccess', _4 => _4.server])
    },
    serverPath: '/v2/login/username',
    stash: {
      ..._optionalChain([passwordKit, 'optionalAccess', _5 => _5.stash]),
      ..._optionalChain([pin2Kit, 'optionalAccess', _6 => _6.stash]),
      ..._optionalChain([recovery2Kit, 'optionalAccess', _7 => _7.stash]),
      ..._optionalChain([usernameKit, 'optionalAccess', _8 => _8.stash])
    }
  }
}

/**
 * Creates the values needed to set up a username.
 * This is only useful for root logins.
 */
export async function makeUsernameKit(
  ai,
  login,
  username
) {
  const { io } = ai.props
  const { loginId, loginKey } = login

  const userId = await hashUsername(ai, username)

  return {
    loginId,
    server: wasChangeUsernamePayload({
      userId,
      userTextBox: encrypt(io, utf8.parse(username), loginKey)
    }),
    serverPath: '',
    stash: { userId, username }
  }
}
