// Copyright Epic Games, Inc. All Rights Reserved.
// UE5 Modular Game Framework — SS_EnhancedInputManager (128)
// Sole authority for Push/Pop input context, key rebinding, and input mode changes.
// In C++, directly wraps UEnhancedInputLocalPlayerSubsystem with priority-based
// context stack management.

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "GameplayTagContainer.h"
#include "InputAction.h"
#include "SS_EnhancedInputManager.generated.h"

class UInputMappingContext;
class UInputAction;
class UEnhancedInputLocalPlayerSubsystem;

/**
 * Input context priority ladder.
 * Higher priority contexts override lower priority for conflicting inputs.
 */
UENUM(BlueprintType)
enum class EInputContextPriority : uint8
{
	Default     = 0    UMETA(DisplayName = "Default (0)"),
	Hiding      = 5    UMETA(DisplayName = "Hiding (5)"),
	Wristwatch  = 10   UMETA(DisplayName = "Wristwatch UI (10)"),
	Inspection  = 20   UMETA(DisplayName = "Inspection (20)"),
	UI          = 100  UMETA(DisplayName = "UI (100)"),
};

/**
 * Mapping context entry in the stack.
 */
USTRUCT(BlueprintType)
struct PG_FRAMEWORK_API FInputContextEntry
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly)
	TObjectPtr<UInputMappingContext> Context = nullptr;

	UPROPERTY(BlueprintReadOnly)
	EInputContextPriority Priority = EInputContextPriority::Default;

	UPROPERTY(BlueprintReadOnly)
	FGameplayTag ContextTag; // For identification: Framework.Input.Context.Default, etc.

	bool operator==(const FInputContextEntry& Other) const
	{
		return Context == Other.Context;
	}
};

// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContextPushed, FGameplayTag, ContextTag, EInputContextPriority, Priority);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContextPopped, FGameplayTag, ContextTag, EInputContextPriority, Priority);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInputModeChanged, bool, bUIMode, bool, bShowCursor);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnKeyRebound, FGameplayTag, ActionTag, FKey, NewKey);

/**
 * SS_EnhancedInputManager — Input Context Stack Authority.
 *
 * Manages all Enhanced Input Mapping Contexts with priority-based ordering.
 * Systems call PushContext/PopContext instead of directly touching the
 * Enhanced Input subsystem. Coordinates input mode (game/UI) with SS_UIManager.
 *
 * C++ gives us direct UEnhancedInputLocalPlayerSubsystem access — no
 * "Get Enhanced Input Local Player Subsystem" node chains.
 */
UCLASS()
class PG_FRAMEWORK_API USS_EnhancedInputManager : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	USS_EnhancedInputManager();

	// ========================================================================
	// Lifecycle
	// ========================================================================

	virtual void Initialize(FSubsystemCollectionBase& Collection) override;
	virtual void Deinitialize() override;

	// ========================================================================
	// Context Stack Management
	// ========================================================================

	/**
	 * Push an input mapping context onto the stack.
	 * Higher priority contexts override lower for conflicting inputs.
	 * Duplicate protection — if the context is already active, it's moved to top.
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void PushContext(UInputMappingContext* Context, EInputContextPriority Priority, FGameplayTag ContextTag);

	/**
	 * Remove an input mapping context from the stack.
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void PopContext(UInputMappingContext* Context);

	/**
	 * Pop a context by its GameplayTag identifier.
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void PopContextByTag(FGameplayTag ContextTag);

	/**
	 * Clear ALL contexts from the stack (e.g., on level transition).
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void ClearAllContexts();

	/**
	 * Returns whether a context is currently active on the stack.
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
	bool IsContextActive(FGameplayTag ContextTag) const;

	/**
	 * Returns the currently highest-priority active context.
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
	FGameplayTag GetTopContext() const;

	// ========================================================================
	// Input Mode Coordination
	// ========================================================================

	/**
	 * Switch between game input mode and UI input mode.
	 * Coordinates cursor visibility and input blocking with SS_UIManager.
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void SetInputMode(bool bUIMode, bool bShowCursor = true, bool bLockMouseToViewport = false);

	/** Returns whether UI input mode is active. */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
	bool IsUIMode() const { return bCurrentUIMode; }

	// ========================================================================
	// Key Rebinding
	// ========================================================================

	/**
	 * Rebind a key for a specific input action.
	 * Persists via SS_SettingsSystem.
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void RebindKey(UInputAction* Action, FKey NewKey, bool bSaveToDisk = true);

	/**
	 * Reset all key bindings to defaults.
	 */
	UFUNCTION(BlueprintCallable, Category = "Framework|Input")
	void ResetAllBindings();

	/**
	 * Get the current key bound to an input action.
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
	FKey GetBoundKey(UInputAction* Action) const;

	// ========================================================================
	// Query
	// ========================================================================

	/**
	 * Check if a specific input action is currently being pressed.
	 * Use this instead of raw Enhanced Input queries.
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
	bool IsActionPressed(UInputAction* Action) const;

	/**
	 * Get the current value of an input action (0.0 to 1.0 for digital, axis value for analog).
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Framework|Input")
	float GetActionValue(UInputAction* Action) const;

	// ========================================================================
	// Configuration
	// ========================================================================

	/** Default input mapping contexts (loaded on initialize). */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Framework|Input|Config")
	TArray<TObjectPtr<UInputMappingContext>> DefaultContexts;

	// ========================================================================
	// Event Dispatchers
	// ========================================================================

	UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
	FOnContextPushed OnContextPushed;

	UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
	FOnContextPopped OnContextPopped;

	UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
	FOnInputModeChanged OnInputModeChanged;

	UPROPERTY(BlueprintAssignable, Category = "Framework|Input|Events")
	FOnKeyRebound OnKeyRebound;

protected:
	// ========================================================================
	// Internal State
	// ========================================================================

	/** Ordered context stack (highest priority at the end/newest). */
	TArray<FInputContextEntry> ContextStack;

	/** Whether UI input mode is currently active. */
	bool bCurrentUIMode = false;

	/** Cached Enhanced Input subsystem. */
	UPROPERTY()
	TObjectPtr<UEnhancedInputLocalPlayerSubsystem> EnhancedInputSubsystem;

	// ========================================================================
	// Internal Methods
	// ========================================================================

	/** Re-sorts the context stack by priority and re-applies to the subsystem. */
	void RebuildContextStack();

	/** Gets the Enhanced Input subsystem, caching it if needed. */
	UEnhancedInputLocalPlayerSubsystem* GetEnhancedInputSubsystem() const;
};
