#pragma once

#include "CoreMinimal.h"
#include "EditorSubsystem.h"
#include "Tickable.h"
#include "TickableEditorObject.h"
#include "Async/Future.h"
#include "Dom/JsonObject.h"
#include "WingUtils.h"
#include "WingNotifier.h"
#include "WingLogCapture.h"
#include "WingHandler.h"
#include "WingManual.h"
#include "WingServer.generated.h"

class FSocket;

/**
 * UWingServer — editor subsystem that listens on a TCP socket and dispatches
 * JSON commands to blueprint editing handlers.
 *
 * Clients connect via TCP and exchange newline-delimited JSON messages.
 * Request format:  {"command": "tool_name", "param1": "value1", ...}
 * Response format: raw JSON result from the handler.
 *
 * 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;
	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); }

	/** Print text to the handler output buffer. */
	static void Print(const TCHAR* Text) { GWingServer->HandlerOutput.Append(Text); }
	static void Print(const FString& Text) { GWingServer->HandlerOutput.Append(Text); }

	/** Print formatted text to the handler output buffer. */
	template <typename FmtType, typename... ArgTypes>
	static void Printf(const FmtType& Fmt, ArgTypes&&... Args)
	{
		GWingServer->HandlerOutput.Appendf(Fmt, Forward<ArgTypes>(Args)...);
	}

	/** Get the handler output buffer directly. */
	static FStringBuilderBase& GetPrintBuffer() { return GWingServer->HandlerOutput; }

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

	/** Whether the server is currently listening. */
	bool IsRunning() const { return bRunning; }

	/** 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, const FString& Documentation);
	static const TArray<FWingHandlerConfig>& AllHandlers() { return GWingServer->WingHandlerRegistry; }

private:
	static UWingServer* GWingServer;

	UPROPERTY()
	FWingNotifier Notifier;
	FWingHandlerConfig* LastHandler;
	TStringBuilder<16384> HandlerOutput;
	TSet<WingManual::Section> SuggestedManualSections;
	FLogCaptureOutputDevice LogCapture;                  // installed once at startup, enabled per-request
	TArray<FWingHandlerConfig> WingHandlerRegistry;  // sorted by Name
	void BuildWingHandlerRegistry();
	FWingHandlerConfig* FindHandler(const FString& Name);

	// Handle a complete JSON line and return the response JSON
	FString HandleRequest(const FString& Line);
	void TryCallHandler(const FString &Line);

	// ----- 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 void ClientThreadFunc(UWingServer* Server, TSharedPtr<FClientConnection> Client);

	// ----- Thread-safe message queue -----
	struct FPendingMessage
	{
		FString Line;
		TPromise<FString> Response;
		FPendingMessage() : Response(TPromise<FString>()) {}
	};
	FCriticalSection Mutex;
	TArray<TSharedPtr<FPendingMessage>> PendingMessages;
	bool bShuttingDown = false;
};
