// Copyright Pacificao. All Rights Reserved.

#include "AgrarianDebugHUD.h"
#include "AgrarianCraftingComponent.h"
#include "AgrarianGameCharacter.h"
#include "AgrarianInventoryComponent.h"
#include "AgrarianSurvivalComponent.h"
#include "Engine/Canvas.h"
#include "GameFramework/CharacterMovementComponent.h"

void AAgrarianDebugHUD::DrawHUD()
{
	Super::DrawHUD();

	if (!Canvas)
	{
		return;
	}

	const AAgrarianGameCharacter* AgrarianCharacter = Cast<AAgrarianGameCharacter>(GetOwningPawn());
	if (!AgrarianCharacter)
	{
		return;
	}

	DrawInteractionPrompt(AgrarianCharacter);
	DrawCriticalStats(AgrarianCharacter->GetSurvivalComponent());
	const float InventoryBottomY = DrawInventoryPanel(AgrarianCharacter);
	DrawCraftingPanel(AgrarianCharacter, InventoryBottomY + 16.0f);

	if (bShowDebugHUD)
	{
		float Y = 32.0f;
		constexpr float X = 32.0f;

		DrawLine(TEXT("AGRARIAN DEV HUD"), X, Y, FColor(160, 220, 140));
		DrawPlayerStatus(AgrarianCharacter, X, Y);
		DrawSurvival(AgrarianCharacter->GetSurvivalComponent(), X, Y);
		DrawInventory(AgrarianCharacter->GetInventoryComponent(), X, Y);
	}
}

void AAgrarianDebugHUD::DrawInteractionPrompt(const AAgrarianGameCharacter* AgrarianCharacter)
{
	if (!bShowInteractionPrompt || !AgrarianCharacter || !AgrarianCharacter->HasInteractionPrompt() || !Canvas)
	{
		return;
	}

	const FString Prompt = FString::Printf(TEXT("[E] %s"), *AgrarianCharacter->GetInteractionPromptText().ToString());
	float TextWidth = 0.0f;
	float TextHeight = 0.0f;
	GetTextSize(Prompt, TextWidth, TextHeight, nullptr, PromptTextScale);

	const float X = (Canvas->ClipX - TextWidth) * 0.5f;
	const float Y = Canvas->ClipY * 0.62f;
	DrawText(Prompt, FColor(245, 245, 225), X, Y, nullptr, PromptTextScale, true);
}

