#pragma once

#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "UI/FrontEndTypes.h"
#include "UIFrontEndFlowSubsystem.generated.h"

class UUIFrontEndScreenSet;
class UUIManagerSubsystem;
class UZeusNetworkSubsystem;
class UZeusCharServerSubsystem;
struct FZMMOMapDef;

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnZMMOFrontEndStateChanged, EZMMOFrontEndState, NewState);

/**
 * Orquestrador do fluxo de front-end. UGameInstanceSubsystem porque o
 * GameInstance persiste o travel boot→mundo (a máquina de estados não pode
 * morrer no OpenLevel).
 *
 * Responsável por: (a) dirigir a conexão via UZeusNetworkSubsystem;
 * (b) manter EZMMOFrontEndState; (c) resolver estado→tela pelo
 * DA_FrontEndScreenSet e pedir ao UUIManagerSubsystem para empurrar na
 * camada certa; (d) detectar a chegada ao mundo.
 *
 * As telas (WBP) ainda não existem — virão do Zeus UMG Forge (§4.8).
 * Enquanto não registradas no DA, cada transição faz no-op + log.
 */
UCLASS(Config = Game)
class ZMMO_API UUIFrontEndFlowSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	// USubsystem
	virtual void Initialize(FSubsystemCollectionBase& Collection) override;
	virtual void Deinitialize() override;

	/**
	 * Ponto de entrada chamado pelo AZMMOFrontEndPlayerController::BeginPlay
	 * (garante que os subsistemas já existem — ver aviso no header do
	 * ZeusNetworkSubsystem sobre GameInstance::Init). Cria o root layout,
	 * liga os delegates de rede e dispara Boot→Connecting.
	 */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd")
	void StartFrontEnd();

	/**
	 *  Garante que o root layout (UUIPrimaryGameLayout_Base) esta no viewport.
	 *  Chamado pelo UIInGameFlowSubsystem::StartInGame apos OpenLevel — o
	 *  `OpenLevel` invalida widgets no viewport ("InvalidateAllWidgets") e o
	 *  RootLayout fica orfao. Idempotente: CreateAndAddRootLayout do
	 *  UIManagerSubsystem checa `IsInViewport` antes de recriar.
	 *
	 *  C++ puro (sem UFUNCTION) — Live Coding nao registra UFUNCTIONs novas;
	 *  consumidores externos chamam direto via C++ (UIInGameFlowSubsystem).
	 */
	void EnsureRootLayout();

	UFUNCTION(BlueprintCallable, Category = "FrontEnd")
	void SetState(EZMMOFrontEndState NewState);

	UFUNCTION(BlueprintPure, Category = "FrontEnd")
	EZMMOFrontEndState GetCurrentState() const { return CurrentState; }

	/** Mostra uma página interna do Lobby ("switch principal"). */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd")
	void ShowLobbyPage(EZMMOLobbyPage Page);

	/** "Voltar". Retorna true se o fluxo consumiu (tela base encaminha aqui). */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd")
	bool RequestBack();

	/** Chamado pela tela Boot quando o usuário clica "Iniciar" (já conectado). */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd")
	void RequestEnterLogin();

	/** Chamado pela tela Login após autenticação OK no CharServer. */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd")
	void RequestEnterServerSelect();

	/**
	 * Memoriza o mundo escolhido na ServerSelect (UUID v4 string). Lido pelo
	 * CharSelect/CharCreate na hora de filtrar/criar personagens. Limpo no
	 * logout/back para ServerSelect.
	 */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds")
	void SetSelectedWorldId(const FString& WorldId);

	UFUNCTION(BlueprintPure, Category = "FrontEnd|Worlds")
	FString GetSelectedWorldId() const { return SelectedWorldId; }

	UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds")
	void ClearSelectedWorld();

	/**
	 *  Resolve `MapId` (uint16 recebido do CharServer) → row do DT_Maps.
	 *  Carrega o asset sync (PIE/dev OK; em prod considerar StreamableManager).
	 *  Retorna nullptr se MapId=0, DT_Maps nao configurado, ou mapId nao
	 *  encontrado.
	 */
	const FZMMOMapDef* FindMapDef(int32 MapId) const;

	/**
	 *  Helper: resolve `MapId` no DT_Maps e retorna `ClientLevel.ToSoftObjectPath().GetLongPackageName()`
	 *  (ex.: `/Game/ThirdPerson/TestWorld`). String vazia se nao encontrado.
	 */
	UFUNCTION(BlueprintPure, Category = "FrontEnd|Maps")
	FString ResolveLevelPathByMapId(int32 MapId) const;

	/**
	 *  Inicia transicao pra mundo dado um `MapId`: faz `OpenLevel` com
	 *  `?game=/Script/ZMMO.ZMMOGameMode`. No-op se `MapId` invalido ou
	 *  DT_Maps nao tem entrada.
	 */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd|Maps")
	bool TravelToMapById(int32 MapId);

	/**
	 *  Memoriza a pose autoritativa do char vinda no `S_CHAR_SELECT_OK`
	 *  (pos salva no DB + yaw). Sobrevive ao travel porque vivemos no
	 *  GameInstance. Lida pelo `AZMMOPlayerCharacter::BeginPlay` pra
	 *  reposicionar o pawn local em vez de spawnar no PlayerStart default
	 *  do level. Limpa apos consumo (a proxima sessao reenvia).
	 */
	UFUNCTION(BlueprintCallable, Category = "FrontEnd|Spawn")
	void SetPendingSpawnPose(FVector PosCm, float YawDeg);

	UFUNCTION(BlueprintCallable, Category = "FrontEnd|Spawn")
	bool ConsumePendingSpawnPose(FVector& OutPosCm, float& OutYawDeg);

	UPROPERTY(BlueprintAssignable, Category = "FrontEnd")
	FOnZMMOFrontEndStateChanged OnStateChanged;

