#include "SessionList.h"
#include "Manager/CsdkSubsystem.h"
#include "message/session_dto.pb.h"
#include "Message/Tool/MessageTool.h"
#include "Session/Tool/SessionTool.h"
#include "Tools/CsdkCommonHttp.h"
#include "Tools/CsdkCommonTools.h"
#include "Async/TaskGraphInterfaces.h"

using SessionIterator = TArray<TObjectPtr<UCsdkSession>>::RangedForIteratorType;
using SessionConstIterator = TArray<TObjectPtr<UCsdkSession>>::RangedForConstIteratorType;

using namespace heroim::client::v3beta::dto::message;
using namespace heroim::client::v3beta::dto::message::model;

void FSessionList::AddReferencedObjects(FReferenceCollector& Collector)
{
	Collector.AddReferencedObjects(SessionArray);
	Collector.AddReferencedObjects(SessionMap);
}

void FSessionList::Initialize(TFunction<void(const int32 ErrorCode,const FString& ErrorMsg,TArray<UCsdkSession*>)> Callback)
{
	GetRemoteSessionList(Callback);
}

void FSessionList::Empty()
{
	SessionArray.Empty();
	SessionMap.Empty();
}

bool FSessionList::IsEmpty() const
{
	return SessionArray.Num() ==0;
}

void FSessionList::Add(UCsdkSession* NewSession)
{
	if(!Contains(NewSession->SessionId))
	{
		SessionArray.Add(NewSession);
		SessionMap.Add(NewSession->SessionId,NewSession);
	}
}

bool FSessionList::Contains(const FCsdkSessionId& Id) const
{
	return SessionMap.Contains(Id);
}


TObjectPtr<UCsdkSession> FSessionList::Find(const FCsdkSessionId& Id) const
{
	if(const auto SessionObj = SessionMap.Find(Id))
	{
		return *SessionObj;
	}
	return nullptr;
}

TObjectPtr<UCsdkSession> FSessionList::RemoveLocalSession(const FCsdkSessionId& Id)
{
	const auto Session = Find(Id);
	if(Session && Session->SessionId.IsValid())
	{
		SessionArray.Remove(Session);
		SessionMap.Remove(Id);
		FCsdkManager::GetInstance().Subsystem->OnSessionChanged.Broadcast(ECsdkSessionChanged::SessionDelete, Session);
	}
	return Session;
}

void FSessionList::Sort()
{
	SessionArray.Sort([](const UCsdkSession& A, const UCsdkSession& B )
	{
		if(A.bIsPinned == B.bIsPinned)
		{
			if(A.bIsPinned)//均置顶
			{
				const FDateTime ATime = A.PinnedTime>A.UpdateTime ? A.PinnedTime:A.UpdateTime;
				const FDateTime BTime = B.PinnedTime>B.UpdateTime ? B.PinnedTime:B.UpdateTime;
				return ATime >BTime;
				
			}
			//均不置顶
			return A.UpdateTime > B.UpdateTime;
		}
		//有一个置顶
		return A.bIsPinned;
	});
}

void FSessionList::GetSessionList(
	TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, TArray<UCsdkSession*>)> Callback)
{
	if (!IsEmpty())
	{
		AsyncTask(ENamedThreads::GameThread, [Callback,this]()
		{
			Callback(ECsdkErrorCode::Success, TEXT("Cached Msg"), GetAsArray());
		});
		return;
	}
	GetRemoteSessionList(Callback);
}

void FSessionList::DeleteSession(const FCsdkSessionId& SessionId,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg)>& Callback)
{
	SessionDeleteRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_sessionkey(CsdkSessionTool::MakePbSessionKey(SessionId));
	
	auto HttpCallback = [this,SessionId,Callback](const int32 ErrorCode, const FString& ErrorMsg,
										   const SessionDeleteResponse& Resp)
	{
		if (ErrorCode == ECsdkErrorCode::Success)
		{
			RemoveLocalSession(SessionId);
		}
		CSDK_LOG(LogChatSdk, Log, TEXT("Delete Session:%s, ErrorCode:%d"), *SessionId.ToString(), ErrorCode);
		Callback(ErrorCode, ErrorMsg);
	};
	FCsdkCommonHttp<SessionDeleteResponse>::Request(TEXT("/spi/v3beta/message/session/delete"), Request, HttpCallback);
}

