#include "LogUploader.h"
#include "HttpModule.h"
#include "tracer.pb.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Tools/CsdkCommonTools.h"


bool FLogUploader::Init()
{
	return true;
}

uint32 FLogUploader::Run()
{

	TArray<int32> Temp;
	while (!IsStopRequested())
	{
		if (!LogQueue.IsEmpty())
		{
			TArray<FLogItem> LogBatch;
			for (int32 i = 0; i < 20 && !LogQueue.IsEmpty() ; ++i)
			{
				FLogItem LogMessage;
				LogQueue.Dequeue(LogMessage);

				LogBatch.Add(LogMessage);
			}
			UploadLogs(LogBatch);
			FPlatformProcess::Sleep(1.0f);
		}
	}
	return 0;
}

void FLogUploader::Stop()
{
	FPlatformAtomics::InterlockedExchange(&StopTaskCounter, 1);
}

bool FLogUploader::IsStopRequested() const {
	return FPlatformAtomics::AtomicRead(&StopTaskCounter) != 0;
}

void FLogUploader::Exit()
{
}

logtracer::client::v1::LogLevel FLogUploader::ConvertVerbosity(const ELogVerbosity::Type Type)
{
	switch (Type)
	{
	case ELogVerbosity::Verbose:
		return logtracer::client::v1::LEVEL_INFO;
	case ELogVerbosity::Warning:
		return logtracer::client::v1::LEVEL_WARN;
	case ELogVerbosity::Error:
		return logtracer::client::v1::LEVEL_ERROR;
	case ELogVerbosity::Fatal:
		return logtracer::client::v1::LEVEL_FATAL;
	default:
		return logtracer::client::v1::LEVEL_DEBUG;
	}
}


void FLogUploader::UploadLogs(const TArray<FLogItem>& Logs)
{
	const TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
	HttpRequest->SetVerb("POST");
	HttpRequest->SetURL(TEXT("http://csdk-test.yingxiong.com/logtracer/api/client/v1/log/report"));

	HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-protobuf"));
	HttpRequest->SetHeader(TEXT("Accept"), TEXT("application/x-protobuf"));

	const FString Nonce = FGuid::NewGuid().ToString();
	HttpRequest->SetHeader(TEXT("nonce"), *Nonce);

	const FDateTime Time = FDateTime::Now();
	const int64 TimeStamp = Time.ToUnixTimestamp() * 1000;
	HttpRequest->SetHeader(TEXT("timestamp"), FString::Printf(TEXT("%lld"), TimeStamp));

	const FString SignMid = FString::Printf(TEXT("nonce=%s&timestamp=%lld&%s"), *Nonce,
	                                        TimeStamp,
	                                        TEXT("r7DLMwsEsLmLUptZ"));
	const FString Sign = FMD5::HashAnsiString(*SignMid);
	HttpRequest->SetHeader(TEXT("sign"), *Sign);

	logtracer::client::v1::LogReportRequest ReportRequest;
	
	const FString UserId = FCsdkCommonTools::GetUserManager().GetCurrentUser().ToString();
	const FString Id = DeviceUniqueId + "_" + UserId;
	ReportRequest.set_device_id(TCHAR_TO_UTF8(*Id));
	for (const auto& Log : Logs)
	{
		logtracer::client::v1::LogReportItem* ReportItem = new logtracer::client::v1::LogReportItem;
		const int64 Value = FDateTime::Now().ToUnixTimestamp() * 1000;
		ReportItem->set_timestamp(Value);
		ReportItem->set_account_id("UNREAL ENGINE");
		ReportItem->set_level(ConvertVerbosity(Log.Verbosity));
		ReportItem->set_log_type(TCHAR_TO_UTF8(*Log.Category.ToString()));
		ReportItem->set_content(TCHAR_TO_UTF8(*Log.Data));
		ReportRequest.mutable_items()->AddAllocated(ReportItem);
	}
	TArray<uint8> Buffer;
	Buffer.SetNum(ReportRequest.ByteSizeLong());
	ReportRequest.SerializeToArray(Buffer.GetData(), Buffer.Num());
	HttpRequest->SetContent(Buffer);

	HttpRequest->OnProcessRequestComplete().BindLambda(
		[this](FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
		{
			if (bSucceeded && HttpResponse.IsValid() && HttpResponse->GetResponseCode() == 200)
			{
				UE_LOG(LogTemp, Log, TEXT("Log Uploaded."));
			}
			else
			{
				UE_LOG(LogTemp, Error, TEXT("Failed to upload log."));
			}
		});

	HttpRequest->ProcessRequest();
}
