#include "ChatRoomManager.h"
#include "ChatRoom/Tool/ChatRoomTool.h"
#include "group/chatroom_dto.pb.h"
#include "Log/CsdkLogMacros.h"
#include "Log/LogChatSdk.h"
#include "Session/CsdkSessionLibrary.h"
#include "Tools/CsdkCommonHttp.h"
#include "Tools/CsdkCommonTools.h"
#include "User/Data/CsdkUserOperateResult.h"
#include "User/Tool/UserTool.h"
#include "Async/TaskGraphInterfaces.h"
#include "Async/Async.h"

using namespace heroim::client::v3beta::dto::group;

void FChatRoomManager::Reset()
{
	ChatRoomMap.Empty();
}

void FChatRoomManager::CreateChatRoom(const FCsdkChatRoomId& UniqueId,const FCsdkChatRoomCreateParam& CreateParam,
                                      const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg,const FCsdkChatRoomInfo& ChatRoomInfo)>& Callback)
{
	ChatRoomCreateRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(UniqueId));
	Request.mutable_options()->set_grouptype(FSTRING_TO_STD_STRING(CreateParam.RoomType));
	Request.mutable_options()->set_title(FSTRING_TO_STD_STRING(CreateParam.RoomName));
	Request.mutable_options()->set_iconurl(FSTRING_TO_STD_STRING(CreateParam.Icon));
	
	for(const auto& ExtraItem:CreateParam.Extra)
	{
		Request.mutable_options()->mutable_extra()->insert({FSTRING_TO_STD_STRING(ExtraItem.Key),FSTRING_TO_STD_STRING(ExtraItem.Value)});
	}

	auto HttpCallback =[this,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomCreateResponse& Resp)
	{
		FCsdkChatRoomInfo RoomInfo;
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			RoomInfo = CsdkChatRoomTool::ConvertRoomInfo(Resp.group());
			//查找或者创建会话
			FCsdkCommonTools::GetSessionManager().CreateOrUpdateSession(UCsdkSessionLibrary::Conv_RoomIdToSessionId(RoomInfo.RoomId));
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("CreateChatRoom:%s,ErrorCode:%d"),*RoomInfo.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg,RoomInfo);
	};
	FCsdkCommonHttp<ChatRoomCreateResponse>::Request(TEXT("/spi/v3beta/chatroom/create"),Request,HttpCallback);
}

void FChatRoomManager::JoinChatRoom(const FCsdkChatRoomId& Id,
                                    const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg,const FCsdkChatRoomInfo& ChatRoomInfo)>& Callback)
{
	if(const auto ChatRoomPtr = ChatRoomMap.Find(Id))
	{
		const auto  ChatRoomInfo = *ChatRoomPtr;
		AsyncTask(ENamedThreads::GameThread,[ChatRoomInfo,Callback]()
		{
			if(Callback)
			{
				CSDK_LOG(LogChatSdk,Log,TEXT("Already Has Chatroom"));
				Callback(ECsdkErrorCode::Success,TEXT("Already Has Chatroom"),ChatRoomInfo);
			}
		});
		return;
	}
	JoinChatRoomRemote(Id,Callback);
}

void FChatRoomManager::JoinAutoCreateChatRoom(const FCsdkChatRoomId& Id,const FCsdkChatRoomCreateParam& Param,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const FCsdkChatRoomInfo& ChatRoomInfo)>&
	Callback)
{
	if(const auto ChatRoomPtr = ChatRoomMap.Find(Id))
	{
		const auto  ChatRoomInfo = *ChatRoomPtr;
		AsyncTask(ENamedThreads::GameThread,[ChatRoomInfo,Callback]()
		{
			if(Callback)
			{
				CSDK_LOG(LogChatSdk,Log,TEXT("Already Has Chatroom"));
				Callback(ECsdkErrorCode::Success,TEXT("Already Has Chatroom"),ChatRoomInfo);
			}
		});
		return;
	}
	JoinAutoCreateChatRoomRemote(Id,Param,Callback);
}

void FChatRoomManager::QuitChatRoom(const FCsdkChatRoomId& Id, 
                                    const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg)>& Callback)
{
	ChatRoomQuitRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(Id));

	auto HttpCallback =[this,Id,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomQuitResponse& Resp)
	{
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			ChatRoomMap.Remove(Id);
			//移除会话
			FCsdkCommonTools::GetSessionManager().RemoveLocalSession(UCsdkSessionLibrary::Conv_RoomIdToSessionId(Id));
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Quit ChatRoom:%s,ErrorCode:%d"),*Id.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg);
	};
	FCsdkCommonHttp<ChatRoomQuitResponse>::Request(TEXT("/spi/v3beta/chatroom/quit"),Request,HttpCallback);
}

void FChatRoomManager::DissolveChatRoom(const FCsdkChatRoomId& Id,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg)>& Callback)
{
	ChatRoomDismissRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(Id));

	auto HttpCallback =[this,Id,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomDismissResponse& Resp)
	{
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			ChatRoomMap.Remove(Id);
			//移除会话
			FCsdkCommonTools::GetSessionManager().RemoveLocalSession(UCsdkSessionLibrary::Conv_RoomIdToSessionId(Id));
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Dismiss ChatRoom:%s,ErrorCode:%d"),*Id.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg);
	};
	FCsdkCommonHttp<ChatRoomDismissResponse>::Request(TEXT("/spi/v3beta/chatroom/dismiss"),Request,HttpCallback);
}

