"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const EUSignCPCore_1 = __importDefault(require("./EUSignCPCore"));
const EndUserCertificate = require('../eusw').EndUserCertificate;
const EndUserError = require('../eusw').EndUserError;
const EndUserCertKeyType = {
    DSTU4145: 1,
    RSA: 2,
    ECDSA: 4, //  'ECDSAWithSHA'
};
const allowedKeyMediaTypes = [
    'криптомод. ІІТ Гряда-61',
    'е.ключ ІІТ Алмаз-1К',
    'е.ключ ІІТ Кристал-1',
    'ID-карта громадянина (БЕН)',
    'е.ключ ІІТ Алмаз-1К (PKCS#11)',
    'е.ключ ІІТ Кристал-1 (PKCS#11)',
    'е.ключ чи смарт-карта Avest (PKCS#11)',
    'е.ключ Ефіт Key (PKCS#11)',
    'е.ключ чи смарт-карта Автор (PKCS#11)',
    'е.ключ чи смарт-карта Автор 338 (PKCS#11)',
    'смарт-карта Техноконс. TEllipse3 (PKCS#11)',
    'смарт-карта Техноконс. TEllipse',
    'смарт-карта Техноконс. TEllipse2/3',
    'см.-карта чи е.ключ Техноконс. TEllipse2/3/4',
];
const allowedKeyMediaDevices = null;
class EUSignCPFrontend extends EUSignCPCore_1.default {
    constructor() {
        super({
            CAServers: '/md-accreditation/CAs.json?v20230201',
            CACert: '/md-accreditation/CACertificates.p7b?v20230201',
        });
        this.kmActiveOperation = false;
        this.m_kmTypes = null;
        this.m_KMs = null;
        this.signAlgos = [];
        this.m_pkContext = null;
    }
    BeginKMOperation() {
        const freeze = 10;
        return new Promise((resolve) => {
            function begin(kmActiveOperation) {
                if (kmActiveOperation) {
                    setTimeout(() => {
                        begin(kmActiveOperation);
                    }, freeze);
                }
                else {
                    kmActiveOperation = true;
                    resolve();
                }
            }
            begin(this.kmActiveOperation);
        });
    }
    EndKMOperation() {
        this.kmActiveOperation = false;
    }
    GetKeyMedias() {
        let typeList = [];
        let allowedDeviceList = null;
        if (allowedKeyMediaDevices) {
            allowedDeviceList = {};
            allowedKeyMediaDevices.forEach((item) => {
                allowedDeviceList[item.type] = item.devices;
            });
        }
        return new Promise((resolve, reject) => {
            this.BeginKMOperation()
                .then(() => (this.m_kmTypes ? this.m_kmTypes : this.GetKeyMediaTypes()))
                .then((c) => {
                this.m_kmTypes = c;
                const aKMT = allowedKeyMediaTypes;
                typeList = [];
                for (let i = 0; i < c.length; i++) {
                    const element = c[i];
                    if (!aKMT || aKMT.indexOf(element) >= 0) {
                        typeList.push(i);
                    }
                }
                return this.GetKeyMediaDevices(typeList);
            })
                .then((deviceList) => {
                const kmList = [];
                for (let i = 0; i < deviceList.length; i++) {
                    const typeIndex = typeList[i];
                    // @ts-ignore
                    const type = this.m_kmTypes[typeIndex];
                    const typeDeviceList = deviceList[i];
                    const allowedArr = allowedDeviceList && allowedDeviceList[type] ? allowedDeviceList[type] : null;
                    for (let j = 0; j < typeDeviceList.length; j++) {
                        if (!allowedArr || allowedArr.indexOf(typeDeviceList[j]) !== -1) {
                            const keyMedia = this.CreateKeyMedia();
                            keyMedia.typeIndex = typeIndex;
                            keyMedia.devIndex = j;
                            keyMedia.password = null;
                            keyMedia.type = type;
                            keyMedia.device = typeDeviceList[j];
                            keyMedia.visibleName = `${keyMedia.device}(${keyMedia.type})`;
                            kmList.push(keyMedia);
                        }
                    }
                }
                this.EndKMOperation();
                resolve(kmList);
            })
                .catch((error) => {
                this.EndKMOperation();
                reject(error);
            });
        });
    }
    MapError(error) {
        var simpleError = new EndUserError();
        if (error.GetErrorCode !== undefined && error.GetMessage !== undefined) {
            simpleError.code = error.GetErrorCode();
            simpleError.message = error.GetMessage();
        }
        else if (error.code != undefined && error.message != undefined) {
            simpleError.code = error.code;
            simpleError.message = error.message;
        }
        else {
            simpleError.code = this.euSign.EU_ERROR_UNKNOWN;
            simpleError.message = error.toString();
        }
        return simpleError;
    }
    ProcessArray(array) {
        return new Promise((resolve, reject) => {
            const queque = [];
            let index = 0;
            function n() {
                if (index >= array.length) {
                    resolve(queque);
                }
                else {
                    array[index]
                        .then((c) => {
                        queque.push(c);
                        n();
                    })
                        .catch(reject);
                }
                index++;
            }
            n();
        });
    }
    IsCMPCompatible(CA, compatibility) {
        return !!CA.cmpAddress && (void 0 === CA.cmpCompatibility || (CA.cmpCompatibility & compatibility) == compatibility);
    }
    SetSettings(IssuerCN) {
        const CAs = this.m_CAs;
        const caSetting = this.GetCASettings(IssuerCN);
        const firstCA = CAs && CAs.length > 0 ? CAs[0] : null;
        const emptyCASetting = caSetting === null || caSetting.address === '';
        return new Promise((resolve, reject) => {
            if (IssuerCN && caSetting === null) {
                throw this.euSign.MakeError(EndUserError.ERROR_WRITE_SETTINGS, '');
            }
            const selectedCA = caSetting && caSetting.tspAddress !== '' ? caSetting : firstCA;
            // @ts-ignore
            const tspSettings = this.euSign.CreateTSPSettings();
            tspSettings.SetGetStamps(!0);
            tspSettings.SetAddress(selectedCA ? selectedCA.tspAddress : '');
            tspSettings.SetPort(selectedCA ? selectedCA.tspAddressPort : '80');
            return this.SetTSPSettings(tspSettings)
                .then(() => {
                const hasSettings = !emptyCASetting && caSetting.ocspAccessPointAddress !== '';
                // @ts-ignore
                const ocspSettings = this.euSign.CreateOCSPSettings();
                ocspSettings.SetUseOCSP(hasSettings);
                if (hasSettings) {
                    ocspSettings.SetBeforeStore(!0);
                    ocspSettings.SetAddress(caSetting.ocspAccessPointAddress);
                    ocspSettings.SetPort('80');
                }
                return this.SetOCSPSettings(ocspSettings);
            })
                .then(() => {
                const hasSettings = !emptyCASetting && caSetting.cmpAddress !== '';
                // @ts-ignore
                const cmpSettings = this.euSign.CreateCMPSettings();
                cmpSettings.SetUseCMP(hasSettings);
                if (hasSettings) {
                    cmpSettings.SetAddress(caSetting.cmpAddress);
                    cmpSettings.SetPort('80');
                }
                return this.SetCMPSettings(cmpSettings);
            })
                .then(resolve)
                .catch(reject);
        });
    }
    ReadPrivateKey(keyMedia, certs, CACommonName) {
        return new Promise((resolve, reject) => {
            this.BeginKMOperation()
                .then(() => this.ReadPrivateKeyInternal(null, null, keyMedia, certs, CACommonName))
                .then((pkContext) => {
                this.EndKMOperation();
                resolve(pkContext);
            })
                .catch((error) => {
                this.EndKMOperation();
                reject(error);
            });
        });
    }
    ReadPrivateKeyBinary(privateKey, password, certs, CACommonName) {
        return this.ReadPrivateKeyInternal(privateKey, password, null, certs, CACommonName);
    }
    ReadPrivateKeyInternal(privateKey, password, keyMedia, certs, CACommonName) {
        //GetOwnerInfo
        return new Promise((resolve, reject) => {
            this.ResetPrivateKey()
                .then(() => this.CtxReadPrivateKeyInternal(null, privateKey, password, keyMedia, certs, CACommonName))
                .then((pkContext) => resolve(pkContext))
                .catch((error) => reject(error));
        });
    }
    SaveCertificatesInternal(certs) {
        return new Promise((resolve, reject) => {
            if (certs) {
                if (Array.isArray(certs)) {
                    this.ProcessArray(certs.map((item) => this.SaveCertificate(item)))
                        .then(() => resolve())
                        .catch((error) => reject(error));
                }
                else {
                    this.SaveCertificates(certs)
                        .then(() => resolve())
                        .catch((error) => reject(error));
                }
            }
            else {
                resolve();
            }
        });
    }
    CtxReadPrivateKeyInternal(context, privateKey, password, keyMedia, certs, CACommonName) {
        return new Promise((resolve, reject) => {
            this.SaveCertificatesInternal(certs)
                .then(() => this.SetSettings(CACommonName))
                .then(() => {
                if (privateKey !== null && password !== null) {
                    return this.CtxReadPrivateKeyBinary(privateKey, password);
                }
                return this.CtxReadPrivateKey(keyMedia);
            })
                /* .then((pkContext) => {
                    this.m_pkContext = pkContext;
                    const ownerInfo = pkContext.GetOwnerInfo();
                    if (!CACommonName) {
                        return this.SetSettings(ownerInfo.GetIssuerCN());
                    }
                    const caSettings = this.GetCASettings(ownerInfo.GetIssuerCN());
                    if (!caSettings || -1 == caSettings.issuerCNs.indexOf(CACommonName)) {
                        throw this.euSign.MakeError(EndUserError.ERROR_CERT_NOT_FOUND, '');
                    }
                }) */
                .then((pkContext) => {
                this.m_pkContext = pkContext;
                return this.GetPrivateKeyOwnerInfo();
            })
                .then((ownerInfo) => {
                if (!CACommonName) {
                    return this.SetSettings(ownerInfo.GetIssuerCN());
                }
                const caSettings = this.GetCASettings(ownerInfo.GetIssuerCN());
                if (!caSettings || -1 == caSettings.issuerCNs.indexOf(CACommonName)) {
                    throw this.euSign.MakeError(EndUserError.ERROR_CERT_NOT_FOUND, '');
                }
            })
                .then(() => resolve(this.m_pkContext))
                .catch((error) => {
                if (this.m_pkContext == null) {
                    if (this.MapError(error).code !== EndUserError.ERROR_CERT_NOT_FOUND) {
                        reject(error);
                    }
                    else {
                        this.SearchPrivateKeyCertificatesWithCMP(privateKey, password, keyMedia)
                            .then((result) => this.CtxReadPrivateKeyInternal(context, privateKey, password, keyMedia, result.certs, result.CACommonName))
                            .then((pkContext) => resolve(pkContext))
                            .catch((err) => reject(err));
                    }
                }
                else {
                    this.ResetPrivateKey()
                        .then(() => reject(error))
                        .catch(() => reject(error));
                }
            });
        });
    }
    SearchPrivateKeyCertificatesWithCMP(privateKey, password, keyMedia, selectedIssuerCN) {
        const caServer = this.m_CAs;
        const EU_CMP_COMPATIBILITY_DOWNLOAD_EU_CERTS = 1;
        const EU_CMP_COMPATIBILITY_SEARCH_EU_CERTS = 2;
        return new Promise((resolve, reject) => {
            (privateKey !== null && password !== null ? this.GetKeyInfoBinary(privateKey, password) : this.GetKeyInfo(keyMedia))
                .then((keyInfo) => {
                const a = (index, error) => {
                    if (index > caServer.length) {
                        reject(this.MapError(this.euSign.MakeError(EndUserError.ERROR_CERT_NOT_FOUND, '')));
                        return;
                    }
                    const cmpCompatibility = selectedIssuerCN ? EU_CMP_COMPATIBILITY_DOWNLOAD_EU_CERTS : EU_CMP_COMPATIBILITY_SEARCH_EU_CERTS;
                    if (this.IsCMPCompatible(caServer[index], cmpCompatibility)) {
                        if (selectedIssuerCN && caServer[index].issuerCNs.indexOf(selectedIssuerCN) === -1) {
                            a(index + 1, error);
                        }
                        else {
                            this.GetCertificatesByKeyInfo(keyInfo, [caServer[index].cmpAddress], ['80'])
                                .then((certificates) => {
                                resolve({
                                    certs: certificates,
                                    CACommonName: caServer[index].issuerCNs[0],
                                });
                            })
                                .catch((error) => {
                                const errorCode = this.MapError(error).code;
                                if (errorCode !== EndUserError.ERROR_CERT_NOT_FOUND && errorCode !== EndUserError.ERROR_TRANSMIT_REQUEST) {
                                    reject(error);
                                }
                                else {
                                    a(index + 1, error);
                                }
                            });
                        }
                    }
                    else {
                        a(index + 1, error);
                    }
                };
                a(0, null);
            })
                .catch((error) => reject(this.MapError(error)));
        });
    }
    GetCASettings(IssuerCN) {
        if (!IssuerCN) {
            return null;
        }
        const CAs = this.m_CAs;
        for (let a = 0; a < CAs.length; a++) {
            const issuerCNs = CAs[a].issuerCNs;
            for (let i = 0; i < issuerCNs.length; i++) {
                if (issuerCNs[i] === IssuerCN) {
                    return CAs[a];
                }
            }
        }
        return null;
    }
    GetOwnCertificates() {
        const certArray = [];
        return new Promise((resolve, reject) => {
            const findCert = (index) => {
                const userCertificate = {};
                this.EnumOwnCertificates(index)
                    .then((certificate) => {
                    if (certificate) {
                        userCertificate.infoEx = certificate;
                        this.GetCertificate(certificate.GetIssuer(), certificate.GetSerial())
                            .then((certData) => {
                            userCertificate.data = certData;
                            certArray.push(userCertificate);
                            findCert(index + 1);
                        })
                            .catch(reject);
                    }
                    else {
                        resolve(certArray);
                    }
                })
                    .catch(reject);
            };
            findCert(0);
        });
    }
    GetJKSPrivateKey(container, index) {
        return new Promise((resolve, reject) => {
            const cert = {
                alias: null,
                info: null,
            };
            // @ts-ignore
            this.euSign
                .EnumJKSPrivateKeys(container, index)
                // eslint-disable-next-line consistent-return
                .then((alias) => {
                if (alias !== null) {
                    cert.alias = alias;
                    // @ts-ignore
                    return this.euSign.GetJKSPrivateKey(container, alias);
                }
                resolve(null);
            })
                .then((info) => {
                cert.info = info;
                const arr = [];
                for (let i = 0; i < info.GetCertificatesCount(); i++) {
                    const certificate = info.GetCertificate(i);
                    // @ts-ignore
                    arr.push(this.euSign.ParseCertificateEx(certificate));
                }
                return this.ProcessArray(arr);
            })
                .then((certificateInfoExList) => {
                const privateKey = {};
                privateKey.alias = cert.alias;
                privateKey.privateKey = cert.info.GetPrivateKey();
                privateKey.certificates = [];
                privateKey.digitalStamp = false;
                for (let i = 0; i < certificateInfoExList.length; i++) {
                    const certificateInfoEx = certificateInfoExList[i];
                    // @ts-ignore
                    if (certificateInfoEx.GetSubjType() === this.euSign.EU_SUBJECT_TYPE_END_USER) {
                        // @ts-ignore
                        if (certificateInfoEx.GetPublicKeyType() === this.euSign.EU_CERT_KEY_TYPE_DSTU4145 &&
                            // eslint-disable-next-line no-bitwise
                            // @ts-ignore
                            (certificateInfoEx.GetKeyUsageType() & this.euSign.EU_KEY_USAGE_DIGITAL_SIGNATURE) === this.euSign.EU_KEY_USAGE_DIGITAL_SIGNATURE) {
                            // @ts-ignore
                            privateKey.digitalStamp = certificateInfoEx.GetExtKeyUsages().indexOf(this.euSign.EU_UA_OID_EXT_KEY_USAGE_STAMP) > -1;
                        }
                        // @ts-ignore
                        const userCertificate = new EndUserCertificate();
                        userCertificate.SetData(cert.info.GetCertificate(i));
                        userCertificate.SetInfoEx(certificateInfoEx);
                        privateKey.certificates.push(userCertificate);
                    }
                }
                privateKey.digitalStamp = false;
                resolve(privateKey);
            })
                .catch((error) => reject(error));
        });
    }
    GetJKSPrivateKeys(privKey) {
        return new Promise((resolve, reject) => {
            const arr = [];
            const func1 = (index) => {
                this.GetJKSPrivateKey(privKey, index)
                    .then((key) => {
                    if (key) {
                        arr.push(key);
                        func1(index + 1);
                    }
                    else {
                        resolve(arr);
                    }
                })
                    .catch((error) => reject(error));
            };
            func1(0);
        });
    }
    GetSupportedSignAlgos(ownCertificates) {
        for (let i = 0; i < ownCertificates.length; i++) {
            const infoEx = ownCertificates[i].infoEx;
            switch (infoEx.publicKeyType) {
                case EndUserCertKeyType.DSTU4145:
                    this.signAlgos.push(EndUserCertKeyType.DSTU4145);
                    break;
                case EndUserCertKeyType.RSA:
                    this.signAlgos.push(EndUserCertKeyType.RSA);
                    break;
                case EndUserCertKeyType.ECDSA:
                    this.signAlgos.push(EndUserCertKeyType.ECDSA);
                    break;
                default:
                    break;
            }
        }
        // return signAlgos;
    }
}
exports.default = EUSignCPFrontend;
