/* Globals */
var DEZE; // context voor alle globale functies vanuit ASP
var __Log;
var __DoLog;
var _AiAi;
var safe;
var Oracle;
var custabspath;

var config;
var token;
function initialize(params)
{
    DEZE = params.DEZE;

    __Log   = DEZE.__Log;
    __DoLog = DEZE.__DoLog;
    _AiAi   = DEZE._AiAi;
    safe    = DEZE.safe;
    customerId = DEZE.customerId;

    custabspath = params.custabspath;
    Oracle = DEZE.Oracle;
}

// notification zoals de webhook hem van Graph binnenkreeg
function process_webhook(res_ruimte_key, zaalemail, notification)
{
    __Log("Now in process_webhook.wsc for " + zaalemail);

    /* global */ config = loadconfig(custabspath + "\\Exchange\\exchange.config");
    /* global */ token  = requestToken(config);

    if ("encryptedContent" in notification) {
        var success = process_encrypted_notification(res_ruimte_key, zaalemail, notification);
        if (success === null) {
            // Either;
            // Signature invalid (= malafide request)
            // Empty notification
            // Event not yet ready for processing
            return false;
        } else if (success) {
            return true; // Gelukt; klaar
        } /* else if (success === false) {
            Niet gelukt; ga hieronder door in de unencrypted flow die zelf alle data ophaalt
        } */
    }

    // Onderstaande route is voor Outlook series, daarvan krijgen we niet direct alle benodigde informatie door
    // Tevens voor backwards compatibility, vlak na de upgrade naar 2024.1 zijn de subscriptions nog niet vernieuwd en ontvangen we nog normale notifications

    /* Zijn alle event-id's al in Facilitor bekend? Zo niet, vul die dan eerst aan */
    if (getMSGraphSyncLevel() & 4) // Anders kunnen we het event bij de organizer toch niet opvragen
    {   /*
            Als wij vanuit Facilitor een reservering aanmaken doen wij dat op naam van de organisator (res_rsv_ruimte_externnr2)
            Dan weten we het id van het event van de ruimte (res_rsv_ruimte_externnr) nog niet.
        */
        var sql = "SELECT 'dummy'"
                + "  FROM res_rsv_ruimte"
                + " WHERE res_rsv_ruimte_externnr IS NOT NULL"
                + "   AND INSTR(res_rsv_ruimte_externnr, " + safe.quoted_sql(notification.resourceData.id) + ") = 1" // =1 want ook bij occurrences is dit het id van de seriesMaster
                + "   AND res_rsv_ruimte_verwijder IS NULL";
        var oRs = Oracle.Execute(sql);
        if (oRs.EoF) {  // We moeten het res_rsv_ruimte_externnr (ruimte-event-id) nog invullen, is alleen leeg reserveringen die in Facilitor zijn aangemaakt
            __Log("Reservering niet teruggevonden in Facilitor op basis van externnr, eerst event-gegevens opvragen bij Outlook."
                + "\nRuimte-resource id: " + notification.resourceData.id);
            if (updateExternnr(zaalemail, notification.resourceData.id) == -1) {
                return null; // Errorcode -1; exit
            }
        }
        oRs.Close();
    }
    return doImport(res_ruimte_key, zaalemail, "EXCHANGE");
}

