#include "ANSUtilities.h"
#include <iostream>
#include <CkFileAccess.h>
#include <CkAuthGoogle.h>
#include <CkSocket.h>
#include <CkGlobal.h>
#include <CkMailMan.h>
#include <CkEmail.h>
#include <CkMht.h>
#include <vector>

static bool ansutilityLicenceValid = false;
static std::once_flag ansutilityLicenseOnceFlag;
std::timed_mutex timeImageMutex;

namespace ANSCENTER
{
    static void VerifyGlobalANSUltilityLicense(const std::string& licenseKey) {
        try {
            static const std::vector<std::pair<int, std::string>> licenseChecks = {
                {1000, "ANNHUB-LV"},
                {1001, "DLHUB-LV"},
                {1002, "ODHUB-LV"},
                {1003, "ANSVIS"},
                {1004, "ANSFR"},
                {1005, "ANSOCR"},
                {1006, "ANSALPR"},
                {1007, "ANSCV"},
                {1008, "ANSSRT"}
            };
            ansutilityLicenceValid = false;
            for (const auto& [productId, productName] : licenseChecks) {
                if (ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, productId, productName)) {
                    ansutilityLicenceValid = true;
                    break; // Stop at the first valid license
                }
            }
        }
        catch (const std::exception& e) {
			ansutilityLicenceValid = false;
        }
    }
    ANSUtilities::ANSUtilities() {
        _unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d";//
        _fireBaseSessionToken = "";
        _proxyHost = "";
        _proxyPort = 0;
        _proxyUsername = "";
        _proxyPassword = "";
        _bProxy = false;
    }
    void ANSUtilities::CheckLicense() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            // Check once globally
            std::call_once(ansutilityLicenseOnceFlag, [this]() {
                VerifyGlobalANSUltilityLicense(_licenseKey);
                });
            // Update this instance's local license flag
            _isLicenseValid = ansutilityLicenceValid;
        }
        catch (const std::exception& e) {
            this->_logger.LogFatal("ANSUtilities::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
        }
    }
    void ANSUtilities::CheckUnlockCode() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            CkGlobal glob;
            _unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d";
            _isUnlockCodeValid = glob.UnlockBundle(_unlockCode.c_str());

            if (!_isUnlockCodeValid) {
                _logger.LogFatal("ANSUtilities::CheckUnlockCode", glob.lastErrorText(), __FILE__, __LINE__);
                return;
            }

            int status = glob.get_UnlockStatus();
            if (status != 2) {
                _logger.LogDebug("ANSUtilities::CheckUnlockCode", "Unlocked in trial mode.", __FILE__, __LINE__);
            }
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::CheckUnlockCode", e.what(), __FILE__, __LINE__);
        }
    }
	ANSUtilities::~ANSUtilities() 
    {
        _fireBaseSessionToken = "";
	}
    bool ANSUtilities::Initialize(const std::string& licenseKey) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            _licenseKey = licenseKey;
            CheckLicense();
            CheckUnlockCode();
		    return _isLicenseValid && _isUnlockCodeValid;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::Initialize", e.what(), __FILE__, __LINE__);
            return false;
        }

    }
    bool ANSUtilities::SetServerProxy(const std::string& proxyHost, int proxyPort, const std::string& proxyUsername, const std::string& proxyPassword) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
        try {
            _proxyHost = proxyHost;
            _proxyPort = proxyPort;
            _proxyUsername = proxyUsername;
            _proxyPassword = proxyPassword;
            _bProxy = false;

            if (!_proxyHost.empty() && (_proxyPort > 0)) {
                _bProxy = true;
            }
            else {
                _bProxy = false;
            }

            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::SetServerProxy", e.what(), __FILE__, __LINE__);
            return false;
        }
    }
    std::string ANSUtilities::GetFirebaseCloudMessageAcccessToken(std::string privateKey)
	{
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isLicenseValid) {
			this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid license key", __FILE__, __LINE__);
			return "Error: Invalid license key";
		}

		if (!_isUnlockCodeValid) {
			this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid unlock code", __FILE__, __LINE__);
			return "Error: Invalid unlock code";
		}
        try {

            auto currentTime = std::chrono::system_clock::now();

            // Check if we need to generate a new token
            bool needNewToken = false;

            if (!tokenInitialized) {
                std::cout << "First time calling getToken(), generating initial token..." << std::endl;
                needNewToken = true;
            }
            else {
                // Calculate time difference in seconds since token was generated
                auto timeDiff = std::chrono::duration_cast<std::chrono::seconds>(currentTime - tokenGeneratedTime);
                long secondsElapsed = timeDiff.count();

                std::cout << "Time since token generated: " << secondsElapsed << " seconds" << std::endl;

                if (secondsElapsed >= TOKEN_EXPIRY_SECONDS) {
                    std::cout << "Token expired (" << secondsElapsed << "s >= " << TOKEN_EXPIRY_SECONDS << "s), regenerating..." << std::endl;
                    needNewToken = true;
                }
                else {
                    long remainingTime = TOKEN_EXPIRY_SECONDS - secondsElapsed;
                    std::cout << "Token still valid, " << remainingTime << " seconds remaining" << std::endl;
                }
            }
            if (needNewToken) {
                CkFileAccess fac;
                const char* jsonKey = "{"
                    "\"type\": \"service_account\","
                    "\"project_id\": \"ansaiboxpushnotifications\","
                    "\"private_key_id\": \"62f7485c308b9ea613866c24c98736fec498d47d\","
                    "\"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDSwXb/N4vPkANa\\nA7nwOTxS9NVCup/9mIdyR7FRYJNV8uHY7699I8LCCvG5rIhWGZ/Rb4Z9ocJKuuj9\\nRelsjTDEWeBU/o8ip41Ru8K3KlOO25HIVDckyhbmfDzzlf8Ham1HhOUZmfDUfQ2d\\n5YoUXflqoCyvOi8aqUgfcP9uZuIpEhyQvjqDfY0d0B36NoXpilFE8IuIlBsnpfZn\\nYkeHs/Cl6CDaKxGeUZmrqinyMH5f7m4Nz30V5s8Xdl4WZdoYhVatn1or57Zjy/zl\\ng+4rWeHRbYrBUJlMb8/7IaaZ7KuDkh0awEFIDvLuNvjXkaGKJT33vxKSsaH4yrhg\\nYiR5OVY3AgMBAAECggEADiUS5f0l4ofhWbS/UXqd7FlnSMO6wivvB0H9ih8ntFCJ\\nTOSFTCpOw3Q1lgcY3WJ54fYQujTVk+togLsk9/af68W2cy3kkGhbaT1nS6DJG+Dr\\nr1zLmKoBkHWNJ7IM/EPt0qt+LtIwoipEdDD4K/bEqx3V8eq/R5RN9WJBmnjIPAZO\\nXYGtTBu66yTjMVX9AFqYGJwaW96mHAi3lI5Mso5wTmFA8tSBiWmiYgpDDUNzu+4w\\nPnbDJabMbv4xnC6UtiqLIvv+P3BWWyLz0bzZ7W9P2fGK9Rz04leTXZlNcfC/IIAr\\nXDPVZfF7E5Wici53Cwc/QnxoCVH9n9hqsyTTUrKYbQKBgQD9WuCLVZRwKnAsW2Wx\\nZjh8Z92aGjIoc/lJTYaFsYGT7Hx496izhmg18kUillUDe/L9gHuDJPIMv89irjV1\\n07jN+z+tANyFT+h/NHCSxreG8FDbo8F88snekMEsBym3CicE/P6aWVx+lIig8rPS\\nHAqA1WOwdFzto/rG4zFKIiajYwKBgQDU9LxgERHwoy8mt2U5h/hg4oiKlycFJCFV\\nsgEqhVFN7Cs8jlov4tJh9863tVeYYGSKrP1HS9dVUE+7VY2qgPbyAtR5QZeHIV+b\\nzMx8o4HVLN4ffmld+/mYiAzIQiQeZOoC0mczakQpRDE8sghQQoYLwEcKXcTkLr75\\nk1XIpT4cHQKBgQCmMSzGeZbrlQsMLdAhdHptMPzuj2yDmL/X0+EAZhYn4KMt/tdN\\nHEfTy16Kd67AoFge7l8XAe89ab0ycDBlYEMD62IzrDL7yBUtDEskHPJas912lo7f\\n1auSMcZliTVV+nTqEsM4oJHJ/sk5Oru2gepp5JCGOW6T/FMOkA3PIWPTHQKBgQCG\\n456Om0Fp03OCaphLoLzLYbJrVuL4drJGvcHPVTLy0K1yZhjqTBpGw9jEtLEPa79D\\nt9+W0YtMFtrqJn7diWLiWLiNNebtSU5uOYMtT8Rla04nVMMZLQoke8jc8EhAmFtB\\n/lQwVRdnrDIj6AEsFXci6mAVSN/2SUXegFzOAx0cYQKBgBYCGGKWd/EFvszO5VS6\\nnegR3fpx4GRShA4iWfbQhPcjPunKthUAUguN4m+FfQuSA6xaBsz+om9hRiSCHe2l\\nGio/U7z2QYznnl/furGXEhS6yE+u3cSN82N0PV/c4XY0DUqKycE6cx9GVTOmx7NP\\n897fh7f103RrGgIRivtM1g3g\\n-----END PRIVATE KEY-----\\n\","
                    "\"client_email\": \"firebase-adminsdk-tcw8f@ansaiboxpushnotifications.iam.gserviceaccount.com\","
                    "\"client_id\": \"108921284506387607836\","
                    "\"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\","
                    "\"token_uri\": \"https://oauth2.googleapis.com/token\","
                    "\"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\","
                    "\"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-tcw8f%40ansaiboxpushnotifications.iam.gserviceaccount.com\","
                    "\"universe_domain\": \"googleapis.com\""
                    "}";
                if (!_isLicenseValid) {
                    this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid license key", __FILE__, __LINE__);
                    _fireBaseSessionToken = "";
                    return "Error: Invalid license key";
                }
                if (!_isUnlockCodeValid) {
                    this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid unlock code", __FILE__, __LINE__);
                    _fireBaseSessionToken = "";
                    return "Error: Invalid unlock code";
                }
                if (privateKey != "62f7485c308b9ea613866c24c98736fec498d47d") {
                    this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", "Error: Invalid private key", __FILE__, __LINE__);
                    _fireBaseSessionToken = "";
                    return "Error: Invalid private key";
                }
                CkAuthGoogle gAuth;
                gAuth.put_JsonKey(jsonKey);
                gAuth.put_Scope("https://www.googleapis.com/auth/cloud-platform");
                gAuth.put_ExpireNumSeconds(TOKEN_EXPIRY_SECONDS);
                gAuth.put_SubEmailAddress("");
                CkSocket tlsSock;
                bool success = tlsSock.Connect("www.googleapis.com", 443, true, 5000);
                if (success != true) {
                    this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", tlsSock.lastErrorText(), __FILE__, __LINE__);
                    _fireBaseSessionToken = "";
                    return std::string("Error:") + tlsSock.lastErrorText();
                }
                success = gAuth.ObtainAccessToken(tlsSock);
                if (success != true) {
                    this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", gAuth.lastErrorText(), __FILE__, __LINE__);
                    _fireBaseSessionToken = "";
                    return std::string("Error:") + tlsSock.lastErrorText();
                }
                _fireBaseSessionToken = gAuth.accessToken();
                tokenGeneratedTime = currentTime;  // Reset the time when token is generated
                tokenInitialized = true;
            }
            return _fireBaseSessionToken;
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSUtilities::GetFirebaseCloudMessageAcccessToken()", e.what(), __FILE__, __LINE__);
            _fireBaseSessionToken = "";
            return "Error: " + std::string(e.what());
		}
       
	}
    std::string ANSUtilities::CreateAWSSNSTopic(const std::string& topicName) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", "Error: Invalid unlock code", __FILE__, __LINE__);
            return "Error: Invalid unlock code";
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", "Error: Invalid license key", __FILE__, __LINE__);
            return "Error: Invalid license key";
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "CreateTopic");
            _rest.AddQueryParam("Name", topicName.c_str());

            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", _rest.lastErrorText(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.lastErrorText());
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::CreateAWSSNSTopic", _rest.responseHeader(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.responseHeader());
            }

            CkXml xml;
            xml.LoadXml(responseXml);
            return xml.chilkatPath("CreateTopicResult|TopicArn|*");
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::CreateAWSSNSTopic", e.what(), __FILE__, __LINE__);
            return "Error: " + std::string(e.what());
        }
    } 
    bool ANSUtilities::DeleteAWSSNSTopic(const std::string& topicARN) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", "Error: Invalid unlock code", __FILE__, __LINE__);
            return false;
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", "Error: Invalid license key", __FILE__, __LINE__);
            return false;
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "DeleteTopic");
            _rest.AddQueryParam("TopicArn", topicARN.c_str());

            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", _rest.lastErrorText(), __FILE__, __LINE__);
                return false;
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::DeleteAWSSNSTopic", _rest.responseHeader(), __FILE__, __LINE__);
                return false;
            }

            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::DeleteAWSSNSTopic", e.what(), __FILE__, __LINE__);
            return false;
        }
    }  
    std::string ANSUtilities::SubcribeSMSPhoneNumber(const std::string& topicARN, const std::string& phoneNumber) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::SubscribeSMSPhoneNumber", "Error: Invalid unlock code", __FILE__, __LINE__);
            return "Error: Invalid unlock code";
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::SubscribeSMSPhoneNumber", "Error: Invalid license key", __FILE__, __LINE__);
            return "Error: Invalid license key";
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "Subscribe");
            _rest.AddQueryParam("Endpoint", phoneNumber.c_str()); // Phone number in E.164 format
            _rest.AddQueryParam("Protocol", "sms");
            _rest.AddQueryParam("TopicArn", topicARN.c_str());

            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::SubscribeSMSPhoneNumber", _rest.lastErrorText(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.lastErrorText());
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::SubscribeSMSPhoneNumber", _rest.responseHeader(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.responseHeader());
            }

            CkXml xml;
            xml.LoadXml(responseXml);

            return xml.chilkatPath("SubscribeResult|SubscriptionArn|*");
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::SubscribeSMSPhoneNumber", e.what(), __FILE__, __LINE__);
            return "Error: " + std::string(e.what());
        }
    }
    std::string ANSUtilities::SubscribeEmailAddress(const std::string& topicARN, const std::string& emailAddress) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::SubscribeEmailAddress", "Error: Invalid unlock code", __FILE__, __LINE__);
            return "Error: Invalid unlock code";
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::SubscribeEmailAddress", "Error: Invalid license key", __FILE__, __LINE__);
            return "Error: Invalid license key";
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "Subscribe");
            _rest.AddQueryParam("Endpoint", emailAddress.c_str());
            _rest.AddQueryParam("Protocol", "email");
            _rest.AddQueryParam("TopicArn", topicARN.c_str());

            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::SubscribeEmailAddress", _rest.lastErrorText(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.lastErrorText());
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::SubscribeEmailAddress", _rest.responseHeader(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.responseHeader());
            }

            CkXml xml;
            xml.LoadXml(responseXml);
            return xml.chilkatPath("SubscribeResult|SubscriptionArn|*"); // Usually "pending confirmation"
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::SubscribeEmailAddress", e.what(), __FILE__, __LINE__);
            return "Error: " + std::string(e.what());
        }
    }
    std::string ANSUtilities::SendMessageToTopic(const std::string& topicARN, const std::string& subjectContent, const std::string& messageContent) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::SendMessageToTopic", "Error: Invalid unlock code", __FILE__, __LINE__);
            return "Error: Invalid unlock code";
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::SendMessageToTopic", "Error: Invalid license key", __FILE__, __LINE__);
            return "Error: Invalid license key";
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "Publish");
            _rest.AddQueryParam("TopicArn", topicARN.c_str());
            _rest.AddQueryParam("Subject", subjectContent.c_str()); // Subject line for emails
            _rest.AddQueryParam("Message", messageContent.c_str()); // The message body

            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::SendMessageToTopic", _rest.lastErrorText(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.lastErrorText());
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::SendMessageToTopic", _rest.responseHeader(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.responseHeader());
            }

            CkXml xml;
            xml.LoadXml(responseXml);

            return xml.chilkatPath("PublishResult|MessageId|*"); // Return the MessageId
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::SendMessageToTopic", e.what(), __FILE__, __LINE__);
            return "Error: " + std::string(e.what());
        }
    }
    std::string ANSUtilities::SendMessageToPhoneNumber(const std::string& phoneNumber, const std::string& messageContent) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::SendMessageToPhoneNumber", "Error: Invalid unlock code", __FILE__, __LINE__);
            return "Error: Invalid unlock code";
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::SendMessageToPhoneNumber", "Error: Invalid license key", __FILE__, __LINE__);
            return "Error: Invalid license key";
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "Publish");
            _rest.AddQueryParam("PhoneNumber", phoneNumber.c_str());
            _rest.AddQueryParam("Message", messageContent.c_str());

            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::SendMessageToPhoneNumber", _rest.lastErrorText(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.lastErrorText());
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::SendMessageToPhoneNumber", _rest.responseHeader(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.responseHeader());
            }

            CkXml xml;
            xml.LoadXml(responseXml);

            return xml.chilkatPath("PublishResult|MessageId|*"); // Return the MessageId
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::SendMessageToPhoneNumber", e.what(), __FILE__, __LINE__);
            return "Error: " + std::string(e.what());
        }
    }
    std::string ANSUtilities::ListAWSSNSTopic() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::ListAWSSNSTopic", "Error: Invalid unlock code", __FILE__, __LINE__);
            return "Error: Invalid unlock code";
        }

        if (!_isLicenseValid) {
            _logger.LogFatal("ANSUtilities::ListAWSSNSTopic", "Error: Invalid license key", __FILE__, __LINE__);
            return "Error: Invalid license key";
        }

        try {
			CkRest _rest;
            _rest.AddQueryParam("Action", "ListTopics");
            const char* responseXml = _rest.fullRequestNoBody("GET", "/");
            if (!_rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::ListAWSSNSTopic", _rest.lastErrorText(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.lastErrorText());
            }

            if (_rest.get_ResponseStatusCode() != 200) {
                _logger.LogError("ANSUtilities::ListAWSSNSTopic", _rest.responseHeader(), __FILE__, __LINE__);
                return "Error: " + std::string(_rest.responseHeader());
            }

            CkXml xml;
            xml.LoadXml(responseXml);

            // Move to Topics list
            xml.chilkatPath("ListTopicsResult|Topics|$");

            int numTopics = xml.get_NumChildren();
            std::vector<std::string> topics;
            topics.reserve(numTopics); // Slight optimization

            for (int i = 0; i < numTopics; ++i) {
                xml.GetChild2(i);
                topics.emplace_back(xml.getChildContent("TopicArn"));
                xml.GetParent2();
            }

            if (topics.empty()) {
                _logger.LogError("ANSUtilities::ListAWSSNSTopic", "No topics found", __FILE__, __LINE__);
                return "";
            }

            // Concatenate topics with ';'
            std::string topicsStr;
            for (size_t i = 0; i < topics.size(); ++i) {
                topicsStr += topics[i];
                if (i + 1 != topics.size()) {
                    topicsStr += ";";
                }
            }

            return topicsStr;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::ListAWSSNSTopic", e.what(), __FILE__, __LINE__);
            return "Error: " + std::string(e.what());
        }
    }
    bool ANSUtilities::AuthenticateGCS(const std::string& jsonKeyFile) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::AuthenticateGCS", "Error: Invalid unlock code", __FILE__, __LINE__);
            return false;
        }

        if (_isAuthenticated) {
            return true;
        }

        try {
            _authGoogle.put_JsonKey(jsonKeyFile.c_str());
            _authGoogle.put_Scope("https://www.googleapis.com/auth/cloud-platform");
            _authGoogle.put_ExpireNumSeconds(3600); // 1 hour token
            _authGoogle.put_SubEmailAddress("");

            CkSocket tlsSock;
            bool success = tlsSock.Connect("www.googleapis.com", 443, true, 5000);
            if (!success) {
                _logger.LogError("ANSUtilities::AuthenticateGCS", tlsSock.lastErrorText(), __FILE__, __LINE__);
                return false;
            }

            success = _authGoogle.ObtainAccessToken(tlsSock);
            if (!success) {
                _logger.LogError("ANSUtilities::AuthenticateGCS", _authGoogle.lastErrorText(), __FILE__, __LINE__);
                return false;
            }

            if (!_authGoogle.accessToken()) {
                _logger.LogError("ANSUtilities::AuthenticateGCS", _authGoogle.lastErrorText(), __FILE__, __LINE__);
                return false;
            }

            _isAuthenticated = true;
            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::AuthenticateGCS", e.what(), __FILE__, __LINE__);
            return false;
        }
    }
    bool ANSUtilities::UploadMatToGCS(const std::string& bucketName, const std::string& objectName, const cv::Mat& image) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Error: Invalid unlock code", __FILE__, __LINE__);
            return false;
        }

        if (!_isAuthenticated) {
            _logger.LogError("ANSUtilities::UploadMatToGCS", "Not authenticated. Call AuthenticateGCS() first.", __FILE__, __LINE__);
            return false;
        }
        try {
            CkRest rest;
            CkSocket	_gcsSocket;

            std::string gcsHost = "www.googleapis.com";
            bool bTls = true;
            bool autoReconnect = true;
            int port = 443;
            int maxWaitMs = 15000;
            if (_bProxy) {
                if (!_proxyHost.empty() && (_proxyPort > 0)) {
                    _gcsSocket.put_HttpProxyHostname(_proxyHost.c_str());
                    _gcsSocket.put_HttpProxyPort(_proxyPort);
                    _gcsSocket.put_HttpProxyUsername(_proxyUsername.c_str());
                    _gcsSocket.put_HttpProxyPassword(_proxyPassword.c_str());
                    _gcsSocket.put_HttpProxyForHttp(true);

                    if (!_gcsSocket.Connect(gcsHost.c_str(), port, bTls, maxWaitMs)) {
                        _logger.LogFatal("ANSUtilities::UploadMatToGCS", _gcsSocket.lastErrorText(), __FILE__, __LINE__);
                        return false;
                    }

                    // Bind socket to rest
                    if (!rest.UseConnection(_gcsSocket, autoReconnect)) {
                        _logger.LogFatal("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__);
                        return false;
                    }
                    rest.SetAuthGoogle(_authGoogle);
                }
                else {
                    _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Invalid proxy hostname and port", __FILE__, __LINE__);
                    return false;

                }
            }
            else { // No proxy
                rest.SetAuthGoogle(_authGoogle);
                // Connect to Google Cloud Storage REST API
                if (!rest.Connect(gcsHost.c_str(), port, bTls, autoReconnect)) {
                    _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__);
                    return false;
                }
            }
            // Convert cv::Mat to JPEG binary
            std::vector<uchar> jpegBuffer;
            std::vector<int> compressionParams = { cv::IMWRITE_JPEG_QUALITY, 90 };
            if (!cv::imencode(".jpg", image, jpegBuffer, compressionParams)) {
                _logger.LogError("ANSUtilities::UploadMatToGCS", "Failed to encode cv::Mat as JPEG.", __FILE__, __LINE__);
                return false;
            }
            // Convert std::vector<uchar> to CkByteData
            CkByteData jpegBytes;
            if (!jpegBuffer.empty()) {
                jpegBytes.append2(jpegBuffer.data(), static_cast<unsigned long>(jpegBuffer.size()));
            }

            // Prepare the HTTP request to upload the file
            std::string urlPath = "/upload/storage/v1/b/" + bucketName + "/o?uploadType=media&name=" + objectName;
            rest.AddHeader("Content-Type", "image/jpeg");

            // Upload the JPEG data to GCS
            std::string jsonResponse = rest.fullRequestBinary("POST", urlPath.c_str(), jpegBytes);
            if (!rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__);
                return false;
            }

            int statusCode = rest.get_ResponseStatusCode();
            if (statusCode != 200) {
                _logger.LogError("ANSUtilities::UploadMatToGCS", rest.responseHeader(), __FILE__, __LINE__);
                return false;
            }

            _logger.LogDebug("ANSUtilities::UploadMatToGCS", "Upload successful. Status Code: " + std::to_string(statusCode), __FILE__, __LINE__);
            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::UploadMatToGCS", e.what(), __FILE__, __LINE__);
            return false;
        }
    }

    bool ANSUtilities::UploadMatToGCS(const std::string& bucketName, const std::string& objectName, unsigned char* jpeg_string, int32 bufferLength) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        if (!_isUnlockCodeValid) {
            _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Error: Invalid unlock code", __FILE__, __LINE__);
            return false;
        }

        if (!_isAuthenticated) {
            _logger.LogError("ANSUtilities::UploadMatToGCS", "Not authenticated. Call AuthenticateGCS() first.", __FILE__, __LINE__);
            return false;
        }
        if(bufferLength<=0) {
            _logger.LogError("ANSUtilities::UploadMatToGCS", "Invalid buffer length", __FILE__, __LINE__);
            return false;
        }   
        try {
            CkRest rest;
            CkSocket	_gcsSocket;
            std::string gcsHost = "www.googleapis.com";
            bool bTls = true;
            bool autoReconnect = true;
            int port = 443;
            int maxWaitMs = 15000;
            if (_bProxy) {
                if (!_proxyHost.empty() && (_proxyPort > 0)) {
                    _gcsSocket.put_HttpProxyHostname(_proxyHost.c_str());
                    _gcsSocket.put_HttpProxyPort(_proxyPort);
                    _gcsSocket.put_HttpProxyUsername(_proxyUsername.c_str());
                    _gcsSocket.put_HttpProxyPassword(_proxyPassword.c_str());
                    _gcsSocket.put_HttpProxyForHttp(true);

                    if (!_gcsSocket.Connect(gcsHost.c_str(), port, bTls, maxWaitMs)) {
                        _logger.LogFatal("ANSUtilities::UploadMatToGCS", _gcsSocket.lastErrorText(), __FILE__, __LINE__);
                        return false;
                    }
                    // Bind socket to rest
                    if (!rest.UseConnection(_gcsSocket, autoReconnect)) {
                        _logger.LogFatal("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__);
                        return false;
                    }
                    rest.SetAuthGoogle(_authGoogle);
                }
                else {
                    _logger.LogFatal("ANSUtilities::UploadMatToGCS", "Invalid proxy hostname and port", __FILE__, __LINE__);
                    return false;

                }
            }
            else { // No proxy
                rest.SetAuthGoogle(_authGoogle);
                // Connect to Google Cloud Storage REST API
                if (!rest.Connect(gcsHost.c_str(), port, bTls, autoReconnect)) {
                    _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__);
                    return false;
                }
            }
          
            // Convert std::vector<uchar> to CkByteData
            CkByteData jpegBytes;
            jpegBytes.append2(jpeg_string, static_cast<unsigned long>(bufferLength));

            // Prepare the HTTP request to upload the file
            std::string urlPath = "/upload/storage/v1/b/" + bucketName + "/o?uploadType=media&name=" + objectName;
            rest.AddHeader("Content-Type", "image/jpeg");

            // Upload the JPEG data to GCS
            std::string jsonResponse = rest.fullRequestBinary("POST", urlPath.c_str(), jpegBytes);
            if (!rest.get_LastMethodSuccess()) {
                _logger.LogError("ANSUtilities::UploadMatToGCS", rest.lastErrorText(), __FILE__, __LINE__);
                return false;
            }

            int statusCode = rest.get_ResponseStatusCode();
            if (statusCode != 200) {
                _logger.LogError("ANSUtilities::UploadMatToGCS", rest.responseHeader(), __FILE__, __LINE__);
                return false;
            }

            _logger.LogDebug("ANSUtilities::UploadMatToGCS", "Upload successful. Status Code: " + std::to_string(statusCode), __FILE__, __LINE__);
            return true;
        }
        catch (const std::exception& e) {
            _logger.LogFatal("ANSUtilities::UploadMatToGCS", e.what(), __FILE__, __LINE__);
            return false;
        }
    }
    bool ANSUtilities::SendEmail(const std::string& smtpServer, int port,
                                const std::string& userName, const std::string& password,
                                const std::string& subjectContent,
                                const std::string& bodyHTMLContent,
                                const std::string& bodyTextContent,
                                const std::string& fromEmailSender, 
                                const std::vector<std::string>& toEmails,
                                const std::vector<std::string>& ccEmails,
                                const std::vector<std::string>& bccEmails)
    
    {
        std::unique_lock<std::timed_mutex> lock(timeImageMutex, std::defer_lock);
        if (!lock.try_lock_for(std::chrono::milliseconds(45000))) {
            std::cerr << "Error: Mutex timeout in ANSUtilities::SendEmail!" << std::endl;
            return false;
        }
        if (!CheckUnlockCode_S()) {
            return false;
        }
        try {
            // The mailman object is used for sending and receiving email.
            CkMailMan mailman;
            mailman.put_SmtpHost(smtpServer.c_str());
            mailman.put_SmtpPort(port);
            mailman.put_StartTLS(true);//STARTTLS 
            // Set the SMTP login/password
            mailman.put_SmtpUsername(userName.c_str());
            mailman.put_SmtpPassword(password.c_str());

            CkEmail email;
            if (!bodyHTMLContent.empty()) {
                CkMht mht;
                mht.put_UseCids(true);
                const char* mime = 0;
                mime = mht.htmlToEML(bodyHTMLContent.c_str());
                bool setmime = email.SetFromMimeText(mime);
			    email.ConvertInlineImages();
            } 
            email.put_Subject(subjectContent.c_str());
            if (!bodyTextContent.empty())
                email.put_Body(bodyTextContent.c_str());
            email.put_From(fromEmailSender.c_str());
            for (const auto& toEmail : toEmails) {
                email.AddTo("", toEmail.c_str());
			}
            for (const auto& ccEmail : ccEmails) {
                email.AddCC("", ccEmail.c_str());
			}
            for (const auto& bccEmail : bccEmails) {
                email.AddBcc("", bccEmail.c_str());
            }
         
            bool success = mailman.SendEmail(email);
            if (success != true) {
                return false;
            }

            success = mailman.CloseSmtpConnection();
            if (success != true) {
            }
            return success;
        }
        catch (const std::exception& e) {
            return false;
        }
    }
    bool ANSUtilities::CheckUnlockCode_S() {
        static std::once_flag unlockOnceFlag;
        static bool isUnlockValid = false;
        try {
            std::call_once(unlockOnceFlag, []() {
                CkGlobal glob;
                const std::string unlockCode = "ANSDRC.CB1122026_MEQCIFwO1IFQCG0BhZwsXFO68QUU6mDB5uge4duOsqOJanEyAiAB67ahqnXin4SRy0vIegISgbFlpldmbuS5gbU21GYVqA==";// "ANSDRC.CB1082025_Ax6P3M7F8B3d";
                if (glob.UnlockBundle(unlockCode.c_str()) && glob.get_UnlockStatus() == 2) {
                    isUnlockValid = true;
                }
                });
            return isUnlockValid;
        }
        catch (...) {
            return false;
        }
    }
    std::string ANSUtilities::AESEncryption(const std::string& inputString, const std::string& inputKey) {
        if (!CheckUnlockCode_S()) {
            return "";
        }
        try {
            CkCrypt2 crypt;
            // AES is also known as Rijndael.
            crypt.put_CryptAlgorithm("aes");
            // CipherMode may be "ecb", "cbc", "ofb", "cfb", "gcm", etc.
            crypt.put_CipherMode("cbc");
            // KeyLength may be 128, 192, 256
            crypt.put_KeyLength(256);
            // The padding scheme determines the contents of the bytes
            // that are added to pad the result to a multiple of the
            // encryption algorithm's block size. AES has a block
            // size of 16 bytes, so encrypted output is always
            // a multiple of 16.
            crypt.put_PaddingScheme(0); // 0 = RFC 1423 padding scheme
            // EncodingMode specifies the encoding of the output for
            // encryption, and the input for decryption.
            // It may be "hex", "url", "base64", or "quoted-printable".
            crypt.put_EncodingMode("base64");

            // Ensure inputKey is exactly 16 characters for IV.
            std::string ivKey = inputKey.substr(0, std::min<size_t>(16, inputKey.size()));
            if (ivKey.size() < 16) {
                ivKey.append(16 - ivKey.size(), 'A'); // Pad with spaces if less than 16 characters
            }
            crypt.SetEncodedIV(ivKey.c_str(), "ascii");

            // The secret key must equal the size of the key. For
            // 256-bit encryption, the binary secret key is 32 bytes.
            // For 128-bit encryption, the binary secret key is 16 bytes.
            std::string secreteKey = inputKey.substr(0, std::min<size_t>(32, inputKey.size()));
            if (secreteKey.size() < 32) {
                secreteKey.append(32 - secreteKey.size(), 'A'); // Pad with spaces if less than 32 characters
            }
            crypt.SetEncodedKey(secreteKey.c_str(), "ascii");
            const char* encStr = crypt.encryptStringENC(inputString.c_str());
            return encStr ? std::string(encStr) : std::string();
        }
        catch (const std::exception& e) {
            // Optional: If you want logging, consider static logging in static functions
            return "";
        }    
    }
    std::string ANSUtilities::AESDecryption(const std::string& encryptString, const std::string& inputKey) {
        if (!CheckUnlockCode_S()) {
            return "";
        }
        try {
            CkCrypt2 crypt;
            // AES is also known as Rijndael.
            crypt.put_CryptAlgorithm("aes");
            // CipherMode may be "ecb", "cbc", "ofb", "cfb", "gcm", etc.
            crypt.put_CipherMode("cbc");
            // KeyLength may be 128, 192, 256
            crypt.put_KeyLength(256);
            // The padding scheme determines the contents of the bytes
            // that are added to pad the result to a multiple of the
            // encryption algorithm's block size. AES has a block
            // size of 16 bytes, so encrypted output is always
            // a multiple of 16.
            crypt.put_PaddingScheme(0); // 0 = RFC 1423 padding scheme
            // EncodingMode specifies the encoding of the output for
            // encryption, and the input for decryption.
            // It may be "hex", "url", "base64", or "quoted-printable".
            crypt.put_EncodingMode("base64");

            // Ensure inputKey is exactly 16 characters for IV.
            std::string ivKey = inputKey.substr(0, std::min<size_t>(16, inputKey.size()));
            if (ivKey.size() < 16) {
                ivKey.append(16 - ivKey.size(), 'A'); // Pad with spaces if less than 16 characters
            }
            crypt.SetEncodedIV(ivKey.c_str(), "ascii");

            // The secret key must equal the size of the key. For
            // 256-bit encryption, the binary secret key is 32 bytes.
            // For 128-bit encryption, the binary secret key is 16 bytes.
            std::string secreteKey = inputKey.substr(0, std::min<size_t>(32, inputKey.size()));
            if (secreteKey.size() < 32) {
                secreteKey.append(32 - secreteKey.size(), 'A'); // Pad with spaces if less than 32 characters
            }
            crypt.SetEncodedKey(secreteKey.c_str(), "ascii");
            const char* decStr = crypt.decryptStringENC(encryptString.c_str());
            return decStr ? std::string(decStr) : std::string();
        }
        catch (const std::exception& e) {
            // Optional: If you want logging, consider static logging in static functions
            return "";
        }
    }
    std::string ANSUtilities::MD5HashFile(const std::string& inputFilePath) {
        if (!CheckUnlockCode_S()) {
            return "";
        }
        try {
            CkCrypt2 crypt;

            // Set the name of the hash algorithm.
            // Other choices include "sha1", "sha256", "sha384", "sha512", "md2", "md5", and "haval". 
            crypt.put_HashAlgorithm("md5");

            // EncodingMode specifies the encoding of the hash output.
            // It may be "hex", "url", "base64", or "quoted-printable".
            crypt.put_EncodingMode("hex");

            // Files of any type may be hashed -- it doesn't matter
            // if the file is binary or text...
            const char* hashStr = nullptr;
            hashStr = crypt.hashFileENC(inputFilePath.c_str());
            return hashStr ? std::string(hashStr) : std::string();
        }
        catch (const std::exception& e) {
            // Optional: If you want logging, consider static logging in static functions
            return "";
        }
    }
    bool ANSUtilities::RestartPC() {
        try {
            return system("shutdown /r /t 0") == 0;
        }
        catch (...) {
            return false;
        }
    }
}

