#pragma once
#include "CsdkCommonTools.h"
#include "Manager/CsdkSubsystem.h"
#include "HttpModule.h"
#include "herogoogle/protobuf/message_lite.h"
#include "Interfaces/IHttpResponse.h"
#include "Parser/ApiResult.h"
#include "Log/CsdkLogMacros.h"
#include "Log/LogChatSdk.h"
#include "Common/CsdkErrorCode.h"

class FCsdkCommonHttpBase;



FORCEINLINE TMap<FString,TSharedPtr<FCsdkCommonHttpBase>>& GetCommonHttpBaseMap()
{
	static TMap<FString,TSharedPtr<FCsdkCommonHttpBase>> CommonHttpBaseMap;
	return CommonHttpBaseMap;
}

class FCsdkCommonHttpBase
{
public:
	virtual ~FCsdkCommonHttpBase(){}
protected:
	explicit FCsdkCommonHttpBase(const FString& InGuid,const FString& InUrl,const ::herogoogle::protobuf::MessageLite& PbRequest)
	: Guid(InGuid)
	,Url(InUrl)
	{
		if(!FCsdkCommonTools::IsAlive() )
		{
			return;
		}
		
		const auto HttpRequest = FHttpModule::Get().CreateRequest();
		HttpRequest->SetURL(FCsdkCommonTools::GetHttpAddress()+ Url);
		HttpRequest->SetVerb(TEXT("POST"));
		FCsdkCommonTools::HttpV2SetDefaultHeader(HttpRequest);
		HttpRequest->SetContent(FCsdkCommonTools::ConvertPbStructToBytes(PbRequest));
		HttpRequest->OnProcessRequestComplete().BindRaw(this,&FCsdkCommonHttpBase::OnHttpResponse);
		HttpRequest->ProcessRequest();
		CSDK_LOG(LogChatSdk,Log,TEXT("Start Request:%s"),*Url);
	}

	void OnHttpResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully)
	{
		if(FCsdkCommonTools::IsAlive() && Response)
		{
			Parse(Response,bConnectedSuccessfully);
		}
		else
		{
			CSDK_LOG(LogChatSdk,Log,TEXT("%s Response Failed no Guild exist "),*Url);
			GetCommonHttpBaseMap().Remove(Guid);
		}
	}
	
	virtual void Parse(const FHttpResponsePtr& Response, bool bConnectedSuccessfully) = 0;
protected:
	FString Guid;
	FString Url;

};

template<typename ResponseType,typename... VarTypes>
class FCsdkCommonHttp :public FCsdkCommonHttpBase
{
	explicit FCsdkCommonHttp(const FString& InGuid,const FString& InUrl,const herogoogle::protobuf::MessageLite& PbRequest,VarTypes... Vars,
		TFunction<void(const int32 ErrorCode,const FString& ErrorMsg, const ResponseType& PbResponse,VarTypes... Params)>  InCallBack)
	:FCsdkCommonHttpBase(InGuid,InUrl,PbRequest)
	,Payload(Vars...)
	,CallBack(InCallBack)
	{
	}
public:
	
	static void Request(const FString& InUrl,const ::herogoogle::protobuf::Message& PbRequest,
		VarTypes... Vars,
		TFunction<void(const int32 ErrorCode,const FString& ErrorMsg, const ResponseType& PbResponse,VarTypes... Params)>  InCallBack
		)
	{
		const FString Guid = FGuid::NewGuid().ToString();
		
		const FString Request = FCsdkCommonTools::ParsePbToJson(PbRequest);
		CSDK_LOG(LogChatSdk,Log,TEXT("Csdk Request-->Guid: %s,  Url: %s,  Content: %s"),*Guid, *InUrl,*Request);
		GetCommonHttpBaseMap().Add(Guid,MakeShareable( new FCsdkCommonHttp{Guid,InUrl,PbRequest,Vars...,InCallBack}));
	}

private:
	virtual void Parse(const FHttpResponsePtr& Response, bool bConnectedSuccessfully) override
	{
		
		ResponseType PbResult;
		TApiResult<ResponseType> Result;
		if(!bConnectedSuccessfully || Response->GetResponseCode()!=200)
		{
			Result.Msg =FString::Printf(TEXT("Request Failed, can't connect to Sever, bConnectedSuccessfully:%s, ResponseCode:%d"),
				bConnectedSuccessfully ? TEXT("true") : TEXT("false"), Response->GetResponseCode());
		}
		else
		{
			Result =TApiResult<ResponseType>::ParseAPIResult(Response->GetContent());
		}
		if(Result.Code == ECsdkErrorCode::Success)
		{
			CSDK_LOG(LogChatSdk,Log,TEXT("Csdk Response-->Guid: %s, Url: %s"),*Guid,*Url);
		}
		else
		{
			CSDK_LOG(LogChatSdk,Error,TEXT("Csdk Response-->Guid: %s, Url: %s"),*Guid,*Url);
		}
		
		auto l = [this,Result]()
		{
			if (Result.Code == ECsdkErrorCode::AuthExpired)
			{
				FCsdkManager::GetInstance().Subsystem->OnKickOff.Broadcast();
				FCsdkCommonTools::GetLoginManager().Logout();
				CSDK_LOG(LogChatSdk,Error,TEXT("Caution! Logout, AuthExpired"));
			}
			Payload.ApplyAfter(CallBack,Result.Code,Result.Msg,Result.EmbedData);
			GetCommonHttpBaseMap().Remove(Guid);
		};

		if (IsInGameThread())
		{
			CSDK_LOG(LogChatSdk, Log, TEXT("CSDK Http Response in Game thread"));
			l();
		}
		else
		{
		
			CSDK_LOG(LogChatSdk, Warning, TEXT("CSDK Http Response not in Game thread"));

			AsyncTask(ENamedThreads::GameThread, [l]()
			{
				if(FCsdkCommonTools::IsAlive())
				{
					l();
				}
			});			
		}
	}
private:
	TTuple<VarTypes...> Payload;
	TFunction<void(const int32 ErrorCode,const FString& ErrorMsg, const ResponseType& PbResponse,VarTypes... Params)> CallBack;
};