void AAgrarianDebugHUD::DrawCriticalStats(const UAgrarianSurvivalComponent* SurvivalComponent)
{
	if (!bShowCriticalStatsHUD || !SurvivalComponent || !Canvas)
	{
		return;
	}

	const FAgrarianSurvivalSnapshot& Survival = SurvivalComponent->Survival;
	float Y = FMath::Max(32.0f, Canvas->ClipY - (178.0f * CriticalStatsTextScale));
	constexpr float X = 32.0f;

	const FColor CriticalColor(230, 90, 70);
	const FColor WarningColor(245, 190, 80);
	const FColor StableColor(225, 235, 220);
	const FColor HeaderColor(160, 220, 140);

	auto StatusColor = [CriticalColor, WarningColor, StableColor](float Value, bool bHigherIsWorse = false)
	{
		if (bHigherIsWorse)
		{
			if (Value >= 75.0f)
			{
				return CriticalColor;
			}
			if (Value >= 40.0f)
			{
				return WarningColor;
			}
			return StableColor;
		}

		if (Value <= 20.0f)
		{
			return CriticalColor;
		}
		if (Value <= 45.0f)
		{
			return WarningColor;
		}
		return StableColor;
	};

	DrawScaledLine(TEXT("SURVIVAL"), X, Y, CriticalStatsTextScale, HeaderColor);
	DrawScaledLine(Survival.bIsDead ? TEXT("State     DEAD") : TEXT("State     ALIVE"), X, Y, CriticalStatsTextScale, Survival.bIsDead ? CriticalColor : StableColor);
	DrawScaledLine(FString::Printf(TEXT("Health    %3.0f"), Survival.Health), X, Y, CriticalStatsTextScale, StatusColor(Survival.Health));
	DrawScaledLine(FString::Printf(TEXT("Stamina   %3.0f"), Survival.Stamina), X, Y, CriticalStatsTextScale, StatusColor(Survival.Stamina));
	DrawScaledLine(FString::Printf(TEXT("Food      %3.0f"), Survival.Hunger), X, Y, CriticalStatsTextScale, StatusColor(Survival.Hunger));
	DrawScaledLine(FString::Printf(TEXT("Water     %3.0f"), Survival.Thirst), X, Y, CriticalStatsTextScale, StatusColor(Survival.Thirst));
	DrawScaledLine(FString::Printf(TEXT("Temp      %4.1f C"), Survival.BodyTemperature), X, Y, CriticalStatsTextScale, Survival.BodyTemperature < 35.0f ? CriticalColor : StableColor);
	DrawScaledLine(FString::Printf(TEXT("Shelter   %3.0f%%"), SurvivalComponent->CurrentWeatherProtection * 100.0f), X, Y, CriticalStatsTextScale, SurvivalComponent->CurrentWeatherProtection > 0.0f ? StableColor : WarningColor);
	DrawScaledLine(FString::Printf(TEXT("Expose   x%.2f %+3.1f C"), SurvivalComponent->CurrentWeatherExposureMultiplier, SurvivalComponent->CurrentWeatherTemperatureOffsetC), X, Y, CriticalStatsTextScale, SurvivalComponent->CurrentWeatherExposureMultiplier > 1.0f ? WarningColor : StableColor);
	DrawScaledLine(FString::Printf(TEXT("Exhaust   %3.0f"), Survival.Exhaustion), X, Y, CriticalStatsTextScale, StatusColor(Survival.Exhaustion, true));
	DrawScaledLine(FString::Printf(TEXT("Injury    %3.0f"), Survival.InjurySeverity), X, Y, CriticalStatsTextScale, StatusColor(Survival.InjurySeverity, true));
	DrawScaledLine(FString::Printf(TEXT("Bleeding  %3.0f"), Survival.BleedingSeverity), X, Y, CriticalStatsTextScale, StatusColor(Survival.BleedingSeverity, true));
	DrawScaledLine(FString::Printf(TEXT("Sprain    %3.0f"), Survival.SprainSeverity), X, Y, CriticalStatsTextScale, StatusColor(Survival.SprainSeverity, true));
	DrawScaledLine(FString::Printf(TEXT("Sickness  %3.0f"), Survival.SicknessSeverity), X, Y, CriticalStatsTextScale, StatusColor(Survival.SicknessSeverity, true));
}

