#pragma once

#include "CoreMinimal.h"
#include "EditorSubsystem.h"
#include "TickableEditorObject.h"
#include "Async/Future.h"
#include "WingNotifier.h"
#include "WingLogCapture.h"
#include "WingBasics.h"
#include "WingManual.h"
#include "WingServer.generated.h"

class FSocket;
class FJsonObject;

/**
 * UWingServer — editor subsystem that listens on a TCP socket and dispatches
 * JSON commands to blueprint editing handlers.
 *
 * Clients connect via TCP and exchange null-delimited JSON messages.
 * Request format:  {"command": "tool_name", "param1": "value1", ...}
 * Response format: Text.
 *
 * Each connected client gets its own thread for blocking I/O;
 * tool calls are dispatched on the game thread.
 *
 * In the editor, FTickableEditorObject drives the tick.
 * In commandlet mode, the commandlet ticks us directly.
 */
UCLASS()
class UWingServer : public UEditorSubsystem, public FTickableEditorObject
{
	GENERATED_BODY()

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

	// FTickableEditorObject
	virtual void Tick(float DeltaTime) override;
	virtual bool IsTickable() const override { return true; }
	virtual TStatId GetStatId() const override;

	/** Tick entry point for commandlet mode (no FTickableEditorObject). */
	static void TickServer(float DeltaTime);

	/** Track an object that has been modified by the current handler. */
	static void AddTouchedObject(UObject* Obj) { GWingServer->Notifier.AddTouchedObject(Obj); }

	/** Suggest that a manual section be printed after the handler finishes. */
	static void SuggestManual(FName Section) { GWingServer->SuggestedManualSections.Add(Section); }

	/** Suggest that handler help be printed after the handler finishes. */
	static void SuggestHandlerHelp() { GWingServer->bSuggestHandlerHelp = true; }

	/** Port the server is listening on. */
	int32 GetPort() const { return Port; }

	// ----- Tool dispatch -----
	static void AddHandler(UObject* Obj, const FString& Documentation);
	static void AddHandler(UObject* Obj, const FString& Name, UObject* Config, EWingHandlerKind Kind, UClass* FactoryClass, const FString& Documentation);
	static const TArray<FWingHandlerConfig>& AllHandlers() { return GWingServer->WingHandlerRegistry; }

private:
	static UWingServer* GWingServer;

	UPROPERTY()
	FWingNotifier Notifier;
	FWingHandlerConfig* LastHandler;
	bool bSuggestHandlerHelp;
	TSet<FName> SuggestedManualSections;
	FLogCaptureOutputDevice LogCapture;                  // installed once at startup, enabled per-request
	TArray<FWingHandlerConfig> WingHandlerRegistry;  // sorted by Name
	void BuildWingHandlerRegistry();
	FDelegateHandle LoadingPhasesCompleteHandle;
	FWingHandlerConfig* FindHandler(const FString& Name);

	// Handle a complete request and return the response bytes.
	TArray<uint8> HandleRequest(const TArray<uint8>& RequestBytes);
	void PreCallHandler();
	FString PostCallHandler();
	void TryCallHandler(TArrayView<const FString> Argv);

	// ----- TCP server -----
	FSocket* ListenSocket = nullptr;
	int32 Port = 9851;
	bool bRunning = false;

	// ----- Client connections -----
	struct FClientConnection
	{
		FSocket* Socket = nullptr;
		TFuture<void> ThreadFuture;
		bool bDone = false;
	};
	TArray<TSharedPtr<FClientConnection>> Clients;
	void AcceptNewConnections();
	void CleanupFinishedClients();
	static uint32 UnpackBigEndian(const uint8 *Data);
	static bool DeserializeArgv(
		const TArray<uint8>& RequestBytes, TArray<FString>& Argv);
	static void ClientThreadFunc(UWingServer* Server, TSharedPtr<FClientConnection> Client);
	static bool ReceiveRequest(
		FSocket* Socket, TArray<uint8>& OutRequest);
	static bool SendAll(FSocket* Socket, const uint8* Data, int32 BytesToSend);
	static bool ProcessRequestOnGameThread(
		const TArray<uint8>& Request, TArray<uint8>& Response);
	static void WaitForAssetRegistry();

	// ----- The Critical Section -----
	struct FPendingMessage
	{
		TArray<uint8> Request;
		TPromise<TArray<uint8>> Response;
		FPendingMessage() : Response(TPromise<TArray<uint8>>()) {}
	};
	FCriticalSection Mutex;
	TArray<TSharedPtr<FPendingMessage>> PendingMessages;
	bool bShuttingDown = false;
};
