#include "UserManager.h"

#include "Common/CsdkErrorCode.h"
#include "search/search.pb.h"
#include "Tools/CsdkCommonHttp.h"
#include "user/user_dto.pb.h"
#include "User/Data/CsdkUserControl.h"
#include "User/Tool/UserTool.h"

using namespace heroim::client::v3beta::dto::user;
using namespace heroim::client::v3beta::dto::search;
using namespace heroim::client::v3beta::dto::user::model;

void FUserManager::Reset()
{
	CurrentUser={};
}

FString FUserManager::GetCurrentUserSavePath() const
{
	return FPaths::Combine(FCsdkCommonTools::CSdkSaveDir(),GetCurrentUserId().ServerId,GetCurrentUserId().RoleId);
}

void FUserManager::GetUserList(const TArray<FCsdkUserId>& UserIdList,
                               const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const TArray<FCsdkUser>& UserList)>& Callback)
{
	ListUserByRoleIdRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	for(const auto& UserId:UserIdList)
	{
		Request.mutable_users()->AddAllocated(CsdkUserTool::MakePbUserKey(UserId));
	}

	auto HttpCallback =[Callback](const int32 ErrorCode,const FString& ErrorMsg,const ListUserByRoleIdResponse& Resp)
	{
		TArray<FCsdkUser> UserList;
		for(const auto& PbUser:Resp.list())
		{
			const FCsdkUser User = CsdkUserTool::ConvertUser(PbUser);
			UserList.Add(User);
		}
		Callback(ErrorCode,ErrorMsg,UserList);
	};
	FCsdkCommonHttp<ListUserByRoleIdResponse>::Request(TEXT("/spi/v3beta/user/list/role"),Request,HttpCallback);
}

void FUserManager::GetUserListStatus(const TArray<FCsdkUserId>& UserIds,
                                     const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const TArray<FCsdkUserStatusResult>&
                                                          StatusResultList)>& Callback)
{
	ListUserStatusRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	for(const auto& UserId:UserIds)
	{
		Request.mutable_users()->AddAllocated(CsdkUserTool::MakePbUserKey(UserId));
	}
	
	auto HttpCallback =[Callback](const int32 ErrorCode,const FString& ErrorMsg,const ListUserStatusResponse& Resp)
	{
		TArray<FCsdkUserStatusResult> Results;
		for(const auto& StatusItem : Resp.list())
		{
			FCsdkUserStatusResult StatusResult;
			StatusResult.UserId =CsdkUserTool::ConvertUserId(StatusItem.userkey());
			StatusResult.ErrorCode = StatusItem.code();
			StatusResult.UserStatus.bIsOnline = StatusItem.status().isonline();
			StatusResult.Msg = STD_STRING_TO_FSTRING(StatusItem.message());
			StatusResult.UserStatus.LastOnlineTime = FDateTime::FromUnixTimestamp(StatusItem.status().lastonlinetime() / 1000);
			for(const auto& ExtraItem:StatusItem.status().extra())
			{
				StatusResult.UserStatus.Extra.Add(STD_STRING_TO_FSTRING(ExtraItem.first),STD_STRING_TO_FSTRING(ExtraItem.second));
			}

			Results.Add(StatusResult);
		}
		Callback(ErrorCode,ErrorMsg,Results);
	};
	FCsdkCommonHttp<ListUserStatusResponse>::Request(TEXT("/spi/v3beta/user/status/list"),Request,HttpCallback);
}

void FUserManager::UpdateUser(const FString& RoleName, const ECsdkGender& Gender, const FString& AvatarUrl,const TMap<FString, FString>& Extra,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg)>& Callback)
{
	UserUpdateRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_rolename(FSTRING_TO_STD_STRING(RoleName));
	Request.set_avatarurl(FSTRING_TO_STD_STRING(AvatarUrl));
	Request.set_gender(static_cast<::heroim::client::v3beta::dto::user::model::Gender>(Gender));
	
	for(const auto& ExtraItem:Extra)
	{
		Request.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 FindUserByRoleIdResponse& Resp)
	{
		CurrentUser = CsdkUserTool::ConvertUser(Resp.user());
		CSDK_LOG(LogChatSdk,Log,TEXT("Get User:%s,ErrorCode,%d"),*CurrentUser.ToString(),ErrorCode);
		Callback(ErrorCode,ErrorMsg);
	};
	FCsdkCommonHttp<FindUserByRoleIdResponse>::Request(TEXT("/spi/v3beta/user/update"),Request,HttpCallback);
}

void FUserManager::SearchUser(const TArray<FCsdkUserSearchCondition>& Conditions, const int32 PageNo,
	const int32 PageSize,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const TArray<FCsdkUser>& UserList)>& Callback)
{
	SearchUserByConditionRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	for(const auto& Condition : Conditions)
	{
		heroim::client::v3beta::dto::search::Condition* PbCondition = new heroim::client::v3beta::dto::search::Condition;
		PbCondition->set_field(FSTRING_TO_STD_STRING(Condition.Field));
		PbCondition->set_operation(static_cast<CompareOperation>(Condition.Operator));
		PbCondition->set_value(FSTRING_TO_STD_STRING(Condition.Value));
		Request.mutable_conditions()->AddAllocated(PbCondition);
	}
	Request.set_pageno(PageNo);
	Request.set_pagesize(PageSize);
	
	auto HttpCallback = [Callback](const int32 ErrorCode,const FString& ErrorMsg,const SearchUserByConditionResponse& Resp)
	{
		TArray<FCsdkUser> Results;
		for(const auto& PbUser : Resp.list())
		{
			Results.Add(CsdkUserTool::ConvertUser(PbUser.user()));
		}
		CSDK_LOG(LogChatSdk, Log, TEXT("Search User Num:%d:,ErrorCode,%d"),Results.Num(),ErrorCode);
		Callback(ErrorCode, ErrorMsg, Results);
	};
	FCsdkCommonHttp<SearchUserByConditionResponse>::Request(TEXT("/spi/v3beta/search/user/list/by_condition"),Request,HttpCallback);
}

