import { AbstractPrivateKeyring } from '../keyring/private';
import { base64 } from 'rfc4648';
import { AbstractSelfAccountKeyring } from '../keyring/account_base';
import { SelfAccountKeyring } from '../keyring/account_self';
import { PeerAccountKeyring } from '../keyring/account_peer';
import { LOGIN_CHALLENGE_CRYPTO_CONTEXT } from '../context/login_challenge/__init__';
import { LoginChallengeConfigLoadArgs } from '../context/login_challenge/args';

/**
 * The private keyring of the account is usually created for authenticating with the server and decrypting
    the master-key for bootstrapping the account keyring.
    It is also used during registration, when preparing and at the login-challenge during the email-verification.
 */
export function create_account_login_private_keyring(
  private_kr_cls: typeof AbstractPrivateKeyring,
  password: string,
  salt_from_server: Uint8Array
): Promise<AbstractPrivateKeyring> {
  return private_kr_cls.load(new TextEncoder().encode(password), salt_from_server);
}

/**
 * To create the login request the client must already have loaded their private keyring using the salt
    acquired from the server in exchange for the email.

    These may be submitted to the "login" endpoint with additional arguments like "nano" to select the scope
    created from the new session. (default is "user")
 */
export type create_account_login_request_params_result = {
  email: string;
  authentication_key: string;
  session_enc_key: string;
  nano?: boolean;
};

export function create_account_login_request_params(
  private_kr: AbstractPrivateKeyring,
  email: string,
  session_enc_key: Uint8Array,
  nano: boolean = false
): create_account_login_request_params_result {
  let r = {
    email,
    authentication_key: base64.stringify(private_kr.auth_key),
    session_enc_key: base64.stringify(session_enc_key),
  };
  if (nano) {
    r['nano'] = true;
  }
  return r;
}

/**
 * Load the account's keyring.
 */
export function create_account_login_keyring<T extends typeof AbstractSelfAccountKeyring>(
  self_kr_cls: T,
  private_kr: AbstractPrivateKeyring,
  account_id: string,
  encrypted_master_key: Uint8Array,
  encrypted_encrypting_secret_ec: Uint8Array,
  encrypted_signing_secret_ec: Uint8Array
): Promise<AbstractSelfAccountKeyring> {
  return self_kr_cls.load(
    private_kr,
    account_id,
    encrypted_master_key,
    encrypted_encrypting_secret_ec,
    encrypted_signing_secret_ec
  );
}

export function create_account_login_keyring_self(
  private_kr: AbstractPrivateKeyring,
  account_id: string,
  encrypted_master_key: Uint8Array,
  encrypted_encrypting_secret_ec: Uint8Array,
  encrypted_signing_secret_ec: Uint8Array
): Promise<SelfAccountKeyring> {
  return SelfAccountKeyring.load(
    private_kr,
    account_id,
    encrypted_master_key,
    encrypted_encrypting_secret_ec,
    encrypted_signing_secret_ec
  );
}

/**
 * Load the encrypted session key using the just loaded account keyring.
 */
export function create_account_login_session(
  self_kr: AbstractSelfAccountKeyring,
  encrypted_session_key: Uint8Array,
  session_public_key: Uint8Array = undefined, // for decrypting the session key; for legacy api compatibility
  session_pks: Object = undefined // prefer using this to session_public_key
): Promise<Uint8Array> {
  let peer_kr;
  if (session_pks) {
    peer_kr = PeerAccountKeyring.load_anonymous_keys(session_pks);
  } else {
    peer_kr = PeerAccountKeyring.load_anonymous(session_public_key);
  }
  return LOGIN_CHALLENGE_CRYPTO_CONTEXT.load(
    new LoginChallengeConfigLoadArgs(encrypted_session_key, self_kr, peer_kr)
  );
}
