#include "UILoginScreen_Base.h"

#include "ZMMO.h"
#include "CharServerOpcodes.h"
#include "UIButton_Base.h"
#include "UICheckBox_Base.h"
#include "UILabel_Base.h"
#include "ZMMOLoginSaveGame.h"
#include "Kismet/GameplayStatics.h"
#include "UIFrontEndFlowSubsystem.h"
#include "ZMMOThemeSubsystem.h"
#include "ZeusCharServerSubsystem.h"
#include "ZeusPacketWriter.h"
#include "ZeusPacketReader.h"
#include "CommonTextBlock.h"
#include "Components/Border.h"
#include "Engine/GameInstance.h"
#include "Engine/DataTable.h"
#include "UI/UIStyleRow.h"

namespace
{
	// Mesmo helper da Boot: runtime usa o tema ativo; design-time carrega a
	// row "Default" de DT_UI_Styles para o Designer ver o fundo.
	FUIStyle ResolveLoginStyle(const UUserWidget* Widget, const FUIStyle& Fallback)
	{
		if (Widget)
		{
			if (const UGameInstance* GI = Widget->GetGameInstance())
			{
				if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem<UZMMOThemeSubsystem>())
				{
					return Theme->GetActiveUIStyle();
				}
			}
		}
		// Design-time relê o DT a cada chamada: editar o DT_UI_Styles e
		// RECOMPILAR o WBP já reflete (sem reiniciar o editor).
		FUIStyle DesignStyle;
		bool bHas = false;
		if (const UDataTable* DT = LoadObject<UDataTable>(nullptr,
			TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")))
		{
			if (const FUIStyleRow* Row = DT->FindRow<FUIStyleRow>(
				FName(TEXT("Default")), TEXT("UILoginDesign"), false))
			{
				DesignStyle = Row->Style;
				bHas = true;
			}
		}
		return bHas ? DesignStyle : Fallback;
	}

}

void UUILoginScreen_Base::NativePreConstruct()
{
	Super::NativePreConstruct();
	RefreshUIStyle(); // preview do fundo no Designer (DT_UI_Styles "Default")
}

void UUILoginScreen_Base::NativeOnActivated()
{
	Super::NativeOnActivated(); // base: RefreshUIStyle + BP_OnScreenActivated

	if (Btn_Login)
	{
		Btn_Login->OnClicked().AddUObject(this, &UUILoginScreen_Base::HandleLoginClicked);
	}
	if (Btn_Back)
	{
		Btn_Back->OnClicked().AddUObject(this, &UUILoginScreen_Base::HandleBackClicked);
	}

	UZeusCharServerSubsystem* Char = GetCharServer();
	if (Char && !bRawBound)
	{
		Char->OnRawMessage.AddDynamic(this, &UUILoginScreen_Base::HandleCharRawMessage);
		bRawBound = true;
	}

	bAwaitingAuth = false;
	const bool bConnected = Char && Char->IsConnected();
	SetBusy(false);
	if (Btn_Login)
	{
		Btn_Login->SetIsInteractionEnabled(bConnected);
	}
	SetStatus(bConnected ? IdleText : NotConnectedText);

	// "Lembrar Acesso": tenta restaurar usuário salvo (async, não trava UI).
	LoadRememberedAccess();
}

void UUILoginScreen_Base::NativeOnDeactivated()
{
	if (Btn_Login)
	{
		Btn_Login->OnClicked().RemoveAll(this);
	}
	if (Btn_Back)
	{
		Btn_Back->OnClicked().RemoveAll(this);
	}
	if (bRawBound)
	{
		if (UZeusCharServerSubsystem* Char = GetCharServer())
		{
			Char->OnRawMessage.RemoveDynamic(this, &UUILoginScreen_Base::HandleCharRawMessage);
		}
		bRawBound = false;
	}
	Super::NativeOnDeactivated();
}

void UUILoginScreen_Base::RefreshUIStyle_Implementation()
{
	Super::RefreshUIStyle_Implementation();

	const FUIStyle Fallback;
	const FUIStyle AS = ResolveLoginStyle(this, Fallback);

	if (Border_Bg)
	{
		// Fundo data-driven (DT_UI_Styles) — sem hard-ref de textura no WBP
		// (§5). Fallback p/ cor se vazio.
		if (AS.ScreenBackground.GetResourceObject() != nullptr)
		{
			Border_Bg->SetBrush(AS.ScreenBackground);
		}
		else
		{
			Border_Bg->SetBrushColor(AS.Palette.Bg0);
		}
	}
	if (Text_Status)
	{
		Text_Status->SetColorAndOpacity(FSlateColor(AS.Semantic.OnSurfaceMuted));
	}
}

UZeusCharServerSubsystem* UUILoginScreen_Base::GetCharServer() const
{
	const UGameInstance* GI = GetGameInstance();
	return GI ? GI->GetSubsystem<UZeusCharServerSubsystem>() : nullptr;
}

void UUILoginScreen_Base::SetBusy(bool bBusy)
{
	if (Btn_Login)
	{
		Btn_Login->SetIsInteractionEnabled(!bBusy);
	}
	if (Input_User)
	{
		Input_User->SetIsEnabled(!bBusy);
	}
	if (Input_Password)
	{
		Input_Password->SetIsEnabled(!bBusy);
	}
}

void UUILoginScreen_Base::SetStatus(const FText& InText)
{
	if (Text_Status)
	{
		Text_Status->SetText(InText);
	}
}

void UUILoginScreen_Base::HandleLoginClicked()
{
	if (bAwaitingAuth)
	{
		return;
	}

	UZeusCharServerSubsystem* Char = GetCharServer();
	if (!Char || !Char->IsConnected())
	{
		SetStatus(NotConnectedText);
		return;
	}

	const FString User = Input_User ? Input_User->GetText().ToString() : FString();
	const FString Pass = Input_Password ? Input_Password->GetText().ToString() : FString();
	if (User.TrimStartAndEnd().IsEmpty() || Pass.IsEmpty())
	{
		SetStatus(EmptyFieldsText);
		return;
	}

	// "Lembrar Acesso": salva/apaga ANTES do envio do auth — atende a
	// expectativa "marquei → vai lembrar próxima vez" independente do
	// resultado da autenticação (senha errada ainda lembra do usuário,
	// padrão dos launchers comerciais).
	SaveOrClearRememberedAccess(User);

	FZeusPacketWriter W;
	W.WriteUInt8(ZMMOCharAuthMethod::PASSWORD);
	W.WriteStringUtf8(User.TrimStartAndEnd());
	W.WriteStringUtf8(Pass);

	const bool bSent = Char->SendCharRequest(
		ZMMOCharOp::C_CHAR_AUTH_REQUEST, W.GetBuffer());
	if (!bSent)
	{
		UE_LOG(LogZMMO, Warning, TEXT("Login: falha ao enviar C_CHAR_AUTH_REQUEST."));
		SetStatus(NotConnectedText);
		return;
	}

	bAwaitingAuth = true;
	SetBusy(true);
	SetStatus(AuthenticatingText);
	UE_LOG(LogZMMO, Log, TEXT("Login: C_CHAR_AUTH_REQUEST enviado (PASSWORD)."));
}

void UUILoginScreen_Base::HandleBackClicked()
{
	if (const UGameInstance* GI = GetGameInstance())
	{
		if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
		{
			Flow->RequestBack();
		}
	}
}

void UUILoginScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>& Payload)
{
	if (!bAwaitingAuth)
	{
		return; // só nos interessa a resposta do auth que disparamos
	}

	if (Opcode == ZMMOCharOp::S_CHAR_AUTH_OK)
	{
		bAwaitingAuth = false;
		FZeusPacketReader R(Payload.GetData(), Payload.Num());
		FString AccountId, Username;
		R.ReadStringUtf8(AccountId);
		R.ReadStringUtf8(Username);
		uint64 ExpiresMs = 0;
		R.ReadUInt64(ExpiresMs);
		UE_LOG(LogZMMO, Log, TEXT("Login: S_CHAR_AUTH_OK (account=%s user=%s) → ServerSelect."),
			*AccountId, *Username);

		if (const UGameInstance* GI = GetGameInstance())
		{
			if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem<UUIFrontEndFlowSubsystem>())
			{
				Flow->RequestEnterServerSelect();
			}
		}
	}
	else if (Opcode == ZMMOCharOp::S_CHAR_AUTH_REJECT)
	{
		bAwaitingAuth = false;
		FZeusPacketReader R(Payload.GetData(), Payload.Num());
		uint16 Reason = 0;
		R.ReadUInt16(Reason);
		UE_LOG(LogZMMO, Warning, TEXT("Login: S_CHAR_AUTH_REJECT (reason=%u)."), Reason);
		SetBusy(false);

		FText Message;
		switch (Reason)
		{
		case ZMMOCharRejectReason::InvalidCredentials:      Message = InvalidCredentialsText; break;
		case ZMMOCharRejectReason::AccountLocked:           Message = AccountLockedText;       break;
		case ZMMOCharRejectReason::AccountSuspended:        Message = AccountSuspendedText;    break;
		case ZMMOCharRejectReason::AccountBanned:           Message = AccountSuspendedText;    break;
		case ZMMOCharRejectReason::CredentialsAuthDisabled: Message = CredentialsDisabledText; break;
		default:
			Message = FText::Format(
				NSLOCTEXT("ZMMO", "LoginRejectFmt", "{0} (cód. {1})"),
				RejectedText, FText::AsNumber(Reason));
			break;
		}
		SetStatus(Message);
	}
}