function process_encrypted_notification(res_ruimte_key, zaalemail, notification)
{
    var success = 0;
    var base64encodedPayload    = notification.encryptedContent.data;
    var base64encodedKey        = notification.encryptedContent.dataKey;
    var base64encodedSignature  = notification.encryptedContent.dataSignature;
    var encryptionCertificateId = notification.encryptedContent.encryptionCertificateId;

    if (!unlockChilkat()) {
        __DoLog("Error Chilkat", "#FF0000");
        return false;
    }

    // 1. Use the encryptionCertificateId property to identify the certificate to use.
    if (encryptionCertificateId !== CERT_ID_PREFIX + customerId.toUpperCase()) {
        __DoLog("Error invalid certificate ID; " + encryptionCertificateId + " (should be: " + CERT_ID_PREFIX + customerId.toUpperCase() + ")", "#FF0000");
        return false;
    } else {
        var pfx = new ActiveXObject("Chilkat_9_5_0.Pfx");
        var success = pfx.LoadPfxFile(custabspath + "\\Exchange\\" + config.certificate, config.certificate_password);
        if (success != 1) {
            __DoLog("LoadPfxFile geen succes", "#FF0000");
            __DoLog(pfx.LastErrorText);
            return false;
        }
    }

    // 2. Initialize an RSA cryptographic component (such as the .NET RSACryptoServiceProvider) with the private key.
    var rsa = new ActiveXObject("Chilkat_9_5_0.Rsa");
    rsa.OaepPadding = 1;
    rsa.EncodingMode = "base64";
    rsa.Charset = "utf-8";
    success = rsa.ImportPrivateKeyObj(pfx.GetPrivateKey(0));
    if (success != 1) {
        __DoLog("Importing private key failed", "#FF0000");
        __DoLog(rsa.LastErrorText);
        return false;
    }

    // 3. Decrypt the symmetric key delivered in the dataKey property of each item in the change notification.
    try {
        var decryptedKeyBytes = rsa.DecryptBytesENC(base64encodedKey, 1);
    } catch (e) {
        __DoLog(e.message);
        return false;
    }

    // 4. Use the symmetric key to calculate the HMAC-SHA256 signature of the value in data (=base64encodedPayload).
    var crypt = new ActiveXObject("Chilkat_9_5_0.Crypt2");
    crypt.MacAlgorithm = "hmac";
    crypt.HashAlgorithm = "SHA-256";
    crypt.SetMacKeyBytes(decryptedKeyBytes);

    var decodedPayloadBytes = crypt.Decode(base64encodedPayload, "base64");

    var base64encodedHMAC = crypt.MacBytesENC(decodedPayloadBytes);
    if (base64encodedHMAC !== base64encodedSignature) {
        __DoLog("Unable to verify signature", "#FF0000");
        __DoLog(notification);
        return null;
    }

    // 5. Use the symmetric key with an Advanced Encryption Standard (AES) (such as the .NET AesCryptoServiceProvider) to decrypt the content in data.
    crypt.EncodingMode = "base64";

    // crypt.SetEncodedKey(base64EncodedDecryptedSymetricKey, "base64");
    crypt.SecretKey = decryptedKeyBytes;

    // Set the "initialization vector" by copying the first 16 bytes of the symmetric key used for decryption.
    // We hebben de eerste 16 bytes (1 byte = 8 bits) nodig, hex is base16, elk karakter is dus 4 bits (2 karakters = 8 bits = 1 byte), dus pak de eerste 32 hex-karakters
    crypt.IV = crypt.Decode(crypt.Encode(decryptedKeyBytes, "hex").slice(0, 32), "hex");

    // 6. The decrypted value is a JSON string that represents the resource instance in the change notification.
    try {
        var decryptedResourceDataStr = crypt.BytesToString(crypt.DecryptBytes(crypt.Decode(base64encodedPayload, "base64")), "utf-8");
        if (decryptedResourceDataStr === "{}") { // Leeg = deleted (en deleted != isCancelled, deleten gebeurd in de agenda van de deelnemende ruimte, isCancelled gebeurd door de organisator)
            var isDeleted = true;
        }
        var decryptedResourceData = JSON.parse(decryptedResourceDataStr);
        if (isDeleted) {
            decryptedResourceData["@removed"] = {
                "reason": "deleted"
            }
        }
        decryptedResourceData.id = notification.resourceData.id; // Die staat niet in de rich notification, maar bij de resourceData
    } catch (e) {
        __DoLog(e.message);
        return false;
    }

    if (decryptedResourceData.type === "singleInstance" || decryptedResourceData["@removed"]) { // Voor series(seriesMaster/occurrence/exception) moeten we toch meer gegevens ophalen, dus via de reguliere flow
        if (!eventReadyForProcessing(zaalemail, decryptedResourceData)) {
            return null;
        }
        return doImportSingle(res_ruimte_key, zaalemail, decryptedResourceData);
    }
}