// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "AgrarianGameCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UInputAction;
class UAgrarianBuildingPlacementComponent;
class UAgrarianCraftingComponent;
class UAgrarianInventoryComponent;
class UAgrarianSurvivalComponent;
struct FInputActionValue;

DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);

/**
 *  A simple player-controllable third person character
 *  Implements a controllable orbiting camera
 */
UCLASS(abstract)
class AAgrarianGameCharacter : public ACharacter
{
	GENERATED_BODY()

	/** Camera boom positioning the camera behind the character */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
	USpringArmComponent* CameraBoom;

	/** Follow camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
	UCameraComponent* FollowCamera;

	/** Replicated survival state for hunger, thirst, stamina, health, temperature, and injury. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
	UAgrarianSurvivalComponent* SurvivalComponent;

	/** Replicated inventory used by gathering, primitive crafting, and early economy prototypes. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
	UAgrarianInventoryComponent* InventoryComponent;

	/** Primitive crafting component for MVP recipes and future knowledge progression. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
	UAgrarianCraftingComponent* CraftingComponent;

	/** Server-authoritative primitive building placement component. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
	UAgrarianBuildingPlacementComponent* BuildingPlacementComponent;
	
protected:

	/** Jump Input Action */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* JumpAction;

	/** Move Input Action */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* MoveAction;

	/** Look Input Action */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* LookAction;

	/** Mouse Look Input Action */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* MouseLookAction;

	/** Interact Input Action */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* InteractAction;

	/** Hold to sprint while stamina allows it. */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* SprintAction;

	/** Toggle crouch movement stance. */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* CrouchAction;

	/** Toggle prone movement stance. */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* ProneAction;

	/** Toggle between third-person and first-person camera views. */
	UPROPERTY(EditAnywhere, Category="Input")
	UInputAction* ToggleCameraAction;

	/** How far the player can interact with MVP objects. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Interaction", meta = (ClampMin = "100"))
	float InteractionDistance = 450.0f;

	/** Actor currently under the local interaction trace. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Agrarian|Interaction", meta = (AllowPrivateAccess = "true"))
	TObjectPtr<AActor> FocusedInteractableActor = nullptr;

	/** Prompt text for the currently focused interactable. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Agrarian|Interaction", meta = (AllowPrivateAccess = "true"))
	FText InteractionPromptText;

	/** Baseline movement speed before sprint, skill, injury, load, and terrain modifiers. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement", meta = (ClampMin = "0"))
	float WalkSpeed = 140.0f;

	/** Short-burst movement speed used by the first sprinting pass. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement", meta = (ClampMin = "0"))
	float SprintSpeed = 550.0f;

	/** Stamina spent each second while sprinting and moving. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement", meta = (ClampMin = "0"))
	float SprintStaminaCostPerSecond = 28.0f;

	/** Minimum stamina required to start or continue sprinting. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement", meta = (ClampMin = "0", ClampMax = "100"))
	float MinSprintStamina = 5.0f;

	/** Movement multiplier while crouched. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement|Stance", meta = (ClampMin = "0.1", ClampMax = "1"))
	float CrouchSpeedMultiplier = 0.55f;

	/** Movement multiplier while prone. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement|Stance", meta = (ClampMin = "0.05", ClampMax = "1"))
	float ProneSpeedMultiplier = 0.25f;

	/** Age hook used by movement until full lifecycle/aging systems own it. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_MovementModifierState, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0"))
	float AgeYears = 25.0f;

	/** General physical condition scalar reserved for care, illness, sleep, and long-term state. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_MovementModifierState, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "1.25"))
	float PhysicalConditionMultiplier = 1.0f;

	/** Strength scalar that mainly offsets carried-weight penalties. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_MovementModifierState, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "2.0"))
	float StrengthMultiplier = 1.0f;

	/** Endurance scalar that improves stamina efficiency and movement resilience. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_MovementModifierState, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "2.0"))
	float EnduranceMultiplier = 1.0f;

	/** Comfort carry capacity before speed penalties, in item-weight units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0"))
	float ComfortableCarryWeight = 25.0f;

	/** Severe carry capacity where movement is heavily reduced, in item-weight units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0"))
	float HeavyCarryWeight = 60.0f;

	/** Terrain hook for later surface/volume systems. Values below one slow the character. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing = OnRep_MovementModifierState, Category="Agrarian|Movement|Modifiers", meta = (ClampMin = "0.25", ClampMax = "1.25"))
	float TerrainMovementMultiplier = 1.0f;

	/** Third-person spring arm distance used when returning from first person. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Camera", meta = (ClampMin = "0"))
	float ThirdPersonCameraDistance = 400.0f;

	/** Local first-person camera offset relative to the capsule root. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Camera")
	FVector FirstPersonCameraOffset = FVector(0.0f, 0.0f, 72.0f);

	/** True when this local character is using the optional first-person view. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Agrarian|Camera", meta = (AllowPrivateAccess = "true"))
	bool bFirstPersonCamera = false;

	/** Replicated player intent to sprint; actual sprinting also depends on stamina and alive state. */
	UPROPERTY(ReplicatedUsing = OnRep_SprintState, VisibleAnywhere, BlueprintReadOnly, Category="Agrarian|Movement", meta = (AllowPrivateAccess = "true"))
	bool bWantsToSprint = false;

