import { TOTP } from 'otpauth'
import { HttpStatusCode, TwoFactorEnableResult } from '@shared/models'
import { unwrapBody } from '../requests'
import { AbstractCommand, OverrideCommandOptions } from '../shared'

export class TwoFactorCommand extends AbstractCommand {

  static buildOTP (options: {
    secret: string
  }) {
    const { secret } = options

    return new TOTP({
      issuer: 'PeerTube',
      algorithm: 'SHA1',
      digits: 6,
      period: 30,
      secret
    })
  }

  request (options: OverrideCommandOptions & {
    userId: number
    currentPassword?: string
  }) {
    const { currentPassword, userId } = options

    const path = '/api/v1/users/' + userId + '/two-factor/request'

    return unwrapBody<TwoFactorEnableResult>(this.postBodyRequest({
      ...options,

      path,
      fields: { currentPassword },
      implicitToken: true,
      defaultExpectedStatus: HttpStatusCode.OK_200
    }))
  }

  confirmRequest (options: OverrideCommandOptions & {
    userId: number
    requestToken: string
    otpToken: string
  }) {
    const { userId, requestToken, otpToken } = options

    const path = '/api/v1/users/' + userId + '/two-factor/confirm-request'

    return this.postBodyRequest({
      ...options,

      path,
      fields: { requestToken, otpToken },
      implicitToken: true,
      defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
    })
  }

  disable (options: OverrideCommandOptions & {
    userId: number
    currentPassword?: string
  }) {
    const { userId, currentPassword } = options
    const path = '/api/v1/users/' + userId + '/two-factor/disable'

    return this.postBodyRequest({
      ...options,

      path,
      fields: { currentPassword },
      implicitToken: true,
      defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
    })
  }

  async requestAndConfirm (options: OverrideCommandOptions & {
    userId: number
    currentPassword?: string
  }) {
    const { userId, currentPassword } = options

    const { otpRequest } = await this.request({ userId, currentPassword })

    await this.confirmRequest({
      userId,
      requestToken: otpRequest.requestToken,
      otpToken: TwoFactorCommand.buildOTP({ secret: otpRequest.secret }).generate()
    })

    return otpRequest
  }
}