void UUILoginScreen_Base::LoadRememberedAccess()
{
	if (!UGameplayStatics::DoesSaveGameExist(
		UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex))
	{
		return;
	}

	FAsyncLoadGameFromSlotDelegate Delegate;
	Delegate.BindUFunction(this,
		FName(TEXT("HandleLoadLoginCacheCompleted")));
	UGameplayStatics::AsyncLoadGameFromSlot(
		UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex, Delegate);
}

void UUILoginScreen_Base::HandleLoadLoginCacheCompleted(const FString& /*InSlotName*/,
	int32 /*InUserIndex*/, USaveGame* LoadedGame)
{
	const UZMMOLoginSaveGame* Save = Cast<UZMMOLoginSaveGame>(LoadedGame);
	if (!Save || !Save->bRememberAccess || Save->SavedUsername.IsEmpty())
	{
		return;
	}
	if (Input_User)
	{
		Input_User->SetText(FText::FromString(Save->SavedUsername));
	}
	if (RememberAccess)
	{
		RememberAccess->SetIsChecked(true);
	}
}

void UUILoginScreen_Base::SaveOrClearRememberedAccess(const FString& User)
{
	const bool bRemember = RememberAccess ? RememberAccess->IsChecked() : false;
	if (bRemember)
	{
		UZMMOLoginSaveGame* Save = Cast<UZMMOLoginSaveGame>(
			UGameplayStatics::CreateSaveGameObject(UZMMOLoginSaveGame::StaticClass()));
		if (Save)
		{
			Save->SavedUsername    = User.TrimStartAndEnd();
			Save->bRememberAccess  = true;
			UGameplayStatics::AsyncSaveGameToSlot(
				Save, UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex);
		}
	}
	else if (UGameplayStatics::DoesSaveGameExist(
		UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex))
	{
		// Checkbox desmarcada: apaga save antigo (não vamos lembrar mais).
		UGameplayStatics::DeleteGameInSlot(
			UZMMOLoginSaveGame::SlotName, UZMMOLoginSaveGame::UserIndex);
	}
}