void FSessionList::PinSession(const FCsdkSessionId& SessionId,const	bool bIsPinned,
	const TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, const FDateTime& PinnedTime)>& Callback)
{
	SessionSortRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());
	Request.set_allocated_sessionkey(CsdkSessionTool::MakePbSessionKey(SessionId));
	const SessionSortRequest_Status Status = bIsPinned?SessionSortRequest_Status_Enable:SessionSortRequest_Status_Disable;
	Request.set_status(Status);
	
	auto HttpCallback = [this,SessionId,bIsPinned,Callback](const int32 ErrorCode, const FString& ErrorMsg,
										   const SessionSortResponse& Resp)
	{
		FDateTime DateTime;
		if (ErrorCode == ECsdkErrorCode::Success)
		{
			DateTime = FDateTime::FromUnixTimestamp(Resp.time() / 1000);
			if(const auto Session = Find(SessionId))
			{
				Session->bIsPinned = bIsPinned;
				Session->PinnedTime = DateTime;
				Sort();
			}
		}
		CSDK_LOG(LogChatSdk, Log, TEXT("PinSession Session:%s, ErrorCode:%d,ErrorMsg:%s"), *SessionId.ToString(), ErrorCode,*ErrorMsg);
		Callback(ErrorCode, ErrorMsg,DateTime);
	};
	FCsdkCommonHttp<SessionSortResponse>::Request(TEXT("/spi/v3beta/message/session/sort"), Request, HttpCallback);
}

TArray<TObjectPtr<UCsdkSession>> FSessionList::GetAsArray() const
{
	return SessionArray;
}


SessionIterator FSessionList::begin()
{
	return SessionArray.begin();
}

SessionConstIterator FSessionList::begin() const
{
	return SessionArray.begin();
}

SessionIterator FSessionList::end()
{
	return SessionArray.end();
}

SessionConstIterator FSessionList::end() const
{
	return SessionArray.end();
}

void FSessionList::GetRemoteSessionList(
	TFunction<void(const int32 ErrorCode, const FString& ErrorMsg, TArray<UCsdkSession*>)> Callback)
{
	SessionListRequest Request;
	Request.set_allocated_base(FCsdkCommonTools::MakePbBase());

	auto RequestCallBack = [this,Callback](const int32 ErrorCode, const FString& ErrorMsg,
	                                       const SessionListResponse& Resp)
	{
		for (const auto& PbSession : Resp.list())
		{
			const FCsdkSessionId SessionId = CsdkSessionTool::ConvertSessionId(PbSession.sessionkey());
			if (Contains(SessionId))
			{
				continue;
			}
			const auto Session = NewObject<UCsdkSession>(FCsdkManager::GetInstance().Subsystem);
			Session->SessionId = SessionId;


			if (PbSession.has_last()) //自定义频道，开局消息为空
			{
				const auto LastMsg = MessageTool::NewMsgObj(FCsdkManager::GetInstance().Subsystem, PbSession.last());
				Session->UpdateTime = LastMsg->Time;
				Session->LastMsg = LastMsg;
			}
			
			Session->bIsPinned= PbSession.sort().status() == UserSessionSort_Status_Enable;
			
			if(Session->bIsPinned)
			{
				Session->UpdateTime = Session->PinnedTime = FDateTime::FromUnixTimestamp(PbSession.sort().time() / 1000);
			}

			this->Add(Session);
		}
		if (Callback)
		{
			Callback(ErrorCode, ErrorMsg, GetAsArray());
		}
		JoinChatRoomBySession();
	};
	FCsdkCommonHttp<SessionListResponse>::Request(TEXT("/spi/v3beta/session/list"), Request, RequestCallBack);
}

void FSessionList::JoinChatRoomBySession()
{
	for(const auto& SessionObj : SessionArray)
	{
		if(SessionObj->SessionId.Type == ECsdkSessionType::ChatRoom)
		{
			FCsdkChatRoomId ChatRoomId{SessionObj->SessionId.ReceiverId,SessionObj->SessionId.ServerId};
			FCsdkCommonTools::GetChatRoomManager().JoinChatRoom(ChatRoomId,nullptr);
		}
	}
}
