import {
  asArray,
  asCodec,
  asDate,
  asNumber,
  asObject,
  asOptional,
  asString,

  uncleaner
} from 'cleaners'
import { justFiles } from 'disklet'
import { base64 } from 'rfc4648'

import { fixUsername } from '../../client-side'
import {
  asBase32,
  asBase64,
  asEdgeBox,
  asEdgeKeyBox,
  asEdgePendingVoucher,
  asEdgeSnrp
} from '../../types/server-cleaners'


import { verifyData } from '../../util/crypto/verify'
import { base58 } from '../../util/encoding'


/**
 * The login data we store on disk.
 */











































/**
 * Reads all login stashes from disk.
 */
export async function loadStashes(
  disklet,
  log
) {
  const out = []
  const paths = await disklet.list('logins').then(justFiles)
  for (const path of paths) {
    try {
      out.push(asLoginStash(JSON.parse(await disklet.getText(path))))
    } catch (error) {
      log.error(`Could not load ${path}: ${String(error)}`)
    }
  }
  return out
}

/**
 * Removes any login stash that may be stored for the given username.
 */
export async function removeStash(
  ai,
  loginId
) {
  const { dispatch, io } = ai.props

  const paths = await io.disklet.list('logins').then(justFiles)
  for (const path of paths) {
    try {
      const stash = asLoginStash(JSON.parse(await io.disklet.getText(path)))
      if (verifyData(stash.loginId, loginId)) await io.disklet.delete(path)
    } catch (error) {}
  }

  dispatch({
    type: 'LOGIN_STASH_DELETED',
    payload: loginId
  })
}

/**
 * Saves a login stash tree to disk.
 */
export async function saveStash(
  ai,
  stashTree
) {
  const { dispatch, io } = ai.props
  const { appId, loginId } = stashTree

  if (appId !== '') {
    throw new Error('Cannot save a login without an appId.')
  }
  if (loginId == null || loginId.length !== 32) {
    throw new Error('Invalid loginId')
  }
  await io.disklet.setText(
    `logins/${base58.stringify(loginId)}.json`,
    JSON.stringify(wasLoginStash(stashTree))
  )

  dispatch({ type: 'LOGIN_STASH_SAVED', payload: stashTree })
}

const asUsername = raw => fixUsername(asString(raw))

export const asLoginStash = asObject({
  // Identity:
  appId: asString,
  created: asOptional(asDate),
  lastLogin: asOptional(asDate),
  loginId: asBase64,

  // 2-factor:
  otpKey: asOptional(asBase32),
  otpResetDate: asOptional(asDate),
  otpTimeout: asOptional(asNumber),
  pendingVouchers: asOptional(asArray(asEdgePendingVoucher), () => []),
  voucherId: asOptional(asString),
  voucherAuth: asOptional(asBase64),

  // Return logins:
  loginAuthBox: asOptional(asEdgeBox),
  parentBox: asOptional(asEdgeBox),

  // Password login:
  passwordAuthBox: asOptional(asEdgeBox),
  passwordAuthSnrp: asOptional(asEdgeSnrp),
  passwordBox: asOptional(asEdgeBox),
  passwordKeySnrp: asOptional(asEdgeSnrp),

  // PIN v2 login:
  pin2Key: asOptional(
    asCodec(
      // Legacy Airbitz can wrongly send this in base58 for Edge login:
      raw => {
        const clean = asString(raw)
        return raw.slice(-1) !== '=' ? base58.parse(clean) : base64.parse(clean)
      },
      clean => base64.stringify(clean)
    )
  ),
  pin2TextBox: asOptional(asEdgeBox),

  // Recovery v2 login:
  recovery2Key: asOptional(asBase64),

  // Username:
  userId: asOptional(asBase64),
  username: asOptional(asUsername),

  // Keys and assorted goodies:
  children: asOptional(asArray(raw => asLoginStash(raw))),
  keyBoxes: asOptional(asArray(asEdgeKeyBox)),
  mnemonicBox: asOptional(asEdgeBox),
  rootKeyBox: asOptional(asEdgeBox),
  syncKeyBox: asOptional(asEdgeBox)
})

export const wasLoginStash = uncleaner(asLoginStash)
