(function (){
    window.CDocSign = getDocSign();

    function getDocSign () {
        function DocsSign () {
            // this.deal = deal;

            // this.didoxToken = '';
            // this.auth = '';
            // this.pkcs7 = '';
            // this.authId = '';
            this.userSNumber = '';
            this.vo = {};
            this.keyId = '';

            this.DIDOX = {
                token: '',
                authId: '',
                signature: ''
            }

            this.EIMZO = {
                keyId: ''
            }
        
            // is current E-IMZO verison
            this._isCVersion = false;

            this.errorCAPIWS = 'Ошибка соединения с E-IMZO. Возможно у вас не установлен модуль E-IMZO или Браузер E-IMZO.';
            this.errorBrowserWS = 'Браузер не поддерживает технологию WebSocket. Установите последнюю версию браузера.';
            this.errorUpdateApp = 'ВНИМАНИЕ !!! Установите новую версию приложения E-IMZO или Браузера E-IMZO.<br /><a href="https://e-imzo.uz/main/downloads/" role="button">Скачать ПО E-IMZO</a>';
            this.errorWrongPassword = 'Пароль неверный.';
            this.errorTaxId = 'У сделки нет обязательного поля (ИНН).';
            this.errorPDF = 'Не найден pdf файл.';
            this.errorTaxIdInfalid = 'У сделки нет обязательного поля (ИНН).';

            // token working time in hours
            this.TOKEM_LIMOT = 24 - 1;
            // pkcs7 working time in hours 
            this.DAUTH_ID_LIMIT = 6 - 1;
            // plcs7 signature time limit in hours
            this.SIGNATURE_LIMIT = 6 - 1;

            // didox auth id storage key 
            this.SAUTH_KEY = '__d.authid';
            // didox token storage key
            this.SDTOKEN_KEY = '__d.token';
            //
            this.SSIGNATURE_KEY = '__d.signature';

            // api keys
            this.EIMZO_API_KEYS = [
                'localhost', '96D0C1491615C82B9A54D9989779DF825B690748224C2B04F500F370D51827CE2644D8D4A82C18184D73AB8530BB8ED537269603F61DB0D03D2104ABF789970B',
                '127.0.0.1', 'A7BCFA5D490B351BE0754130DF03A068F855DB4333D43921125B9CF2670EF6A40370C646B90401955E1F7BC9CDBF59CE0B2C5467D820BE189C845D0B79CFC96F',
                'null', 'E0A205EC4E7B78BBB56AFF83A733A1BB9FD39D562E67978CC5E7D73B0951DB1954595A20672A63332535E13CC6EC1E1FC8857BB09E0855D7E76E411B6FA16E9D',
                'dls.yt.uz', 'EDC1D4AB5B02066FB3FEB9382DE6A7F8CBD095E46474B07568BC44C8DAE27B3893E75B79280EA82A38AD42D10EA0D600E6CE7E89D1629221E4363E2D78650516'
            ];

            // this.buyerInfo = {
            //     address: ['bx', 'didox'],
            //     name: ['bx', 'didox'],
            //     tin: ['bx', 'didox'],
            // };

            // 
            this.checkVersion();
        }
        
        DocsSign.prototype = {

            /**
             * main
             * @param {Function} success 
             * @param {Function} error
             */
            sign: function (success) {
                var self = this;

                if (!success) {
                    success = function () {}
                }

                success.bind(self);

                if (this._isCVersion) {
                    // show select
                    self._uiShowApiKeys(function (itm, vo) {
                        // on confirm
                        self.ckeckTokens(vo, function (token) {
                            Didox.setUserKey(token);
                            success(vo, token);
                        });
                    });
                } else {
                    // version error
                    self._uiShowMessage(self.errorCAPIWS);
                }
            },

            createDoc: function (doc, success) {
                var self = this,
                    pdfUrl = doc.pdfUrlMachine,
                    taxId = this.getTaxId(),
                    buyer = this.getBuyer();

                if (!taxId) {
                    this._uiShowMessage(this.errorTaxId);
                    return;
                }

                if (!pdfUrl) {
                    this._uiShowMessage(this.errorPDF);
                    return;
                }

                this.sign(function (vo, token) {
                    App.loadFile(pdfUrl, function (pdfB64) {
                        getTinInfo(vo.TIN, function (selerFI) {
                            // checking for necessary buyer data
                            if (!self._checkBuyer(buyer)) { 
                                getTinInfo(taxId, function (bayerFI) {
                                    // success
                                    buyer = self.extendsBuyer(buyer, bayerFI);
                                    crateDocument(
                                        self.createDocNode(doc, {
                                            buyer: buyer,
                                            seller: {
                                                name: selerFI.fullName,
                                                address: selerFI.address,
                                                tin: selerFI.tin,
                                            },
                                            document: pdfB64
                                        })
                                    );
                                });
                            } else {
                                crateDocument(
                                    self.createDocNode(doc, {
                                        buyer: buyer,
                                        seller: {
                                            name: selerFI.fullName,
                                            address: selerFI.address,
                                            tin: selerFI.tin,
                                        },
                                        document: pdfB64
                                    })
                                );
                            }
                        });
                    });
                });
                

                function crateDocument (data) {
                    self.createDidoxDoc(
                        '000', data, 
                        function (data) {
                            // success
                            self._callFunc(success, data);
                        }, function (data) {
                            // error
                            if (typeof data.responseText === 'string') {
                                self._uiShowMessage(data.responseText);
                            } else {
                                self._uiShowMessage(data.error.message);
                            }                            
                        }, function (data) {
                            // fail
                            self._uiShowMessage(data.data.error.message);
                        }
                    );
                }

                function getTinInfo(tin, success) {
                    self.loadFullInfoByTin(tin, function (data) {
                        // success
                        success(data);
                    }, function (data) {
                        // error
                        var msg = data.responseJSON.message;
                        if (!msg) {
                            msg = self.errorTaxIdInfalid;
                        }
                        self._uiShowMessage(msg);
                    }, function (data) {
                        // fail
                        self._uiShowMessage(data.responseJSON.message);
                    });
                }
            },
            signDoc: function (docId, success, error) {
                var self = this;

                this.sign(function (vo, token) {
                    self.ckeckSignature(vo, function (signature) {
                        self.signDidoxDoc(docId, {
                            signature: signature
                        }, function (data) {
                            self._callFunc(success, data);
                        }, function (data) {
                            if (data.message) {
                                self._uiShowMessage(data.message);
                            } else {
                                self._uiShowMessage('Ошибки при подписании');
                            }
                            self._callFunc(error, data);
                        }, function (data) {
                            self._callFunc(error, data);
                        });
                    });
                });
            },

            /**
             * checking basic saved data (pkcs7, didoxToken)
             * @public ckeckTokens
             * @param {Object} vo 
             * @param {Function} success - args vo, token
             * @param {Function} error 
             * @returns 
             */
            ckeckTokens: function (vo, success, error) {
                var self = this, sAuthId, sDToken, sSignature;

                if (!vo) {
                    return;
                }
               
                // sAuthId = self.getSDAuthId(vo);

                // if (sAuthId.data && self.checkToken(self.getSDAuthIdLimot(-1), self._getCurrentDate(), sAuthId.date)) {
                //     var authId = sAuthId.data;
                //     var sDToken = self.getSDToken(vo);
                //     if ( sDToken.data && self.checkToken(self.getSTokenLimot(-1), self._getCurrentDate(), sDToken.date) ) {
                //         self.DIDOX.token = sDToken.data;
                //         self._callFunc(success, vo, sDToken.data);
                //     } else {
                //         self.laodEImzoKey(vo, function(id) {
                //             self.createPkcs7(id, self._createPkcsData(authId), function (pkcs7) {
                //                 loadDidoxToken(pkcs7, vo);
                //             }, pkcs7Fail);
                //         });
                //         // loadDidoxToken(pkcs7, vo);
                //     }
                // } else {
                //     self._loadAuthId(vo, function (pkcs7) {
                //         // success
                //         var sDToken = self.getSDToken(vo);

                //         if ( sDToken.data && self.checkToken(self.getSTokenLimot(-1), self._getCurrentDate(), sDToken.date) ) {
                //             self.DIDOX.token = sDToken.data;
                //             self._callFunc(success, vo, sDToken.data);
                //         } else {
                //             self.saveSDAuthid(self.DIDOX.authId, vo);
                //             loadDidoxToken(pkcs7, vo);
                //         }
                //     }, pkcs7Fail);
                // }

                // return;
                sDToken = self.getSDToken(vo);
                sAuthId = self.getSDAuthId(vo);
                sSignature = self.getSsignature(vo);

                if (isCurrentToken(sDToken)) {
                    if (isCurrentAuth(sAuthId)) {
                        self.DIDOX.token = sDToken.data;
                        self.DIDOX.authId = sAuthId.data;
                        self._callFunc(success, sDToken.data, vo);
                    } else {
                        self._loadAuthId(vo, function (pkcs7, authId, id) {
                            self.DIDOX.authId = authId;
                            self.saveSDAuthid(authId, vo);

                            loadSignature(id, vo);

                            self._callFunc(success, sDToken.data, vo);
                        }, pkcs7Fail)
                    }
                } else {
                    self._loadAuthId(vo, function (pkcs7, authId, id) {
                        self.DIDOX.authId = authId;
                        self.saveSDAuthid(authId, vo);
                        
                        loadSignature(id, vo);

                        self.loadDidoxToken({
                            pkcs7: pkcs7,
                            serialNumber: vo.serialNumber
                        }, function (data) {
                            // success
                            self.DIDOX.token = data.token;
                            self.saveSDToken(data.token, vo);
    
                            self._callFunc(success, data.token, vo);
                        }, function (data) {
                            // error
                            if (typeof data.responseText === 'string') {
                                self._uiShowMessage(data.responseText);
                            } else {
                                self._uiShowMessage(data.error.message);
                            }
                        }, function (data) {
                            // fail 
                            if (typeof data.responseText === 'string') {
                                self._uiShowMessage(data.responseText);
                            } else {
                                self._uiShowMessage(data.error.message);
                            }
                            
                        });
                    }, pkcs7Fail);
                    // loadDidoxToken(pkcs7, vo);
                }
                
                function isCurrentToken (sToken) {
                    return sToken.data && self.checkToken(self.getSTokenLimot(), self._getCurrentDate(), sToken.date);
                }
                function isCurrentAuth (sAuth) {
                    return sAuth.data && self.checkToken(self.getSDAuthIdLimot(), self._getCurrentDate(), sAuth.date);
                }
                // function loadDidoxToken (pkcs7, vo) {
                //     self.loadDidoxToken({
                //         pkcs7: pkcs7,
                //         serialNumber: vo.serialNumber
                //     }, function (token) {
                //         // success
                //         self.DIDOX.token = token.token;
                //         self.saveSDToken(token.token, vo);

                //         self._callFunc(success, vo, token.token);
                //     }, function (error) {
                //         // error
                //         self._uiShowMessage(error);
                //     }, function (error) {
                //         // fail 
                //         self._uiShowMessage(error);
                //     });
                // }
                function pkcs7Fail (e, r) {
                    self.loadKeysErrorCbf(e, r);
                    self._callFunc(error, e, r);
                }
                function loadSignature (id, vo) {
                    if (!sSignature.data) {
                        self.createPkcs7(id, vo.TIN, function (pkcs7) {
                            self.DIDOX.signature = pkcs7;
                            self.saveSsignature(pkcs7, vo);
                        }, console.warn, console.error);
                    }
                }
            },
            ckeckSignature: function (vo, success, error) {
                var self = this, sPksc7Sign;

                if (!vo) {
                    return ;
                }

                sPksc7Sign = self.getSsignature(vo);

                if (!self.DIDOX.signature) {
                    return self._callFunc(success, self.DIDOX.signature, vo);
                }

                if (isCurrent(sPksc7Sign)) {
                    self.DIDOX.signature = sPksc7Sign.data;
                    self._callFunc(success, sPksc7Sign.data, vo);
                } else {
                    self.laodEImzoKey(vo, function (id) {
                        self.createPkcs7(id, vo.TIN, function (pkcs7) {
                            self.DIDOX.signature = pkcs7;
                            self.saveSsignature(pkcs7, vo);
                            self._callFunc(success, pkcs7, vo);
                        }, error, self.loadKeysErrorCbf);
                    }, self.loadKeysErrorCbf);
                }
                
                function isCurrent (saved) {
                    return saved.data && self.checkToken(self.getSSignatureLimot(), self._getCurrentDate(), saved.date);
                }
            },
        
            checkVersion: function () {
                var self = this;
                EIMZOClient.checkVersion(success, error);
        
                function success (major, minor) {
                    var newVersion = self.EIMZO_MAJOR * 100 + self.EIMZO_MINOR;
                    var installedVersion = parseInt(major) * 100 + parseInt(minor);
                    if (installedVersion < newVersion) {
                        self._isCVersion = false;
                        self._uiAppdateApp();
                    } else {
                        self._isCVersion = true;
                    }
                }
        
                function error (e, r) {
                    if (r) {
                        self.__callFunc(self.events.onShowMessage, r);
                    } else {
                        self.__callFunc(self.events.onNotLoaded, e);
                    }
                }
            },

            loadAuthId: function (vo, success, error, fail) {
                Didox.loadAuthId(vo.serialNumber, success, error, fail);
            },
            laodEImzoKey: function (vo, success, error) {
                var self = this;

                EIMZOClient.loadKey(vo, function (id) {
                    self.keyId = id;
                    success(id);
                }, error);
            },
            loadDidoxToken: function (node, success, error, fail) {
                Didox.loadToken(node, success, error, fail)
            },
            loadFullInfoByTin: function (tin, success, error, fail) {
                Didox.getInfoByTaxId(tin, success, error, fail);
            },
            createDidoxDoc: function (docType, node, success, error, fail) {
                Didox.createDoc(docType, node, success, error, fail);
            },
            signDidoxDoc: function (docId, node, success, error, fail) {
                Didox.signDoc(docId, node, success, error, fail);
            },

            createPkcs7: function (id, data, success, fail) {
                EIMZOClient.createPkcs7(id, data, null, success, fail);
            },

            /**
             * checks if the token has expired
             * @public checkToken
             * @param {Number} limit - teken's active time in seconds
             * @param {Date|Number} now - The current date
             * @param {Number} saved - the date of the saved token
             * @returns {Boolean}
             */
            checkToken: function (limit, now, saved) {
                var res = now - saved;

                if (limit <= res) {
                    return false;
                }
                return true;
            },

            /**
             * get saved didox token
             * @public getSDToken
             * @returns {Object}
             */
            getSDToken: function (vo) {
                var result = AppStorage.get(this._getSDToken(vo.serialNumber));
        
                if (result && result.data) {
                    return result;
                }
        
                return {};
            },

            /**
             * save didox token
             * @public saveSDToken
             * @param {String} pkcs7 
             * @param {Object} vo 
             * @returns {void}
             */
            saveSDToken: function (pkcs7, vo) {
                AppStorage.set(this._getSDToken(vo.serialNumber), pkcs7);
            },

            /**
             * remove didox token from storage
             * @public removeSDToken
             * @param {String} vo
             * @returns {void}
             */
            removeSDToken: function (vo) {
                AppStorage.remove(this._getSDToken(vo.serialNumber));
            },


            getSDAuthId: function (vo) {
                var data = AppStorage.get(this._getSAuthKey(vo.serialNumber));
                if (data.data) {
                    return data;
                }
                return {};
            },

            saveSsignature: function (siganture, vo) {
                AppStorage.set(this._getSsignatureKey(vo.serialNumber), siganture);
            },

            getSsignature: function (vo) {
                var data = AppStorage.get(this._getSsignatureKey(vo.serialNumber));
                if (data.data) {
                    return data;
                }
                return {};
            },

            saveSDAuthid: function (authId, vo) {
                AppStorage.set(this._getSAuthKey(vo.serialNumber), authId);
            },

            /**
             * 
             * @returns {String}
             */
            getTaxId: function () {
                if (typeof deal !== 'object') {
                    return '';
                }
                return deal['UF_CRM_60B72515DC39D'];
            },
            getBuyer: function () {
                if (typeof deal !== 'object') {
                    return null;
                }

                return {
                    address: '',
                    name: '',
                    tin: '',
                }
            },
            extendsBuyer: function (buyer, fullInfo) {
                var res = {};

                extenObj({
                    name: 'shortName',
                    address: 'address',
                    tin: 'tin',
                });

                return res;

                function extenObj (params) {
                    Object.keys(params).forEach(function (key) {
                        var FIKey = params[key];
                        if (!buyer[key]) {
                            res[key] = fullInfo[FIKey];
                        } else {
                            res[key] = buyer[key];
                        }
                    });
                }
            },
            createDocNode: function (doc, params) {
                var node = {

                };
                
                node.data = {
                    isValid: true,
                    Buyer: {
                        DepartmentName: '',
                        DepartmentTin: '',
                        Name: params.buyer.name,
                        Address: params.buyer.address,
                    },
                    BuyerTin: params.buyer.tin,
                    Document: {
                        DocumentNo: doc.number,
                        DocumentDate: doc.createTime,
                        DocumentName: doc.title,
                    },
                    ContractDoc: {
                        ContractNo: '',
                        ContractDate: ''
                    },
                    SellerTin: params.seller.tin,
                    Seller: {
                        Name: params.seller.name,
                        Address: params.seller.address
                    },
                };

                if (params.document) {
                    node.document = params.document;
                }

                return node;
            },


            /**
             * Get Saved Token Limit time
             * @public 
             * @param {Number} num - 
             * @returns {Number} - token active time in seconds
             */
            getSTokenLimot: function (num) {
                if (!num || typeof num !== 'number') {
                    num = 0;
                }
                return this._currentLimit(this.TOKEM_LIMOT + num);
            },
            /**
             * Get Saved Didox Auth Id Limit time
             * @public
             * @param {Number} num 
             * @returns {Number}
             */
            getSDAuthIdLimot: function (num) {
                if (!num || typeof num !== 'number') {
                    num = 0;
                }
                return this._currentLimit(this.DAUTH_ID_LIMIT + num);
            },
            /**
             * Get Saved Signature Limit time 
             * @public
             * @param {Number} num 
             * @returns {Number}
             */
            getSSignatureLimot: function (num) {
                if (!num || typeof num !== 'number') {
                    num = 0;
                }
                return this._currentLimit(this.SIGNATURE_LIMIT + num);
            },
            _currentLimit: function (h) {
                return h * 60 * 60 * 1000;
            },


            loadKeysErrorCbf: function (e, r) {
                var self = this;

                if (r) {
                    if (r.indexOf("BadPaddingException") != -1) {
                        self._uiShowMessage(self.errorWrongPassword);
                    } else {
                        self._uiShowMessage(r);
                    }
                } else {
                    self._uiShowMessage(self.errorBrowserWS);
                } if (e) {
                    self._wsError(e);
                }
            },

            
            _checkBuyer: function (buyer) {
                var keys = Object.keys(buyer);
                var result = true;

                keys.forEach(function (key) {
                    if (buyer[key]) {
                        return;
                    }
                    result = false;
                });

                return result;
            },
            _getSAuthKey: function (serialNumber) {
                return this.SAUTH_KEY + '_' + serialNumber;
            },
            _getSsignatureKey: function (serialNumber) {
                return this.SSIGNATURE_KEY + '_' + serialNumber;
            },
            _getSDToken: function (serialNumber) {
                return this.SDTOKEN_KEY + '_' + serialNumber;
            },
            _getCurrentDate: function () {
                return new Date();
            },

            /**
             * @private _createAuthPkcs7
             * @param {*} vo 
             * @param {*} success 
             * @param {*} fail 
             */
            _loadAuthId: function (vo, success, error, fail) {
                var self = this;

                self.loadAuthId(vo, function (data) {
                    self.laodEImzoKey(vo, function (id) {
                        self.createPkcs7(id, self._createPkcsData(data.authId), function (pkcs7) {
                            // self.createPkcs7(id, vo.TIN, function (pkcs7) {
                            //     self.aaa = pkcs7;
                            // }, function(e) {
                            //     console.error(e);
                            // });
                            self._callFunc(success, pkcs7, data.authId, id, vo);
                        }, error, fail);
                    }, fail);
                });
            },

            _getDToken: function (id, authId, vo, success, fail) {
                var self = this;

                self.createPkcs7(id, self._createPkcsData(authId),
                    function (pkcs7) {

                        // self._createSignature(vo);

                        self._callFunc(success, pkcs7, vo);
                    },
                    function (e, r) {
                        self._callFunc(fail, e, r);
                    }
                );
            },
            _getSignature: function (vo, success, fail) {
                var self = this;

                self.createPkcs7(id, vo.TIN, function (pkcs7) {
                    self.saveSsignature(pkcs7);

                    self._callFunc(success, pkcs7);
                }, function(e, r) {
                    self._callFunc(fail, e, r);
                });
            },

            /**
             * leads to the format required for sending in loadDidoxKey
             * @private _createPkcsData
             * @param {String} authId 
             * @returns {String}
             */
            _createPkcsData: function (authId) {
                // return "{ authId: '" + authId + "' }";
                return JSON.stringify({
                    authId: authId
                });
            },

            _isFunction: function (func) {
                return typeof func === 'function';
            },

            /**
             * @private _callFunc
             * @param {Function} func 
             * @param  {...any} args function arguments
             * @returns {*}
             */
            _callFunc: function (func, ...args) {
                if (this._isFunction(func)) {
                    if (args) {
                        return func(...args);
                    } else {
                        return func();
                    }
                }

                return null;
            },



            _uiShowApiKeys: function (onConfirm) {
                var self = this;
                EIMZOClient.installApiKeys(function () {
                    // success
                    Swal.fire({
                        html: '<form><select id="key" class="w-full border p-2"></select></form>',
                        didOpen: function () {
                            self._uiLoadKeys();
                        }
                    }).then(function (params) {
                        var itm = (self._uiGetSelect()).value;
    
                        if (params.isConfirmed && itm) {
                            var id = document.getElementById(itm);
                            var vo = JSON.parse(id.getAttribute('vo'));
    
                            if (typeof onConfirm === 'function') {
                                onConfirm(itm, vo);
                            }
                        }
                    });
                }, function (e, r) {
                    if (r) {
                        self._uiShowMessage(r);
                    } else {
                        self._wsError(e);
                    }
                });
            },
            _uiAppdateApp: function () {
                Swal.fire({
                    icon: 'warning',
                    text: this.errorUpdateApp,
                });
            },
            _uiLoadKeys: function () {
                var self = this;
                EIMZOClient.listAllUserKeys(function (o, i) {
                    var itemId = "itm-" + o.serialNumber + "-" + i;
                    return itemId;
                }, function (itemId, v) {
                    return self._uiCreateItem(itemId, v);
                }, function (items, firstId) {
                    self._uiFillCombo(items);
                    self._uiComboSelect(firstId);
                }, function (e, r) {
                    self._uiShowMessage(self.errorCAPIWS);
                });
            },
            _uiShowMessage: function (msg) {
                alert(msg);
            },
            _uiCreateItem: function (itmkey, vo) {
                var now = new Date();
                vo.expired = dates.compare(now, vo.validTo) > 0;
                var itm = document.createElement("option");
                itm.value = itmkey;
                itm.text = vo.CN;
                if (!vo.expired) {
        
                } else {
                    itm.style.color = 'gray';
                    itm.text = itm.text + ' (срок истек)';
                }
                itm.setAttribute('vo', JSON.stringify(vo));
                itm.setAttribute('id', itmkey);
                return itm;
            },
            _uiFillCombo: function (items) {
                var combo = this._uiGetSelect();
                for (var itm in items) {
                    combo.append(items[itm]);
                }
            },
            _uiComboSelect: function (itm) {
                if (itm) {
                    var id = document.getElementById(itm);
                    id.setAttribute('selected', 'true');
                }
            },
            _uiGetSelect: function () {
                return document.getElementById('key');
            },
            _uiNotLoaded: function (e) {
                if (e) {
                    this._wsError(e);
                } else {
                    this._uiShowMessage(this.errorBrowserWS);
                }
            },
            _wsError: function (e) {
                if (e) {
                    this._uiShowMessage(this.errorCAPIWS + " : " + e);
                } else {
                    this._uiShowMessage(this.errorBrowserWS);
                }
            },
        }
    
        return DocsSign;
    }


    function getDocConstructor () {
        function DocConstrucor () {

        }

        DocConstrucor.prototype = {

        }

        return DocConstrucor;
    }

})();