void FChatRoomManager::InviteToChatRoom(const FCsdkChatRoomId& Id, const TSet<FCsdkUserId>& UserIds,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg)>& Callback)
{
	ChatRoomInviteRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(Id));
	for(const auto& UserId : UserIds)
	{
		Request.mutable_users()->AddAllocated(CsdkUserTool::MakePbUserKey(UserId));
	}

	auto HttpCallback =[this,Id,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomInviteResponse& Resp)
	{
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			/*for(const auto& InviteRespItem :Resp.list())
			{
				auto UserId = CsdkUserTool::ConvertUserId(InviteRespItem.userkey());
				FString
				@todo invite 
			}*/

		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Invite To ChatRoom:%s,ErrorCode:%d"),*Id.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg);
	};
	FCsdkCommonHttp<ChatRoomInviteResponse>::Request(TEXT("/spi/v3beta/chatroom/invite"),Request,HttpCallback);
}

void FChatRoomManager::RemoveChatRoomUser(const FCsdkChatRoomId& Id, const TSet<FCsdkUserId>& UserIds,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg,const TArray<FCsdkUserOperateResult>& OperateResult)>& Callback)
{
	ChatRoomUserRemoveRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(Id));
	for(const auto& UserId : UserIds)
	{
		Request.mutable_users()->AddAllocated(CsdkUserTool::MakePbUserKey(UserId));
	}

	auto HttpCallback =[this,Id,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomUserRemoveResponse& Resp)
	{
		TArray<FCsdkUserOperateResult> OperateResults;
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			
			for(const auto& RemoveRespItem :Resp.list())
			{
				FCsdkUserOperateResult OperateResult;
				OperateResult.Id = CsdkUserTool::ConvertUserId(RemoveRespItem.userkey());
				OperateResult.Msg = STD_STRING_TO_FSTRING(RemoveRespItem.msg());
				OperateResult.ErrorCode =  RemoveRespItem.code();
				
				OperateResults.Add(OperateResult);
			}
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Invite To ChatRoom:%s,ErrorCode:%d"),*Id.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg,OperateResults);
	};
	FCsdkCommonHttp<ChatRoomUserRemoveResponse>::Request(TEXT("/spi/v3beta/chatroom/invite"),Request,HttpCallback);
}

bool FChatRoomManager::GetChatRoomInfoById(const FCsdkChatRoomId& Id, FCsdkChatRoomInfo& ChatRoomInfo) const
{
	if(const auto RoomInfoPtr = ChatRoomMap.Find(Id))
	{
		ChatRoomInfo = *RoomInfoPtr;
		return true;
	}

	return false;
}

TArray<FCsdkChatRoomInfo> FChatRoomManager::GetChatRoomsInfo() const
{
	TArray<FCsdkChatRoomInfo> Result;
	ChatRoomMap.GenerateValueArray(Result);
	return Result;
}

void FChatRoomManager::OnEventRecived(const EChatRoomEventType EventType, const FString& JsonData)
{
	switch (EventType)
	{
	case EChatRoomEventType::Join:
		OnEventJoin(JsonData);
		break;
	case EChatRoomEventType::MemberRemoved:
		OnEventMemberRemoved(JsonData);
		break;
	case EChatRoomEventType::Dismissed:
		OnEventDismissed(JsonData);
		break;
	default:
		return;
	}
}

void FChatRoomManager::JoinChatRoomRemote(const FCsdkChatRoomId& Id,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const FCsdkChatRoomInfo& ChatRoomInfo)>&
	Callback)
{
	ChatRoomJoinRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(Id));

	auto HttpCallback =[this,Id,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomJoinResponse& Resp)
	{
		FCsdkChatRoomInfo RoomInfo;
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			RoomInfo = CsdkChatRoomTool::ConvertRoomInfo(Resp.group());
			OnEventJoin(RoomInfo);
			//查找或者创建会话
			FCsdkCommonTools::GetSessionManager().CreateOrUpdateSession(UCsdkSessionLibrary::Conv_RoomIdToSessionId(RoomInfo.RoomId));
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Join ChatRoom:%s,ErrorCode:%d"),*Id.ToString(),ErrorCode);
		if(Callback)
		{
			Callback(ErrorCode,ErrorMsg,RoomInfo);
			return;
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Join ChatRoom no callback"));
	};
	FCsdkCommonHttp<ChatRoomJoinResponse>::Request(TEXT("/spi/v3beta/chatroom/join"),Request,HttpCallback);
}