float AAgrarianDebugHUD::DrawInventoryPanel(const AAgrarianGameCharacter* AgrarianCharacter)
{
	if (!bShowInventoryHUD || !AgrarianCharacter || !Canvas)
	{
		return 32.0f;
	}

	const UAgrarianInventoryComponent* InventoryComponent = AgrarianCharacter->GetInventoryComponent();
	if (!InventoryComponent)
	{
		return 32.0f;
	}

	const float Scale = FMath::Max(0.25f, InventoryTextScale);
	const float PanelWidth = 360.0f * Scale;
	const float X = FMath::Max(32.0f, Canvas->ClipX - PanelWidth - 32.0f);
	float Y = 32.0f;

	const int32 VisibleRows = InventoryComponent->Items.IsEmpty()
		? 1
		: FMath::Min(InventoryComponent->Items.Num(), FMath::Max(1, MaxInventoryPanelRows));
	const float LineHeight = 18.0f * Scale;
	const float PanelHeight = (56.0f * Scale) + (VisibleRows * LineHeight);

	DrawRect(FLinearColor(0.02f, 0.025f, 0.02f, 0.72f), X - (12.0f * Scale), Y - (10.0f * Scale), PanelWidth, PanelHeight);
	DrawText(
		FString::Printf(
			TEXT("INVENTORY  %d/%d slots  %.1f wt"),
			InventoryComponent->Items.Num(),
			InventoryComponent->MaxSlots,
			InventoryComponent->GetTotalWeight()),
		FColor(160, 220, 140),
		X,
		Y,
		nullptr,
		Scale,
		false);
	Y += 24.0f * Scale;

	if (InventoryComponent->Items.IsEmpty())
	{
		DrawText(TEXT("Empty"), FColor::Silver, X, Y, nullptr, Scale, false);
		return (32.0f + PanelHeight);
	}

	for (int32 Index = 0; Index < VisibleRows; ++Index)
	{
		const FAgrarianItemStack& Stack = InventoryComponent->Items[Index];
		const FText DisplayName = Stack.DisplayName.IsEmpty() ? FText::FromName(Stack.ItemId) : Stack.DisplayName;
		FString ItemName = DisplayName.ToString();
		if (ItemName.Len() > 24)
		{
			ItemName = ItemName.Left(21) + TEXT("...");
		}

		DrawText(
			FString::Printf(TEXT("%02d  %-24s x%-3d %5.1f"), Index + 1, *ItemName, Stack.Quantity, Stack.UnitWeight * Stack.Quantity),
			FColor(225, 235, 220),
			X,
			Y,
			nullptr,
			Scale,
			false);
		Y += LineHeight;
	}

	return (32.0f + PanelHeight);
}

void AAgrarianDebugHUD::DrawCraftingPanel(const AAgrarianGameCharacter* AgrarianCharacter, float TopY)
{
	if (!bShowCraftingHUD || !AgrarianCharacter || !Canvas)
	{
		return;
	}

	const UAgrarianCraftingComponent* CraftingComponent = AgrarianCharacter->GetCraftingComponent();
	const UAgrarianInventoryComponent* InventoryComponent = AgrarianCharacter->GetInventoryComponent();
	if (!CraftingComponent || !InventoryComponent)
	{
		return;
	}

	TArray<FAgrarianRecipe> Recipes;
	CraftingComponent->GetKnownRecipes(Recipes);

	const float Scale = FMath::Max(0.25f, InventoryTextScale);
	const float PanelWidth = 420.0f * Scale;
	const float X = FMath::Max(32.0f, Canvas->ClipX - PanelWidth - 32.0f);
	float Y = FMath::Max(32.0f, TopY);

	const int32 VisibleRows = Recipes.IsEmpty()
		? 1
		: FMath::Min(Recipes.Num(), FMath::Max(1, MaxCraftingPanelRows));
	const float LineHeight = 18.0f * Scale;
	const float PanelHeight = (56.0f * Scale) + (VisibleRows * LineHeight);

	DrawRect(FLinearColor(0.02f, 0.025f, 0.02f, 0.72f), X - (12.0f * Scale), Y - (10.0f * Scale), PanelWidth, PanelHeight);
	DrawText(
		FString::Printf(TEXT("CRAFTING  %d recipes"), Recipes.Num()),
		FColor(160, 220, 140),
		X,
		Y,
		nullptr,
		Scale,
		false);
	Y += 24.0f * Scale;

	if (Recipes.IsEmpty())
	{
		DrawText(TEXT("No known recipes"), FColor::Silver, X, Y, nullptr, Scale, false);
		return;
	}

	for (int32 Index = 0; Index < VisibleRows; ++Index)
	{
		const FAgrarianRecipe& Recipe = Recipes[Index];
		FText FailureReason;
		const bool bCanCraft = CraftingComponent->CanCraft(Recipe.RecipeId, FailureReason);
		FString RecipeName = Recipe.DisplayName.IsEmpty() ? Recipe.RecipeId.ToString() : Recipe.DisplayName.ToString();
		if (RecipeName.Len() > 18)
		{
			RecipeName = RecipeName.Left(15) + TEXT("...");
		}

		FString IngredientSummary;
		for (const FAgrarianItemStack& Ingredient : Recipe.Ingredients)
		{
			if (!IngredientSummary.IsEmpty())
			{
				IngredientSummary += TEXT(" ");
			}
			IngredientSummary += FString::Printf(
				TEXT("%s %d/%d"),
				*Ingredient.ItemId.ToString(),
				InventoryComponent->GetItemCount(Ingredient.ItemId),
				Ingredient.Quantity);
		}

		if (IngredientSummary.Len() > 28)
		{
			IngredientSummary = IngredientSummary.Left(25) + TEXT("...");
		}

		DrawText(
			FString::Printf(TEXT("%02d  %-18s %-28s"), Index + 1, *RecipeName, *IngredientSummary),
			bCanCraft ? FColor(225, 235, 220) : FColor(170, 150, 125),
			X,
			Y,
			nullptr,
			Scale,
			false);
		Y += LineHeight;
	}
}

