/**************************************************************************************************
*
* ADOBE SYSTEMS INCORPORATED
* Copyright 2019 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the
* terms of the Adobe license agreement accompanying it.  If you have received this file from a
* source other than Adobe, then your use, modification, or distribution of it requires the prior
* written permission of Adobe.
*
**************************************************************************************************/

const fs = require('fs-extra');
const path = require('path');
const node_ssl = fs.existsSync('./build/Debug/node_ssl.node') ? require('./build/Debug/node_ssl.node') : require('./build/Release/node_ssl.node');
const { promisify } = require('util');
const { execSync } = require('child_process');

const _getCertData = promisify(node_ssl.getCertData);

/**
 * This function indicates whether a self signed identity with the name specified by identityName
 * is present at the location specified by identityLocation.
 * @param {string} identityLocation location of the identity.
 * @param {string} identityName name of the identity.
 * @returns {promise} which resolves to an object containing the location of key, certificate and DER-encoded
 * certificate files, on success; otherwise rejects. It can also be rejected in case there is any
 * problem with the passed arguments.
 */
module.exports.isSelfSignedIdentityPresent = function (identityLocation, identityName) {
    if (arguments.length !== 2) {
        return Promise.reject(new TypeError('Wrong number of arguments provided'));
    }
    if (typeof identityLocation !== 'string' || typeof identityName !== 'string') {
        return Promise.reject(new TypeError('Wrong type of arguments provided'));
    }

    return Promise.all([
        fs.pathExists(path.join(identityLocation, `${identityName}.crt`)),
        fs.pathExists(path.join(identityLocation, `${identityName}.dcrt`)),
        fs.pathExists(path.join(identityLocation, `${identityName}.key`))
    ])
        .then(results => {
            const allExists = results.every(result => result);
            if (allExists) {
                return _getCertData(path.join(identityLocation, `${identityName}.crt`))
                    .then(data => {
                        return {
                            ...data,
                            key: path.join(identityLocation, `${identityName}.key`),
                            cert: path.join(identityLocation, `${identityName}.crt`),
                            dCert: path.join(identityLocation, `${identityName}.dcrt`),
                        };
                    });
            } else {
                return Promise.reject(new TypeError('one of the three files is missing'));
            }
        })
};

/**
 * This function creates a self signed identity with the name specified by identityName at location
 * specified by identityLocation indicates. It also adds a domainSuffix to the identityName as the URL
 * and adds commonName for the issuer.
 * @param {string} identityLocation location of the identity.
 * @param {string} identityName name of the identity.
 * @param {string} domainSuffix suffix to be added to the identityName to use as URL for the identity.
 * @param {string} commonName common name to be added to the generated identity.
 * @param {string} serialNumber serial number to be used for the generated identity.
 * @param {string} validityInDays validity of the certificate in days.
 * @returns {promise} which resolves to an object containing the location of key, certificate and DER-encoded
 * certificate files, on success; otherwise it rejects. It can also be rejected in case there is any
 * problem with the passed arguments.
 */
// eslint-disable-next-line no-unused-vars
module.exports.createSelfSignedIdentity = function (identityLocation, identityName, domainSuffix, commonName, version, validityInDays) {
    return new Promise((resolve, reject) => {
        node_ssl.createSelfSignedIdentity(...arguments, (err, value) => {
            if (!value) { err = new Error('Failed to create Self Signed Identity!'); }
            if (err) { return reject(err); }
            const data = _getCertData(path.join(identityLocation, `${identityName}.crt`))
                .then(data => {
                    return {
                        ...data,
                        key: path.join(identityLocation, `${identityName}.key`),
                        cert: path.join(identityLocation, `${identityName}.crt`),
                        dCert: path.join(identityLocation, `${identityName}.dcrt`)
                    }
                });
            resolve(data);
        });
    });
};

/**
 * This function indicates whether the certificate specified by the certificateLocation is trusted or not.
 * @param {string} certificateLocation location of the DER-encoded certificate.
 * @returns {promise} which resolves in case certificate is trusted; otherwise rejects.
 * It can also be rejected in case there is any problem with the passed arguments.
 */
module.exports.isCertificateTrusted = promisify(node_ssl.isCertificateTrusted);

/**
 * This function adds the certificate specified by the certificateLocation to user's trust store.
 * @param {string} certificateLocation location of the DER-encoded certificate.
 * @param {string} label label to be used while adding to the trust store.
 * @returns {promise} which resolves in case certificate is added to the trust store; otherwise rejects.
 * It can also be rejected in case there is any problem with the passed arguments.
 */
if (process.platform === 'win32') {
    module.exports.addCertificateToTrustStore = promisify(node_ssl.addCertificateToTrustStore);
} else if (process.platform === 'darwin') {
    const wrapper = (path, label, cb) => {
        try {
            execSync(`security delete-certificate -c "${label}"`);
        } catch(err) {
            //ignore
        }
        node_ssl.addCertificateToTrustStore(path, label, cb);
    };
    module.exports.addCertificateToTrustStore = promisify(wrapper);
}

/**
 * This function removes the certificate specified by the certificateLocation from user's trust store.
 * @param {string} certificateLocation location of the DER-encoded certificate.
 * @returns {promise} which resolves in case certificate is removed from the trust store; otherwise rejects.
 * It can also be rejected in case there is any problem with the passed arguments.
 */
module.exports.removeCertificateFromTrustStore = promisify(node_ssl.removeCertificateFromTrustStore);

/**
 * This functions shows a native dialog box to the user and indicates back whether user selected the first option or not.
 * @param {string} title The title of the dialog box.
 * @param {string} message The message of the dialog box.
 * @param {string} firstButtonLabel The label to be used for first Button.
 * @param {string} secondButtonLabel The label to be used for second Button.
 * @returns {promise} which resolves in case user clicked first Button/pressed Enter (Return) key; otherise rejects.
 * It can also be rejected in case there is any problem with the passed arguments.
 */
module.exports.showDialog = promisify(node_ssl.showDialog);
