import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { c } from 'ttag';
import {
    useApi,
    useAuthentication,
    useNotifications,
    useEventManager,
    useLoading,
    FormModal,
    Alert,
    Table,
    TableHeader,
    TableBody,
    TableRow
} from 'react-components';
import { DEFAULT_ENCRYPTION_CONFIG, ENCRYPTION_CONFIGS } from 'proton-shared/lib/constants';
import { generateMemberAddressKey } from 'proton-shared/lib/keys/organizationKeys';
import { decryptMemberToken } from 'proton-shared/lib/keys/memberToken';
import { decryptPrivateKeyArmored, generateAddressKey } from 'proton-shared/lib/keys/keys';

import SelectEncryption from '../keys/addKey/SelectEncryption';
import MissingKeysStatus, { STATUS } from './MissingKeysStatus';
import { createMemberAddressKeys } from '../members/actionHelper';
import { createKeyHelper } from '../keys/shared/actionHelper';

const updateAddress = (oldAddresses, address, status) => {
    return oldAddresses.map((oldAddress) => {
        if (oldAddress.ID === address.ID) {
            return { ...address, status };
        }
        return oldAddress;
    });
};

const CreateMissingKeysAddressModal = ({ onClose, member, addresses, organizationKey, ...rest }) => {
    const api = useApi();
    const authentication = useAuthentication();
    const { call } = useEventManager();
    const { createNotification } = useNotifications();
    const [loading, withLoading] = useLoading();
    const doneRef = useRef(false);
    const [formattedAddresses, setFormattedAddresses] = useState(() =>
        addresses.map((address) => ({
            ...address,
            status: {
                type: STATUS.QUEUED
            }
        }))
    );

    const [encryptionType, setEncryptionType] = useState(DEFAULT_ENCRYPTION_CONFIG);

    const processMember = async () => {
        const encryptionConfig = ENCRYPTION_CONFIGS[encryptionType];

        const PrimaryKey = member.Keys.find(({ Primary }) => Primary === 1);

        if (!PrimaryKey) {
            return createNotification({ text: c('Error').t`Member keys are not set up.` });
        }

        const decryptedToken = await decryptMemberToken(PrimaryKey.Token, organizationKey);
        const primaryMemberKey = await decryptPrivateKeyArmored(PrimaryKey.PrivateKey, decryptedToken);

        await Promise.all(
            addresses.map(async (address) => {
                try {
                    setFormattedAddresses((oldState) => updateAddress(oldState, address, { type: STATUS.LOADING }));

                    await createMemberAddressKeys({
                        api,
                        Member: member,
                        Address: address,
                        keys: [], // Assume no keys exists for this address since we are in this modal.
                        ...(await generateMemberAddressKey({
                            email: address.Email,
                            primaryKey: primaryMemberKey,
                            organizationKey,
                            encryptionConfig
                        }))
                    });

                    setFormattedAddresses((oldState) => updateAddress(oldState, address, { type: STATUS.DONE }));
                } catch (e) {
                    setFormattedAddresses((oldState) =>
                        updateAddress(oldState, address, { type: STATUS.FAILURE, tooltip: e.message })
                    );
                }
            })
        );

        await call();
    };

    const processSelf = async () => {
        const encryptionConfig = ENCRYPTION_CONFIGS[encryptionType];

        await Promise.all(
            addresses.map(async (address) => {
                try {
                    setFormattedAddresses((oldState) => updateAddress(oldState, address, { type: STATUS.LOADING }));

                    const { privateKey, privateKeyArmored } = await generateAddressKey({
                        email: address.Email,
                        passphrase: authentication.getPassword(),
                        encryptionConfig
                    });

                    await createKeyHelper({
                        api,
                        privateKey,
                        privateKeyArmored,
                        Address: address,
                        keys: []
                    });

                    setFormattedAddresses((oldState) => updateAddress(oldState, address, { type: STATUS.DONE }));
                } catch (e) {
                    setFormattedAddresses((oldState) =>
                        updateAddress(oldState, address, { type: STATUS.FAILURE, tooltip: e.message })
                    );
                }
            })
        );

        await call();
    };

    const handleSubmit = () => {
        if (doneRef.current) {
            return onClose();
        }
        doneRef.current = true;

        withLoading(member.Self ? processSelf() : processMember());
    };

    return (
        <FormModal
            title={c('Title').t`Generate missing keys`}
            close={c('Action').t`Close`}
            submit={c('Action').t`Submit`}
            onClose={onClose}
            onSubmit={handleSubmit}
            loading={loading}
            {...rest}
        >
            <Alert>{c('Info')
                .t`Before you can start sending and receiving emails from your new addresses you need to create encryption keys for them.`}</Alert>
            <SelectEncryption encryptionType={encryptionType} setEncryptionType={setEncryptionType} />
            <Table>
                <TableHeader
                    cells={[c('Header for addresses table').t`Address`, c('Header for addresses table').t`Status`]}
                />
                <TableBody colSpan={2}>
                    {formattedAddresses.map((address) => (
                        <TableRow
                            key={address.ID}
                            cells={[address.Email, <MissingKeysStatus key={0} {...address.status} />]}
                        />
                    ))}
                </TableBody>
            </Table>
        </FormModal>
    );
};

CreateMissingKeysAddressModal.propTypes = {
    onClose: PropTypes.func,
    organizationKey: PropTypes.object,
    member: PropTypes.object.isRequired,
    addresses: PropTypes.array.isRequired
};

export default CreateMissingKeysAddressModal;

@@ -21,16 +21,8 @@
 tate
-, useRef
  %7D f
@@ -649,16 +649,75 @@
 s/keys';
+%0Aimport %7B noop %7D from 'proton-shared/lib/helpers/function';
 %0A%0Aimport
@@ -1545,30 +1545,41 @@
 nst 
-doneRef = useRef(false
+%5Bstep, setStep%5D = useState('init'
 );%0A 
@@ -4888,45 +4888,259 @@
 if (
-doneRef.current) %7B%0A            return
+step === 'init') %7B%0A            withLoading(%0A                (member.Self ? processSelf() : processMember())%0A                    .then(() =%3E setStep('done'))%0A                    .catch(() =%3E setStep('error'))%0A            );%0A        %7D else %7B%0A           
  onC
@@ -5165,110 +5165,254 @@
     
+%7D;%0A%0A
     
-doneRef.current = true;%0A%0A        withLoading(member.Self ? processSelf() : processMember())
+const submitText = (() =%3E %7B%0A        if (step === 'done') %7B%0A            return c('Action').t%60Done%60;%0A        %7D%0A        if (step === 'error') %7B%0A            return c('Action').t%60Close%60;%0A        %7D%0A        return c('Action').t%60Submit%60
 ;%0A    %7D
+)()
 ;%0A%0A 
@@ -5559,37 +5559,26 @@
 submit=%7B
-c('Action').t%60S
+s
 ubmit
-%60
+Text
 %7D%0A      
@@ -5906,16 +5906,32 @@
 cryption
+%0A               
  encrypt
@@ -5954,16 +5954,32 @@
 ionType%7D
+%0A               
  setEncr
@@ -5995,25 +5995,62 @@
 e=%7Bs
-etEncryptionType%7D
+tep === 'init' ? setEncryptionType : noop%7D%0A           
  /%3E%0A

Fix missing keys submit text (#515)