void AAgrarianDebugHUD::DrawPlayerStatus(const AAgrarianGameCharacter* AgrarianCharacter, float X, float& Y)
{
	if (!AgrarianCharacter)
	{
		DrawLine(TEXT("Player: unavailable"), X, Y, FColor::Red);
		return;
	}

	const UCharacterMovementComponent* MovementComponent = AgrarianCharacter->GetCharacterMovement();
	const FVector Location = AgrarianCharacter->GetActorLocation();
	const float HorizontalSpeed = AgrarianCharacter->GetVelocity().Size2D();
	const FString MovementMode = MovementComponent
		? UEnum::GetValueAsString(MovementComponent->MovementMode)
		: TEXT("Unavailable");

	DrawLine(FString::Printf(
		TEXT("Role: %s | Local: %s"),
		AgrarianCharacter->HasAuthority() ? TEXT("Authority") : TEXT("Simulated/Autonomous"),
		AgrarianCharacter->IsLocallyControlled() ? TEXT("yes") : TEXT("no")),
		X,
		Y,
		FColor(160, 220, 140));
	DrawLine(FString::Printf(TEXT("Location: X %.0f  Y %.0f  Z %.0f"), Location.X, Location.Y, Location.Z), X, Y);
	DrawLine(FString::Printf(TEXT("Speed: %.0f uu/s | Mode: %s"), HorizontalSpeed, *MovementMode), X, Y);
	DrawLine(FString::Printf(
		TEXT("Stance: %s | Sprint: %s | Camera: %s"),
		AgrarianCharacter->IsProne() ? TEXT("Prone") : (AgrarianCharacter->bIsCrouched ? TEXT("Crouched") : TEXT("Standing")),
		AgrarianCharacter->IsSprinting() ? TEXT("yes") : TEXT("no"),
		AgrarianCharacter->IsFirstPersonCamera() ? TEXT("First") : TEXT("Third")),
		X,
		Y);
	DrawLine(FString::Printf(
		TEXT("Move Mult: %.2f | Carry: %.1f"),
		AgrarianCharacter->GetCurrentMovementSpeedMultiplier(),
		AgrarianCharacter->GetCurrentCarryWeight()),
		X,
		Y);
	DrawLine(FString::Printf(
		TEXT("Age %.1f | Cond %.2f | Str %.2f | End %.2f | Terrain %.2f"),
		AgrarianCharacter->GetAgeYears(),
		AgrarianCharacter->GetPhysicalConditionMultiplier(),
		AgrarianCharacter->GetStrengthMultiplier(),
		AgrarianCharacter->GetEnduranceMultiplier(),
		AgrarianCharacter->GetTerrainMovementMultiplier()),
		X,
		Y);
	Y += 10.0f * TextScale;
}