	/** Replicated prone stance for low crawl movement. */
	UPROPERTY(ReplicatedUsing = OnRep_ProneState, VisibleAnywhere, BlueprintReadOnly, Category="Agrarian|Movement|Stance", meta = (AllowPrivateAccess = "true"))
	bool bIsProne = false;

public:

	/** Constructor */
	AAgrarianGameCharacter();	

protected:

	virtual void Tick(float DeltaSeconds) override;
	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

	/** Initialize input action bindings */
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

protected:

	/** Called for movement input */
	void Move(const FInputActionValue& Value);

	/** Called for looking input */
	void Look(const FInputActionValue& Value);

	/** Called for interaction input */
	void Interact();

	/** Called when sprint input is pressed. */
	void StartSprint();

	/** Called when sprint input is released. */
	void StopSprint();

	/** Called when crouch input is pressed. */
	void ToggleCrouchStance();

	/** Called when prone input is pressed. */
	void ToggleProneStance();

	/** Called for camera perspective toggle input */
	void ToggleCameraPerspective();

	/** Applies local camera presentation state. */
	void SetFirstPersonCamera(bool bEnableFirstPerson);

	/** Applies the requested sprint intent locally and, when needed, on the server. */
	void SetWantsToSprint(bool bNewWantsToSprint);

	/** Returns true when the character has enough survival state to sprint. */
	bool CanSprint() const;

	/** Applies current walk or sprint speed to character movement. */
	void ApplyMovementSpeed();

	/** Calculates the current multiplier from survival, carried weight, terrain, and placeholder traits. */
	float CalculateMovementSpeedMultiplier() const;

	float CalculateAgeMovementMultiplier() const;
	float CalculateSurvivalMovementMultiplier() const;
	float CalculateCarryWeightMovementMultiplier() const;
	float CalculateStanceMovementMultiplier() const;

	UFUNCTION()
	void OnRep_SprintState();

	UFUNCTION()
	void OnRep_ProneState();

	UFUNCTION()
	void OnRep_MovementModifierState();

public:

	/** Handles move inputs from either controls or UI interfaces */
	UFUNCTION(BlueprintCallable, Category="Input")
	virtual void DoMove(float Right, float Forward);

	/** Handles look inputs from either controls or UI interfaces */
	UFUNCTION(BlueprintCallable, Category="Input")
	virtual void DoLook(float Yaw, float Pitch);

	/** Handles jump pressed inputs from either controls or UI interfaces */
	UFUNCTION(BlueprintCallable, Category="Input")
	virtual void DoJumpStart();

	/** Handles jump pressed inputs from either controls or UI interfaces */
	UFUNCTION(BlueprintCallable, Category="Input")
	virtual void DoJumpEnd();

