#ifndef ANSULT_H
#define ANSULT_H
#define ANSULT_API __declspec(dllexport)
#pragma once
#include "LabVIEWHeader/extcode.h"
#include <map>
#include <string>
#include "ANSLicense.h"
#include <CkRest.h>
#include "CkAuthGoogle.h"
#include "CkByteData.h"
#include <CkAuthAws.h>
#include <CkXml.h>
#include <CkCrypt2.h>
#include <CkSocket.h>
#include <opencv2/opencv.hpp>
#include <thread>
#include <atomic>

namespace ANSCENTER {
	class ANSULT_API ANSUtilities {
		private:
			// Protects all shared mutable data
			mutable std::recursive_mutex _mutex;
			bool _isUnlockCodeValid{ false};
			bool _isLicenseValid{ false};
			std::string _licenseKey;
			std::string _unlockCode;
			std::string _proxyHost;
			int _proxyPort{ 0 };
			std::string _proxyUsername;
			std::string _proxyPassword;
			bool _bProxy{ false };

			std::string _fireBaseSessionToken;

			bool _bTls{ true };
			int  _port{ 443 };
			bool _bAutoReconnect{ true };
			bool _isAWSConnected{ false };
	

			// Google Cloud Storage Authentication
			CkAuthGoogle _authGoogle;
			bool _isAuthenticated{ false };

			SPDLogger& _logger = SPDLogger::GetInstance("ANSUtilities", false);


			std::chrono::system_clock::time_point tokenGeneratedTime;
			bool tokenInitialized = false;
			static const int TOKEN_EXPIRY_SECONDS = 3600;
		public:
			ANSUtilities();
			~ANSUtilities();
			[[nodiscard]] bool Initialize(const std::string& licenseKey);
			void CheckLicense();
			void CheckUnlockCode();
			[[nodiscard]] bool SetServerProxy(const std::string& proxyHost, int proxyPort, const std::string& proxyUsername, const std::string& proxyPassword);

			[[nodiscard]] std::string GetFirebaseCloudMessageAcccessToken(std::string privateKey);
			//AWS SNS Functions
			[[nodiscard]] std::string CreateAWSSNSTopic(const std::string& topicName); // Will return the ARN of the topic
			[[nodiscard]] bool DeleteAWSSNSTopic(const std::string& topicARN); // Will return the ARN of the topic
			[[nodiscard]] std::string ListAWSSNSTopic(); // Will return the list ARN of the topic in string with ; concatenated
			[[nodiscard]] std::string SubcribeSMSPhoneNumber(const std::string& topicARN, const std::string& phoneNumber);
			[[nodiscard]] std::string SubscribeEmailAddress(const std::string& topicARN, const std::string& emailAddress);
			[[nodiscard]] std::string SendMessageToTopic(const std::string& topicARN, const std::string& subjectContent, const std::string& messageContent);// Return the message ID
			[[nodiscard]] std::string SendMessageToPhoneNumber(const std::string& phoneNumber, const std::string& messageContent);// Return the message ID
			// Google Cloud Storage Upload using Chilkat
			[[nodiscard]] bool AuthenticateGCS(const std::string& jsonKeyString);
			[[nodiscard]] bool UploadMatToGCS(const std::string& bucketName, const std::string& objectName, const cv::Mat& image);
			[[nodiscard]] bool UploadMatToGCS(const std::string& bucketName, const std::string& objectName, unsigned char* jpeg_string, int32 bufferLength);

			[[nodiscard]] static bool CheckUnlockCode_S();
			[[nodiscard]] static std::string AESEncryption(const std::string& inputString, const std::string& inputKey);
			[[nodiscard]] static std::string AESDecryption(const std::string& encryptString, const std::string& inputKey);
			[[nodiscard]] static std::string MD5HashFile(const std::string& inputFilePath);
			// SMTP for office360 email sending
			[[nodiscard]] static bool 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);