void FUserManager::OnEventReceived(const EUserEventType EventType, const FString& JsonData)
{
	switch (EventType)
	{
	case EUserEventType::UserProfileChanged:
		OnUserProfileChanged(JsonData);
		break;
	case EUserEventType::UserStatusChanged:
		OnUserStatusChanged(JsonData);
		break;
	case EUserEventType::UserControlChanged:
		OnUserControlChanged(JsonData);
		break;
	case EUserEventType::UserConnected:
		OnUserConnected(JsonData);
		break;
	case EUserEventType::UserDisconnected:
		OnUserDisconnected(JsonData);
		break;
	default:
		return;
	}
}

void FUserManager::GetUserControl(
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const FCsdkUserControl& CsdkUserControl)>& Callback)
{
	UserControlFindRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	
	auto HttpCallback =[Callback](const int32 ErrorCode,const FString& ErrorMsg,const UserControlFindResponse& Resp)
	{
		FCsdkUserControl CsdkUserControl;
		const UserControl Detail = Resp.detail();
		//被禁言为1
		CsdkUserControl.bCanSendMsg = !Detail.message();
		CsdkUserControl.bCanSendVoice = !Detail.talk();
		CsdkUserControl.MessageControlDetail.EndTime = FDateTime::FromUnixTimestamp(Detail.messagedetail().endtime() / 1000);
		CsdkUserControl.MessageControlDetail.Reason = STD_STRING_TO_FSTRING(Detail.messagedetail().reason());

		CsdkUserControl.VoiceControlDetail.EndTime = FDateTime::FromUnixTimestamp(Detail.talkdetail().endtime() / 1000);
		CsdkUserControl.VoiceControlDetail.Reason = STD_STRING_TO_FSTRING(Detail.talkdetail().reason());
		Callback(ErrorCode,ErrorMsg, CsdkUserControl);
	};
	FCsdkCommonHttp<UserControlFindResponse>::Request(TEXT("/spi/v3beta/user/control"),Request,HttpCallback);
}

void FUserManager::OnUserProfileChanged(const FString& JsonData)
{
	const TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<TCHAR>::Create(JsonData);
	TSharedPtr<FJsonObject> RootObject;
	if(!FJsonSerializer::Deserialize(JsonReader, RootObject))
	{
		return;
	}
	const TSharedPtr<FJsonObject>* UserObject =nullptr;
	RootObject->TryGetObjectField(TEXT("user"),UserObject);

	const FCsdkUser User = CsdkUserTool::ConvertUser(*UserObject);
	FCsdkManager::GetInstance().Subsystem->OnUserProfileChanged.Broadcast(User);
	CSDK_LOG(LogChatSdk, Log, TEXT("On Event User Profile Changed : %s"), *User.ToString());
}

void FUserManager::OnUserStatusChanged(const FString& JsonData)
{
	const TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<TCHAR>::Create(JsonData);
	TSharedPtr<FJsonObject> RootObject;
	if(!FJsonSerializer::Deserialize(JsonReader, RootObject))
	{
		return;
	}
	const TSharedPtr<FJsonObject>* UserObject =nullptr;
	RootObject->TryGetObjectField(TEXT("userKey"),UserObject);
	const FCsdkUserId UserKey = CsdkUserTool::ConvertUserId(*UserObject);
	const TSharedPtr<FJsonObject>* UserStatusObject =nullptr;
	RootObject->TryGetObjectField(TEXT("status"),UserStatusObject);
	const FCsdkUserStatus UserStatus = CsdkUserTool::ConvertUserStatus(*UserStatusObject);
	FCsdkManager::GetInstance().Subsystem->OnUserStatusChanged.Broadcast(UserKey, UserStatus);
	CSDK_LOG(LogChatSdk, Log, TEXT("On Event User Status Changed : %s"), *UserKey.ToString());
}

void FUserManager::OnUserControlChanged(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("status"), DataObj);
		FCsdkUserControl CsdkUserControl;
		if (DataObj)
		{
			CsdkUserTool::ConvertControlStatus(*DataObj, CsdkUserControl);
		}
		FCsdkManager::GetInstance().Subsystem->OnControlStatusChanged.Broadcast(CsdkUserControl);
		CSDK_LOG(LogChatSdk, Log, TEXT("On Event Control Status Changed CanSendMsg: %s,CanSendVoice: %s"),
		         CsdkUserControl.bCanSendMsg?TEXT("True"):TEXT("False"),
		         CsdkUserControl.bCanSendVoice?TEXT("True"):TEXT("False"));
	}
}

void FUserManager::OnUserConnected(const FString& JsonData)
{
	//@todo 暂时不做
}

void FUserManager::OnUserDisconnected(const FString& JsonData)
{
	//@todo 暂时不做
}