protected:
	/** DA com o mapa estado→tela. Config em DefaultGame.ini. */
	UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd")
	TSoftObjectPtr<UUIFrontEndScreenSet> ScreenSetAsset;

	/**
	 *  DataTable com FZMMOMapDef. Source of truth client-side para
	 *  `MapId -> ClientLevel/spawns/displayName`. Configure em DefaultGame.ini:
	 *      [/Script/ZMMO.UIFrontEndFlowSubsystem]
	 *      MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps
	 */
	UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Maps")
	TSoftObjectPtr<UDataTable> MapsTableAsset;

private:
	void BindNetwork();
	void UnbindNetwork();
	UZeusNetworkSubsystem* GetZeusNetwork() const;
	UZeusCharServerSubsystem* GetCharServer() const;
	UUIManagerSubsystem* GetUIManager() const;
	UUIFrontEndScreenSet* GetScreenSet();

	/** Resolve a soft class do estado e empurra na camada Menu (no-op+log se vazio). */
	void ResolveAndPushScreen(EZMMOFrontEndState State);

	UFUNCTION()
	void HandleConnected();

	UFUNCTION()
	void HandleConnectionFailed(FString Reason);

	UFUNCTION()
	void HandleCharDisconnected(int32 StatusCode, FString Reason);

	UFUNCTION()
	void HandleServerTravelRequested(const FString& MapName, const FString& MapPath);

	void HandlePostLoadMap(UWorld* LoadedWorld);

	/**
	 * Dívida técnica (D5): ServerHello traz ThemeId, mas o UZeusNetworkSubsystem
	 * ainda não expõe um delegate/getter dedicado. Quando expuser, ligar aqui
	 * → UZMMOThemeSubsystem::ApplyServerTheme. Sem isto, o tema resolve por
	 * calendário/Default (ARQUITETURA.md §1.10).
	 */
	void HandleServerHelloTheme(FName ThemeId);

	UPROPERTY(Transient)
	EZMMOFrontEndState CurrentState = EZMMOFrontEndState::None;

	UPROPERTY(Transient)
	FString SelectedWorldId;

	/** Pose autoritativa pendente (do S_CHAR_SELECT_OK). */
	UPROPERTY(Transient)
	FVector PendingSpawnPosCm = FVector::ZeroVector;

	UPROPERTY(Transient)
	float PendingSpawnYawDeg = 0.f;

	UPROPERTY(Transient)
	bool bHasPendingSpawnPose = false;

	UPROPERTY(Transient)
	TObjectPtr<UUIFrontEndScreenSet> ScreenSet;

	bool bNetBound = false;
	bool bTravelingToWorld = false;
	FDelegateHandle PostLoadMapHandle;
};