			// Restart PC
			[[nodiscard]] static bool RestartPC();
		};
	// Connection bundle for pool
	struct S3Connection {
		CkRest    rest;
		CkSocket  socket;
		CkAuthAws authAws;
	};

	class ANSULT_API ANSAWSS3 {
		private:
			bool _isUnlockCodeValid{ false };
			bool _isLicenseValid{ false };
			bool _bConnected{ false };
			bool _bAwsPath{ true };		// true = virtual-hosted (AWS), false = path-style (MinIO)
			std::string _licenseKey;
			std::string _unlockCode;

			// ── Connection pool (replaces single _rest/_socket/_authAws) ──
			std::mutex _poolMutex;
			std::vector<std::unique_ptr<S3Connection>> _pool;

			// ── Config (read-only after setup, protected by _configMutex) ──
			std::mutex  _configMutex;
			std::string	_fullAWSURL;		// AWS S3 service URL
			bool	_bTls{ true };
			int		_port{ 443 };
			bool	_bAutoReconnect{ true };

			std::string _serviceName;
			std::string _bucketRegion;
			std::string _baseDomain;
			std::string _accessKey;
			std::string _secretKey;
			bool _authReady{ false };

			// Proxy settings
			std::string _proxyHost;
			int _proxyPort{ 0 };
			std::string _proxyUsername;
			std::string _proxyPassword;
			bool _bProxy{ false };
			SPDLogger& _logger = SPDLogger::GetInstance("ANSAWSS3", false);

			// Pool helpers
			std::unique_ptr<S3Connection> CreateConnection();
			std::unique_ptr<S3Connection> AcquireConnection();
			void ReleaseConnection(std::unique_ptr<S3Connection> conn);

			std::string ExtractFileName(const std::string& filePath);
			std::string GetContentType(const std::string& filePath);

			// Background retry
			std::thread         _retryThread;
			std::atomic<bool>   _stopRetry{ false };
			std::atomic<bool>   _retryInProgress{ false };
			void StopRetry();
			void RetryLoop();
			bool TryConnect(bool& awsPath);

		public:
			ANSAWSS3();
			~ANSAWSS3();
			[[nodiscard]] bool Initialize(const std::string& licenseKey);
			void CheckLicense();
			void CheckUnlockCode();
			[[nodiscard]] bool SetServerProxy(const std::string& proxyHost, int proxyPort, const std::string& proxyUsername, const std::string& proxyPassword);
			// Returns: 1 = connected, 0 = failed (bad auth/URL), 2 = no internet (background retry started)
			[[nodiscard]] int Connect(const std::string& baseDomain, const std::string& bucketRegion, const std::string& serviceName, int port, bool bTls, bool autoReconnect, bool& awsPath);
			[[nodiscard]] bool SetAuthentication(const std::string& accessKey, const std::string& secretKey);
			[[nodiscard]] std::vector<std::string> ListBuckets();
			[[nodiscard]] std::vector<std::string> ListBucketObjects(const std::string& bucketName);
			[[nodiscard]] std::vector<std::string> ListBucketObjectsWithPrefix(const std::string& bucketName, const std::string& prefix);
			[[nodiscard]] bool CreateBucket(const std::string& bucketName);
			[[nodiscard]] bool DeleteBucket(const std::string& bucketName);

			[[nodiscard]] bool CreateFolder(const std::string& bucketName, const std::string& prefix);
			[[nodiscard]] bool DeleteFolder(const std::string& bucketName, const std::string& prefix);
			[[nodiscard]] bool DeleteBucketObject(const std::string& bucketName, const std::string& objectName);
			[[nodiscard]] std::string GetBucketRegion(const std::string& bucketName);

			// Upload data to AWS S3
			[[nodiscard]] bool UploadTextData(const std::string& bucketName, const std::string &textFilePath, std::string& uploadedFilePath);
			[[nodiscard]] bool UploadFileStream(const std::string& bucketName, const std::string& dataFilePath, std::string& uploadedFilePath);

			[[nodiscard]] bool UploadBinaryData(const std::string& bucketName, const std::string& dataFilePath, std::string& uploadedFilePath);
			[[nodiscard]] bool UploadPrefixBinaryData(const std::string& bucketName, const std::string& prefix, const std::string& dataFilePath, const std::string& objectName, std::string& uploadedFilePath);

			[[nodiscard]] bool UploadMultipartData(const std::string& bucketName, const std::string& dataFilePath, std::string& uploadedFilePath, int partSize=5242880);
			[[nodiscard]] bool UploadPrefixMultipartData(const std::string& bucketName, const std::string& prefix, const std::string& dataFilePath, const std::string& fileName, std::string& uploadedFilePath, int partSize= 5242880);

			[[nodiscard]] bool UploadPrefixJpegImage(const std::string& bucketName, const std::string& prefix, unsigned char* jpeg_string, int32 bufferLength, const std::string& fileName, std::string& uploadedFilePath);
			[[nodiscard]] bool UploadJpegImage(const std::string& bucketName, unsigned char* jpeg_string, int32 bufferLength, const std::string& fileName, std::string& uploadedFilePath);
			[[nodiscard]] bool DownloadFile(const std::string& bucketName, const std::string& objectName, const std::string& saveFilePath);

	};

}