void AAgrarianDebugHUD::DrawSurvival(const UAgrarianSurvivalComponent* SurvivalComponent, float X, float& Y)
{
	if (!SurvivalComponent)
	{
		DrawLine(TEXT("Survival: unavailable"), X, Y, FColor::Red);
		return;
	}

	const FAgrarianSurvivalSnapshot& Survival = SurvivalComponent->Survival;
	DrawLine(FString::Printf(TEXT("Health:  %.0f"), Survival.Health), X, Y);
	DrawLine(FString::Printf(TEXT("Stamina: %.0f"), Survival.Stamina), X, Y);
	DrawLine(FString::Printf(TEXT("Exhaust: %.0f"), Survival.Exhaustion), X, Y);
	DrawLine(FString::Printf(TEXT("Hunger:  %.0f"), Survival.Hunger), X, Y);
	DrawLine(FString::Printf(TEXT("Thirst:  %.0f"), Survival.Thirst), X, Y);
	DrawLine(FString::Printf(TEXT("Temp:    %.1f C"), Survival.BodyTemperature), X, Y);
	DrawLine(FString::Printf(TEXT("State:   %s"), Survival.bIsDead ? TEXT("DEAD") : TEXT("ALIVE")), X, Y, Survival.bIsDead ? FColor::Red : FColor::Green);
	if (Survival.bIsDead)
	{
		DrawLine(FString::Printf(TEXT("Death:   %s"), *Survival.LastDeathReason.ToString()), X, Y, FColor::Red);
	}
	DrawLine(FString::Printf(TEXT("Shelter: %.0f%%"), SurvivalComponent->CurrentWeatherProtection * 100.0f), X, Y);
	DrawLine(FString::Printf(TEXT("Expose:  x%.2f %+3.1f C"), SurvivalComponent->CurrentWeatherExposureMultiplier, SurvivalComponent->CurrentWeatherTemperatureOffsetC), X, Y);
	DrawLine(FString::Printf(TEXT("Injury:  %.0f"), Survival.InjurySeverity), X, Y);
	DrawLine(FString::Printf(TEXT("Bleed:   %.0f"), Survival.BleedingSeverity), X, Y);
	DrawLine(FString::Printf(TEXT("Sprain:  %.0f"), Survival.SprainSeverity), X, Y);
	DrawLine(FString::Printf(TEXT("Sick:    %.0f"), Survival.SicknessSeverity), X, Y);
	const FAgrarianCareHistorySnapshot& Care = SurvivalComponent->CareHistory;
	DrawLine(FString::Printf(TEXT("Care N/S/T: %.2f %.2f %.2f"), Care.NutritionQuality, Care.SleepQuality, Care.TreatmentQuality), X, Y, FColor::Silver);
	DrawLine(FString::Printf(TEXT("Burden I/I/S/W: %.2f %.2f %.2f %.2f"), Care.IllnessBurden, Care.InjuryBurden, Care.StressBurden, Care.WorkloadBurden), X, Y, FColor::Silver);
	Y += 10.0f * TextScale;
}

void AAgrarianDebugHUD::DrawInventory(const UAgrarianInventoryComponent* InventoryComponent, float X, float& Y)
{
	if (!InventoryComponent)
	{
		DrawLine(TEXT("Inventory: unavailable"), X, Y, FColor::Red);
		return;
	}

	DrawLine(FString::Printf(TEXT("Inventory: %d/%d slots"), InventoryComponent->Items.Num(), InventoryComponent->MaxSlots), X, Y, FColor(160, 220, 140));

	if (InventoryComponent->Items.IsEmpty())
	{
		DrawLine(TEXT("- empty"), X, Y, FColor::Silver);
		return;
	}

	for (const FAgrarianItemStack& Stack : InventoryComponent->Items)
	{
		const FText DisplayName = Stack.DisplayName.IsEmpty() ? FText::FromName(Stack.ItemId) : Stack.DisplayName;
		DrawLine(FString::Printf(TEXT("- %s x%d"), *DisplayName.ToString(), Stack.Quantity), X, Y);
	}
}

void AAgrarianDebugHUD::DrawLine(const FString& Text, float X, float& Y, const FColor& Color)
{
	DrawScaledLine(Text, X, Y, TextScale, Color);
}

void AAgrarianDebugHUD::DrawScaledLine(const FString& Text, float X, float& Y, float Scale, const FColor& Color)
{
	DrawText(Text, Color, X, Y, nullptr, Scale, false);
	Y += 18.0f * Scale;
}