	/** Attempts to interact with the object in front of the player. */
	UFUNCTION(BlueprintCallable, Category="Agrarian|Interaction")
	virtual void TryInteract();

	/** Returns true when the local interaction trace has a usable target. */
	UFUNCTION(BlueprintPure, Category="Agrarian|Interaction")
	bool HasInteractionPrompt() const;

	/** Returns the current local interaction prompt text. */
	UFUNCTION(BlueprintPure, Category="Agrarian|Interaction")
	FText GetInteractionPromptText() const { return InteractionPromptText; }

	/** Returns the actor currently under the local interaction trace, if any. */
	UFUNCTION(BlueprintPure, Category="Agrarian|Interaction")
	AActor* GetFocusedInteractableActor() const { return FocusedInteractableActor; }

	/** Returns true when this local character is using first-person camera presentation. */
	UFUNCTION(BlueprintPure, Category="Agrarian|Camera")
	bool IsFirstPersonCamera() const { return bFirstPersonCamera; }

	/** Returns true when sprint intent and current stamina allow sprinting. */
	UFUNCTION(BlueprintPure, Category="Agrarian|Movement")
	bool IsSprinting() const;

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement|Stance")
	bool IsProne() const { return bIsProne; }

	UFUNCTION(BlueprintCallable, Category="Agrarian|Movement|Stance")
	void SetProne(bool bNewProne);

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement")
	float GetCurrentCarryWeight() const;

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement")
	float GetCurrentMovementSpeedMultiplier() const;

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement|Modifiers")
	float GetAgeYears() const { return AgeYears; }

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement|Modifiers")
	float GetPhysicalConditionMultiplier() const { return PhysicalConditionMultiplier; }

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement|Modifiers")
	float GetStrengthMultiplier() const { return StrengthMultiplier; }

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement|Modifiers")
	float GetEnduranceMultiplier() const { return EnduranceMultiplier; }

	UFUNCTION(BlueprintPure, Category="Agrarian|Movement|Modifiers")
	float GetTerrainMovementMultiplier() const { return TerrainMovementMultiplier; }

	UFUNCTION(BlueprintCallable, Category="Agrarian|Movement")
	void SetTerrainMovementMultiplier(float NewTerrainMovementMultiplier);

	/** Server-authoritative interaction entry point. */
	UFUNCTION(Server, Reliable)
	void ServerInteract(AActor* TargetActor);

	/** Server-authoritative sprint intent update. */
	UFUNCTION(Server, Reliable)
	void ServerSetWantsToSprint(bool bNewWantsToSprint);

	/** Server-authoritative prone stance update. */
	UFUNCTION(Server, Reliable)
	void ServerSetProne(bool bNewProne);

	/** Server-authoritative terrain movement modifier update. */
	UFUNCTION(Server, Reliable)
	void ServerSetTerrainMovementMultiplier(float NewTerrainMovementMultiplier);

	/** Refreshes local interactable focus and prompt text. */
	void UpdateInteractionPrompt();

	/** Finds a valid interactable in front of this character. */
	AActor* FindFocusedInteractable(FText* OutPromptText = nullptr) const;

public:

	/** Returns CameraBoom subobject **/
	FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }

	/** Returns FollowCamera subobject **/
	FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }

	/** Returns SurvivalComponent subobject **/
	FORCEINLINE UAgrarianSurvivalComponent* GetSurvivalComponent() const { return SurvivalComponent; }

	/** Returns InventoryComponent subobject **/
	FORCEINLINE UAgrarianInventoryComponent* GetInventoryComponent() const { return InventoryComponent; }

	/** Returns CraftingComponent subobject **/
	FORCEINLINE UAgrarianCraftingComponent* GetCraftingComponent() const { return CraftingComponent; }

	/** Returns BuildingPlacementComponent subobject **/
	FORCEINLINE UAgrarianBuildingPlacementComponent* GetBuildingPlacementComponent() const { return BuildingPlacementComponent; }
};