extern "C" ANSULT_API int  CreateANSUtilityHandle(ANSCENTER::ANSUtilities** Handle, const char* licenseKey);
extern "C" ANSULT_API int  ReleaseANSUtilityHandle(ANSCENTER::ANSUtilities** Handle);
extern "C" ANSULT_API int  GetFCMAccessToken(ANSCENTER::ANSUtilities** Handle, const char* privateKey, LStrHandle accessToken);
extern "C" ANSULT_API int  CreateAWSSNSTopic(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, LStrHandle arnTopic);
extern "C" ANSULT_API int  DeleteAWSSNSTopic(ANSCENTER::ANSUtilities** Handle, const char* arnTopic);
extern "C" ANSULT_API int  ListASWTopics(ANSCENTER::ANSUtilities** Handle, LStrHandle arnTopics);
extern "C" ANSULT_API int  AESEncryption(const char* inputString, const char* inputKey, LStrHandle encryptionMessage);
extern "C" ANSULT_API int  AESDecryption(const char* encryptedString, const char* inputKey, LStrHandle decryptionMessage);
extern "C" ANSULT_API int  MD5HashFile(const char* filePath, LStrHandle decryptionMessage);

extern "C" ANSULT_API int  SubcribeSMSPhoneNumberAWSSNSTopic(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, const char* phoneNumber, LStrHandle subscribedARN);
extern "C" ANSULT_API int  SubcribeEmailAddressAWSSNSTopic(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, const char* emailAddress, LStrHandle subscribedARN);
extern "C" ANSULT_API int  SendMessageToAWSSNSTopic(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, const char* subjectContent,const char * messageContent, LStrHandle messageId);
extern "C" ANSULT_API int  SendMessageToPhoneNumber(ANSCENTER::ANSUtilities** Handle, const char* phoneNumber,  const char* messageContent, LStrHandle messageId);

extern "C" ANSULT_API int  GetFCMAccessTokenCpp(ANSCENTER::ANSUtilities** Handle, const char* privateKey, std::string &accessToken);
extern "C" ANSULT_API int  CreateAWSSNSTopicCpp(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, std::string& arnTopic);
extern "C" ANSULT_API int  SubcribeSMSPhoneNumberAWSSNSTopicCpp(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, const char* phoneNumber, std::string& subscribedARN);
extern "C" ANSULT_API int  SubcribeEmailAddressAWSSNSTopicCpp(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, const char* emailAddress, std::string& subscribedARN);
extern "C" ANSULT_API int  SendMessageToAWSSNSTopicCpp(ANSCENTER::ANSUtilities** Handle, const char* snsTopicName, const char* subjectContent, const char* messageContent, std::string& messageId);
extern "C" ANSULT_API int  SendMessageToPhoneNumberCpp(ANSCENTER::ANSUtilities** Handle, const char* phoneNumber, const char* messageContent, std::string& messageId);
extern "C" ANSULT_API int  ListASWTopicsCpp(ANSCENTER::ANSUtilities** Handle, std::string& arnTopics);

// Static functions
extern "C" ANSULT_API int  AESEncryptionCpp(const char* inputString, const char* inputKey, std::string& encryptedString);
extern "C" ANSULT_API int  AESDecryptionCpp(const char* encryptedString, const char* inputKey, std::string& decryptionMessage);