void FChatRoomManager::JoinAutoCreateChatRoomRemote(const FCsdkChatRoomId& Id,
	const FCsdkChatRoomCreateParam& Param,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const FCsdkChatRoomInfo& ChatRoomInfo)>&
	Callback)
{
	ChatRoomJoinAutoCreateRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_groupkey(CsdkChatRoomTool::MakeGroupKey(Id));

	Request.mutable_createoptions()->set_grouptype(FSTRING_TO_STD_STRING(Param.RoomType));
	Request.mutable_createoptions()->set_title(FSTRING_TO_STD_STRING(Param.RoomName));
	Request.mutable_createoptions()->set_iconurl(FSTRING_TO_STD_STRING(Param.Icon));

	for(const auto& ExtraItem:Param.Extra)
	{
		Request.mutable_createoptions()->mutable_extra()->insert({FSTRING_TO_STD_STRING(ExtraItem.Key),FSTRING_TO_STD_STRING(ExtraItem.Value)});
	}

	auto HttpCallback =[this,Id,Callback](const int32 ErrorCode,const FString& ErrorMsg,const ChatRoomJoinAutoCreateResponse& Resp)
	{
		FCsdkChatRoomInfo RoomInfo;
		if(ErrorCode == ECsdkErrorCode::Success)
		{
			RoomInfo = CsdkChatRoomTool::ConvertRoomInfo(Resp.group());
			OnEventJoin(RoomInfo);
			//查找或者创建会话
			FCsdkCommonTools::GetSessionManager().CreateOrUpdateSession(UCsdkSessionLibrary::Conv_RoomIdToSessionId(RoomInfo.RoomId));
		}
		CSDK_LOG(LogChatSdk,Log,TEXT("Join Auto Create ChatRoom:%s,ErrorCode:%d"),*Id.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg,RoomInfo);
	};
	FCsdkCommonHttp<ChatRoomJoinAutoCreateResponse>::Request(TEXT("/spi/v3beta/chatroom/join/auto_create"),Request,HttpCallback);
}


void FChatRoomManager::OnEventJoin(const FString& JsonData)
{
	
	TSharedPtr<FJsonObject> RootObject = MakeShareable(new FJsonObject());
	const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonData);
	if (FJsonSerializer::Deserialize(Reader, RootObject))
	{
		const TSharedPtr<FJsonObject>* DataObj = nullptr;
		//暂时不暴露User
		//FCsdkUser User;
		//RootObject->TryGetObjectField("user", DataObj);
		//User = CsdkUserTool::ConvertUser(*DataObj);
		RootObject->TryGetObjectField(TEXT("group"), DataObj);
		const FCsdkChatRoomInfo RoomInfo = CsdkChatRoomTool::ConvertRoomInfo(*DataObj);
		OnEventJoin(RoomInfo);
	}
}

void FChatRoomManager::OnEventJoin(const FCsdkChatRoomInfo& RoomInfo)
{
	if(!ChatRoomMap.Find(RoomInfo.RoomId))
	{
		ChatRoomMap.Add(RoomInfo.RoomId,RoomInfo);
		FCsdkManager::GetInstance().Subsystem->OnChatRoomJoin.Broadcast(RoomInfo);
		CSDK_LOG(LogChatSdk, Log, TEXT("On Event ChatRoom Joined,RoomInfo:%s"), *RoomInfo.ToString());
	}

}

void FChatRoomManager::OnEventMemberRemoved(const FString& JsonData)
{
	TSharedPtr<FJsonObject> RootObject = MakeShareable(new FJsonObject());
	const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonData);
	if (FJsonSerializer::Deserialize(Reader, RootObject))
	{
		const TSharedPtr<FJsonObject>* DataObj = nullptr;
		RootObject->TryGetObjectField(TEXT("group"), DataObj);
		const FCsdkChatRoomInfo RoomInfo = CsdkChatRoomTool::ConvertRoomInfo(*DataObj);
		ChatRoomMap.Remove(RoomInfo.RoomId);
		FCsdkManager::GetInstance().Subsystem->OnBeChatRoomRemoved.Broadcast(RoomInfo);
		CSDK_LOG(LogChatSdk, Log, TEXT("On Event ChatRoom Member Removed, RoomInfo : %s "), *RoomInfo.ToString());
	}
}

void FChatRoomManager::OnEventDismissed(const FString& JsonData)
{
	TSharedPtr<FJsonObject> RootObject = MakeShareable(new FJsonObject());
	const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonData);
	if (FJsonSerializer::Deserialize(Reader, RootObject))
	{
		const TSharedPtr<FJsonObject>* DataObj = nullptr;
		RootObject->TryGetObjectField(TEXT("user"), DataObj);
		const FCsdkUser Operator = CsdkUserTool::ConvertUser(*DataObj);
		RootObject->TryGetObjectField(TEXT("group"), DataObj);
		const FCsdkChatRoomInfo RoomInfo = CsdkChatRoomTool::ConvertRoomInfo(*DataObj);
		ChatRoomMap.Remove(RoomInfo.RoomId);
		FCsdkManager::GetInstance().Subsystem->OnChatRoomDismissed.Broadcast(RoomInfo,Operator);
		CSDK_LOG(LogChatSdk, Log, TEXT("On Event ChatRoom Dismissed,RoomInfo:%s , Operator : %s"), *RoomInfo.ToString(),*Operator.ToString());
	}
}

