"use strict";
function CyclicHash(sHashFunc, hashBuffer, nRounds) {
    return new Promise(function (resolve, reject) {
        if (nRounds <= 0) {
            resolve(hashBuffer);
        }
        else {
            crypto.subtle.digest(sHashFunc, hashBuffer).then(hashBuffer2 => {
                CyclicHash(sHashFunc, hashBuffer2, nRounds - 1).then(hashBuffer3 => resolve(hashBuffer3));
            });
        }
    });
}
const enc = new TextEncoder();
function CreateKeyAndIv(sPassword, sSalt, sHashFunc, nRounds, nKeyLen, nIVLen) {
    return new Promise(function (resolve, reject) {
        if (sSalt.length < 8 || nKeyLen < 1 || nIVLen < 1) {
            reject();
            return;
        }
        const msgBuffer = enc.encode(sPassword);
        const saltBuffer = enc.encode(sSalt);
        DeriveKeyAndIV([], msgBuffer, saltBuffer, sHashFunc, nRounds - 1, nKeyLen, nIVLen, { key: [], iv: [] }).then(hash => resolve(hash));
    });
}
function DeriveKeyAndIV(aHash, msgBuffer, saltBuffer, sHashFunc, nRounds, nKeyLen, nIVLen, hashs) {
    return new Promise(function (resolve, reject) {
        const u8bHash = Uint8Array.from(aHash);
        const combinedBuffer = new Uint8Array(msgBuffer.length + saltBuffer.length + u8bHash.length);
        combinedBuffer.set(u8bHash);
        combinedBuffer.set(msgBuffer, u8bHash.length);
        combinedBuffer.set(saltBuffer, msgBuffer.length + u8bHash.length);
        crypto.subtle.digest(sHashFunc, combinedBuffer).then(hashBuffer => {
            CyclicHash(sHashFunc, hashBuffer, nRounds).then(hashBuffer2 => {
                const hashArray1 = Array.from(new Uint8Array(hashBuffer2));
                let hashArray = hashArray1.slice();
                if (hashs.key.length < nKeyLen) {
                    const nDelta = nKeyLen - (hashs.key.length);
                    const aKeyArr = hashArray.splice(0, nDelta);
                    hashs.key = hashs.key.concat(aKeyArr);
                }
                if (hashs.iv.length < nIVLen && hashArray.length > 0) {
                    const nDelta = nIVLen - (hashs.iv.length);
                    const aIvArr = hashArray.splice(0, nDelta);
                    hashs.iv = hashs.iv.concat(aIvArr);
                }
                if ((hashs.key.length < nKeyLen) || (hashs.iv.length < nIVLen)) {
                    DeriveKeyAndIV(hashArray1, msgBuffer, saltBuffer, sHashFunc, nRounds, nKeyLen, nIVLen, hashs).then(hashs2 => resolve(hashs2));
                }
                else {
                    resolve(hashs);
                }
            });
        });
    });
}
const crcTable = (() => {
    let c;
    const crcTable = [];
    for (let n = 0; n < 256; n++) {
        c = n;
        for (let k = 0; k < 8; k++) {
            c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
        }
        crcTable[n] = c;
    }
    return crcTable;
})();
function Crc32(str) {
    let crc = 0 ^ (-1);
    const ui8aMsg = enc.encode(str);
    for (const byte of ui8aMsg) {
        crc = (crc >>> 8) ^ crcTable[(crc ^ byte) & 0xFF];
    }
    return (crc ^ (-1)) >>> 0;
}
async function EncryptAesCbc(key, ui8aIv, message) {
    if (!key) {
        ErrorMsgShow(GetLocaleText("Err_CryptoKeyCorrupted"), { eGoal: "both", sErrType: "Bad CryptoKey" });
        throw "";
    }
    const ui8aMsg = enc.encode(message);
    try {
        const data = await crypto.subtle.encrypt({ name: "AES-CBC", length: 256, iv: new Uint8Array(ui8aIv) }, key, ui8aMsg);
        return encodeBase64(Array.from(new Uint8Array(data)));
    }
    catch (err) {
        throw "encryptMsg:" + JSON.stringify(err);
    }
}
async function DecryptAesCbc(key, ui8aIv, message) {
    if (!key) {
        ErrorMsgShow(GetLocaleText("Err_CryptoKeyCorrupted"), { eGoal: "both", sErrType: "Bad CryptoKey" });
        throw "";
    }
    const ui8aMsg = new Uint8Array(decodeBase64(message));
    try {
        const data = await crypto.subtle.decrypt({ name: "AES-CBC", length: 256, iv: new Uint8Array(ui8aIv) }, key, ui8aMsg);
        return new TextDecoder().decode(data);
    }
    catch (err) {
        throw "decryptMsg:" + JSON.stringify(err);
    }
}
async function EncryptStringAsGS(sInput, sPassword) {
    try {
        const sInputAndCrc32 = sInput + Crc32(sInput).toString(16).padStart(8, "0");
        const hashs = await CreateKeyAndIv(sPassword, "Jf5tuaDP", "SHA-1", 10, 32, 16);
        const ckKey = await crypto.subtle.importKey("raw", new Uint8Array(hashs.key), { name: 'AES-CBC', length: 256 }, true, ['encrypt', 'decrypt']);
        return await EncryptAesCbc(ckKey, new Uint8Array(hashs.iv), sInputAndCrc32);
    }
    catch (err) {
        throw err;
    }
}
async function EncryptStringWithKey(sInput, ckKey, iv) {
    if (!sInput) {
        return "";
    }
    if (!ckKey) {
        ErrorMsgShow(GetLocaleText("Err_CryptoKeyCorrupted"), { eGoal: "both", sErrType: "Bad CryptoKey" });
        throw "";
    }
    const sInputAndCrc32 = sInput + Crc32(sInput).toString(16).padStart(8, "0");
    try {
        return await EncryptAesCbc(ckKey, new Uint8Array(iv), sInputAndCrc32);
    }
    catch (err) {
        throw "EncryptStringWithKey error:" + err;
    }
}
async function DecryptStringWithKey(sInput, ckKey, iv) {
    if (!sInput) {
        return "";
    }
    if (!ckKey) {
        ErrorMsgShow(GetLocaleText("Err_CryptoKeyCorrupted"), { eGoal: "both", sErrType: "Bad CryptoKey" });
        throw "Bad CryptoKey: Key is missing or invalid.";
    }
    try {
        const data = await DecryptAesCbc(ckKey, new Uint8Array(iv), sInput);
        if (data.length < 9) {
            throw "DecryptStringWithKey error: data too short";
        }
        const text = data.slice(0, -8);
        const receivedCrc = parseInt(data.slice(-8), 16);
        if (receivedCrc !== Crc32(text)) {
            throw "DecryptStringWithKey error: CRC32 is wrong";
        }
        return text;
    }
    catch (err) {
        throw "DecryptStringWithKey error:" + err;
    }
}
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
function encodeBase64(bytes) {
    let output = '';
    for (let i = 0; i < bytes.length; i += 3) {
        const byte1 = bytes[i];
        const byte2 = i + 1 < bytes.length ? bytes[i + 1] : 0;
        const byte3 = i + 2 < bytes.length ? bytes[i + 2] : 0;
        const enc1 = byte1 >> 2;
        const enc2 = ((byte1 & 3) << 4) | (byte2 >> 4);
        const enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);
        const enc4 = byte3 & 63;
        if (i + 1 == bytes.length) {
            output += chars.charAt(enc1) + chars.charAt(enc2) + '==';
        }
        else if (i + 2 == bytes.length) {
            output += chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + '=';
        }
        else {
            output += chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + chars.charAt(enc4);
        }
    }
    return output;
}
function decodeBase64(base64) {
    const output = [];
    base64 = base64.replaceAll('-', '+');
    base64 = base64.replaceAll('_', '/');
    base64 = base64.replaceAll(/[^A-Za-z0-9+/=]/g, '');
    for (let i = 0; i < base64.length;) {
        const enc1 = chars.indexOf(base64.charAt(i++));
        const enc2 = chars.indexOf(base64.charAt(i++));
        const enc3 = chars.indexOf(base64.charAt(i++));
        const enc4 = chars.indexOf(base64.charAt(i++));
        const byte1 = (enc1 << 2) | (enc2 >> 4);
        const byte2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        const byte3 = ((enc3 & 3) << 6) | enc4;
        if (byte1 >= 0) {
            output.push(byte1);
        }
        if (enc3 !== 64 && byte2 >= 0) {
            output.push(byte2);
        }
        if (enc4 !== 64 && byte3 >= 0) {
            output.push(byte3);
        }
    }
    return output;
}
function PassHide(dom, domPassField, sSize) {
    const img = SetImg("view-medium", "1.25rem");
    img.classList.add("show_hide_psw");
    dom.innerHTML = "";
    dom.append(img);
    dom.onclick = function () { PassShow(dom, domPassField, sSize); };
    domPassField.type = "password";
}
function PassShow(dom, domPassField, sSize) {
    const img = SetImg("hide-medium", "1.25rem");
    img.classList.add("show_hide_psw");
    dom.innerHTML = "";
    dom.append(img);
    dom.onclick = function () { PassHide(dom, domPassField, sSize); };
    domPassField.type = "text";
}
function hashPassword(password, salt, iterations = 10000) {
    return new Promise(function (resolve, reject) {
        const passwordBytes = enc.encode(password);
        const saltBytes = new Uint8Array(salt);
        crypto.subtle.importKey('raw', passwordBytes, { name: 'PBKDF2' }, false, ['deriveBits']).then(keyMaterial => crypto.subtle.deriveBits({ name: 'PBKDF2', salt: saltBytes, iterations: iterations, hash: 'SHA-256' }, keyMaterial, 256).then(hashBuffer => resolve(hashBuffer)));
    });
}
function hmacSHA256(key, data) {
    return new Promise(function (resolve, reject) {
        const keyBytes = new Uint8Array(key);
        const dataBytes = enc.encode(data);
        crypto.subtle.importKey('raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']).then(oCryptoKey => {
            crypto.subtle.sign('HMAC', oCryptoKey, dataBytes).then(signature => resolve(signature));
        });
    });
}
function generateNonce(length = 16) {
    const array = new Uint8Array(length);
    crypto.getRandomValues(array);
    return Array.from(array);
}
function xor(a, b) {
    const result = new Uint8Array(a.length);
    for (let i = 0; i < a.length; i++) {
        result[i] = a[i] ^ b[i];
    }
    return result;
}
function sha256(data) {
    return new Promise(function (resolve, reject) {
        const dataBytes = new Uint8Array(data);
        crypto.subtle.digest('SHA-256', dataBytes).then(hashBuffer => resolve(hashBuffer));
    });
}
function CreateClientFirstMsg(username) {
    let clientNonce = encodeBase64(generateNonce());
    const clientFirstMsgBare = `n=${username},r=${clientNonce}`;
    return {
        clientNonce: clientNonce,
        clientFirstMsg: `n,,${clientFirstMsgBare}`,
        clientFirstMsgBare
    };
}
function clientFinalMsg(password, clientFirstMsgBare, serverNonce, salt, iterations, serverFirstMsg) {
    return new Promise(function (resolve, reject) {
        const clientFinalMsgWithoutProof = 'c=biws,r=' + serverNonce;
        hashPassword(password, salt, iterations).then(saltedPassword => hmacSHA256(saltedPassword, 'Client Key').then(clientKey => sha256(clientKey).then(storedKey => {
            const authMsg = clientFirstMsgBare + "," + serverFirstMsg + "," + clientFinalMsgWithoutProof;
            hmacSHA256(storedKey, authMsg).then(clientSignature => {
                const ui8aClientKey = new Uint8Array(clientKey);
                const ui8aClientSignature = new Uint8Array(clientSignature);
                const clientProof = xor(ui8aClientKey, ui8aClientSignature);
                resolve(`${clientFinalMsgWithoutProof},p=${encodeBase64(Array.from(clientProof))}`);
            });
        })));
    });
}
function BigCredentialsRequest(sTitle, sQuestion, obj) {
    return new Promise(function (resolve, reject) {
        let Credentials = {
            "title": sTitle,
            "bottom_box": [
                { "title": "LOG IN", "close": true, "command": "save and exec", "default": true, },
            ],
            "bottom_box_hide": true,
            cont: [
                { "title": "", cont: [
                        { type: "row", "h": "30%" },
                        { type: "row", "h": "40%", cont: [
                                { type: "column", w: "30%" },
                                { type: "column", w: "40%", cont: [
                                        { type: "text", "title": sQuestion },
                                        { type: "separator", w: "99%" },
                                    ] },
                                { type: "column", w: "30%" }
                            ] },
                    ] }
            ]
        };
        for (let i = 0; i < obj.arr.length; i++) {
            let sType = "textfield";
            if (obj.arr[i].password) {
                sType = "password";
            }
            Credentials.cont[0].cont[1].cont[1].cont.push({ type: "text", "title": obj.arr[i].title });
            Credentials.cont[0].cont[1].cont[1].cont.push({ type: sType, title: "", value: "arr[" + i + "].value" });
        }
        Credentials.cont[0].cont[1].cont[1].cont.push({ type: "row", "h": "10px" });
        Credentials.cont[0].cont[1].cont[1].cont.push({ type: "bottom_box", "title": "", });
        Credentials.cont[0].cont.push({ type: "row", "h": "30%" });
        if (!obj.oCredDialogPar) {
            obj.oCredDialogPar = { top: "40%", width: "400px" };
        }
        CreateFloatMenu(Credentials, obj.oCredDialogPar, obj, { fontSize: "14px", bNoClose: true, bNoborder: true, bAboveAll: true, bNoTitle: true, bNoResize: true })
            .promise.then(data => resolve(data.data)).catch(data => reject(data));
    });
}
async function CredentialsRequest(sTitle, sQuestion, obj) {
    let Credentials = {
        "title": sTitle,
        "bottom_box": [
            { "title": "OK", "close": true, "command": "save and exec", "default": true },
            { "title": "Cancel", "close": true, }
        ],
        cont: [
            { "title": "", cont: [
                    { "type": "row", "h": "10px" },
                    { "type": "text", "title": sQuestion },
                    { "type": "row", "h": "10px" },
                    { type: "separator", w: "100%" },
                ] }
        ]
    };
    for (let i = 0; i < obj.arr.length; i++) {
        let sType = "textfield";
        if (obj.arr[i].password) {
            sType = "password";
        }
        Credentials.cont[0].cont.push({ "type": sType, "title": obj.arr[i].title, "value": "arr[" + i + "].value", title_width_group: "1" });
    }
    Credentials.cont[0].cont.push({ "type": "row", "h": "10px" });
    Credentials.cont[0].cont.push({ type: "separator", w: "100%" });
    const oMenu = CreateFloatMenu(Credentials, { top: "40%", width: "400px" }, obj, { "fontSize": "14px" });
    try {
        await oMenu.promise;
    }
    catch {
        throw null;
    }
    return obj;
}
function Authentication(login) {
    if (!sAuthentication)
        return;
    if (sBearerToken) {
        sAuthentication = "";
        return;
    }
    if (!login) {
        login = "";
    }
    const oCRP = { arr: [{ title: "Login: ", value: login }, { title: "Password: ", value: "", password: true }],
        oCredDialogPar: { top: "0px", left: "0px", width: "100%", height: -100 } };
    const sTmp = sAuthentication;
    sAuthentication = "";
    BigCredentialsRequest("Authentication of user", sTmp, oCRP).then(data => {
        const username = data.arr[0].value;
        const pass = data.arr[1].value;
        {
            CreateKeyAndIv(pass, "Jf5tuaDP", "SHA-1", 10, 32, 16).then(hashs => {
                crypto.subtle.importKey("raw", new Uint8Array(hashs.key), { name: 'AES-CBC', length: 256 }, true, ['encrypt', 'decrypt']).then(key => {
                    oSavedData.iv = hashs.iv;
                    oSavedData.key = key;
                    oSavedData.sequence = hashs.key;
                    localStorage.setItem("oSavedData", JSON.stringify(oSavedData));
                });
            });
        }
        const oClientFirstMsg = CreateClientFirstMsg(username);
        SendRecivePost("/auth", { "message": oClientFirstMsg.clientFirstMsgBare })
            .then(firstServerMsg => FirstServerMessProcessing(oClientFirstMsg.clientFirstMsgBare, pass, firstServerMsg))
            .catch(() => { sAuthentication = "Str_AuthSignIn"; });
    });
}
function Logout() {
    sAuthentication = "Str_AuthSignIn";
    sBearerToken = "";
    localStorage.setItem("sBearerToken", "");
}
function FirstServerMessProcessing(clientFirstMsgBare, pass, firstServerMsg) {
    const oFirstServerMsg = ParseFirstServerMsg(firstServerMsg.message);
    if (!oFirstServerMsg) {
        sAuthentication = "Str_AuthSmthngWntWrng";
        return;
    }
    clientFinalMsg(pass, clientFirstMsgBare, oFirstServerMsg.sClientAndServerNonce, decodeBase64(oFirstServerMsg.salt), oFirstServerMsg.iterations, firstServerMsg.message)
        .then(sClientFinMsg => {
        SendRecivePost("/auth", { "session_id": firstServerMsg.session_id, "message": sClientFinMsg })
            .then(finServerMsg => FinServerMessProcessing({ "session_id": firstServerMsg.session_id, "message": sClientFinMsg }, finServerMsg))
            .catch(() => { sAuthentication = "Str_AuthSmthngWntWrng"; });
    });
}
function FinServerMessProcessing(finCliMsg, finServerMsg) {
    if (finServerMsg.token) {
        localStorage.setItem("sBearerToken", finServerMsg.token);
        sBearerToken = finServerMsg.token;
        sAuthentication = "";
    }
    else {
        sAuthentication = "Str_AuthErrCreds";
    }
}
function ParseFirstServerMsg(message) {
    const messageParts = message.split(',');
    const rPart = messageParts.find(part => part.startsWith('r='));
    const sPart = messageParts.find(part => part.startsWith('s='));
    const iPart = messageParts.find(part => part.startsWith('i='));
    if (!rPart || !sPart || !iPart) {
        return null;
    }
    const sClientAndServerNonce = rPart.substring(2);
    const salt = sPart.substring(2);
    const iterations = parseInt(iPart.substring(2), 10);
    return { sClientAndServerNonce, salt, iterations };
}
function ParseUserInfo(data) {
    PutPostApiDialogToConsol(data);
    if (data.response.result && data.response.result.master_encryption_params) {
        const aEncPar = data.response.result.master_encryption_params.split(',');
        if (aEncPar.length == 3) {
            const sequence = decodeBase64(aEncPar[1]);
            crypto.subtle.importKey("raw", new Uint8Array(sequence), { name: 'AES-CBC', length: 256 }, true, ['encrypt', 'decrypt']).then(key => {
                oSavedData.iv = decodeBase64(aEncPar[2]).splice(0, 16);
                oSavedData.key = key;
                oSavedData.sequence = sequence;
                localStorage.setItem("oSavedData", JSON.stringify(oSavedData));
            });
        }
    }
}
function generateKeyPair() {
    return new Promise(function (resolve, reject) {
        crypto.subtle.generateKey({ name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, true, ["encrypt", "decrypt"]).then(keyPair => resolve(keyPair)).catch(err => reject(err));
    });
}
function encryptData(publicKey, data) {
    return new Promise(function (resolve, reject) {
        const encodedData = new TextEncoder().encode(data);
        crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, encodedData)
            .then(encryptedData => resolve(encryptedData)).catch(err => reject(err));
    });
}
function decryptData(privateKey, encryptedData) {
    return new Promise(function (resolve, reject) {
        crypto.subtle.decrypt({ name: "RSA-OAEP" }, privateKey, encryptedData)
            .then(decryptedData => resolve(new TextDecoder().decode(decryptedData))).catch(err => reject(err));
    });
}
function exportPublicKeyToSPKI(publicKey) {
    return new Promise(function (resolve, reject) {
        crypto.subtle.exportKey("spki", publicKey)
            .then(exportedKey => resolve(encodeBase64(Array.from(new Uint8Array(exportedKey)))))
            .catch(err => reject(err));
    });
}
async function AsyncPreauthByCookieProcess() {
    const sPreauthToken = GetCookieByName("preauth_token");
    try {
        if (sPreauthToken) {
            const preauthMsg = await SendRecivePost("/preauth", {}, sPreauthToken);
            ParsePreauthInfo(preauthMsg);
            DeleteCookie("preauth_token");
        }
        else {
            sAuthentication = "Str_AuthSignIn";
        }
    }
    catch {
        sAuthentication = "Str_AuthSignIn";
        throw null;
    }
}
function ParsePreauthInfo(oData) {
    if (oData.token) {
        localStorage.setItem("sBearerToken", oData.token);
        sBearerToken = oData.token;
        sAuthentication = "";
    }
    else {
        sAuthentication = "Str_AuthSignIn";
    }
    if (oData.master_encryption_params) {
        const aEncPar = oData.master_encryption_params.split(',');
        if (aEncPar.length == 3) {
            const oAlgorithm = GetImportKeyAlgorithm(aEncPar[0]);
            if (!oAlgorithm) {
                HtmlMsgbox(GetLocaleText("Err_KeyDerivationAlgorithmNotSupported", [aEncPar[0]]), "Preauth error (0)", { bNoCurrJobName: true });
                sBearerToken = "";
                sAuthentication = "Str_AuthSignIn";
                return;
            }
            const sequence = decodeBase64(aEncPar[1]);
            crypto.subtle.importKey("raw", new Uint8Array(sequence), oAlgorithm, true, ['encrypt', 'decrypt']).then(key => {
                oSavedData.iv = decodeBase64(aEncPar[2]).splice(0, 16);
                oSavedData.key = key;
                oSavedData.sequence = sequence;
                localStorage.setItem("oSavedData", JSON.stringify(oSavedData));
            }).catch(() => {
                HtmlMsgbox(GetLocaleText("Err_PreauthInfoNotCorrect"), "Preauth error (1)", { bNoCurrJobName: true });
                sAuthentication = "Str_AuthSignIn";
                sBearerToken = "";
            });
        }
        else {
            HtmlMsgbox(GetLocaleText("Err_PreauthInfoNotCorrect"), "Preauth error (2)", { bNoCurrJobName: true });
            sAuthentication = "Str_AuthSignIn";
            sBearerToken = "";
        }
    }
    else {
        HtmlMsgbox(GetLocaleText("Err_PreauthInfoNotCorrect"), "Preauth error (3)", { bNoCurrJobName: true });
        sAuthentication = "Str_AuthSignIn";
        sBearerToken = "";
    }
}
function GetImportKeyAlgorithm(sAlgId) {
    switch (sAlgId) {
        case "aes256-cbc": return { name: 'AES-CBC', length: 256 };
        default: return null;
    }
}