// Google Cloud Storage
extern "C" ANSULT_API int  SetupServerProxy(ANSCENTER::ANSUtilities** Handle, const char* hostName, int port, const char* userName, const char* passWord);
extern "C" ANSULT_API int  AuthenticateGCS(ANSCENTER::ANSUtilities** Handle, const char* jsonKeyString);
extern "C" ANSULT_API int  UploadMatToGCS(ANSCENTER::ANSUtilities** Handle, const char* bucketName, const char* objectName, unsigned char* jpeg_string, int32 bufferLength);
extern "C" ANSULT_API int  UploadImageToGCS(ANSCENTER::ANSUtilities** Handle, const char* bucketName, const char* objectName, cv::Mat image);
extern "C" ANSULT_API int  SendEmail(const char* smtpServer, int port,
									const char* userName, const char* password,
									const char* title, const char* bodyHTMLContent,
									const char* bodyTextContent,
									const char* fromEmailSender, 
									const char* toEmails,
									const char* ccEmails,
									const char* bccEmails);

extern "C" ANSULT_API int  RebootSystem();


// AWS S3 class

extern "C" ANSULT_API int  CreateANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* licenseKey);
extern "C" ANSULT_API int  ReleaseANSAWSHandle(ANSCENTER::ANSAWSS3** Handle);

extern "C" ANSULT_API int  ConnectANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* baseDomain, const char* bucketRegion, const char* serviceName, int port, int bTls, int autoReconnect, int* awsPath);
extern "C" ANSULT_API int  SetProxyANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* proxyHost, int proxyPort, const char* proxyUsername, const char* proxyPassword);
extern "C" ANSULT_API int  SetAuthenticationANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* accessKey, const char* secretKey);

extern "C" ANSULT_API int  ListBucketANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, std::string &bucketList);
extern "C" ANSULT_API int  ListBucketObjectsANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, std::string& bucketNameList);
extern "C" ANSULT_API int  ListBucketObjectsWithPrefixANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, std::string& bucketNameList);
extern "C" ANSULT_API int  GetRegionANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, std::string& region);

extern "C" ANSULT_API int  ListBucketANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, LStrHandle bucketList);
extern "C" ANSULT_API int  ListBucketObjectsANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, LStrHandle bucketNameList);
extern "C" ANSULT_API int  ListBucketObjectsWithPrefixANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, LStrHandle bucketNameList);

extern "C" ANSULT_API int  GetRegionANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, LStrHandle region);
extern "C" ANSULT_API int  CreateBucketANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName);
extern "C" ANSULT_API int  DeleteBucketANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName);
extern "C" ANSULT_API int  CreateBucketPrefixANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char *prefix);
extern "C" ANSULT_API int  DeleteBucketPrefixANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix);
extern "C" ANSULT_API int  DeleteBucketObjectANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* bucketObject);

extern "C" ANSULT_API int  UploadTextDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* textDataPath, LStrHandle uploadedFilePath);
extern "C" ANSULT_API int  UploadBinaryDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* binaryDataPath, LStrHandle uploadedFilePath);
extern "C" ANSULT_API int  UploadFileStreamDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, LStrHandle uploadedFilePath);
extern "C" ANSULT_API int  UploadMultiPartDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, int fileSize, LStrHandle uploadedFilePath);
extern "C" ANSULT_API int  UploadJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, LStrHandle uploadedFilePath);

extern "C" ANSULT_API int  UploadPrefixBinaryDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* binaryDataPath, const char* objectName, LStrHandle uploadedFilePath);
extern "C" ANSULT_API int  UploadPrefixMultiPartDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* fileDataPath, const char* objectName, int fileSize, LStrHandle uploadedFilePath);
extern "C" ANSULT_API int  UploadPrefixJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, LStrHandle uploadedFilePath);

extern "C" ANSULT_API int  DownloadFileStreamANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* objectName, const char* savedFilePath);

// C++ test APIs (use std::string& instead of LStrHandle)
extern "C" ANSULT_API int  UploadTextDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* textDataPath, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* binaryDataPath, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadFileStreamDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, int fileSize, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadPrefixBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* binaryDataPath, const char* objectName, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadPrefixMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* fileDataPath, const char* objectName, int fileSize, std::string& uploadedFilePath);
extern "C" ANSULT_API int  UploadPrefixJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, std::string& uploadedFilePath);


#endif
