// Copyright ExByte Studios. Item Interaction Ecosystem.

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "ItemDefinitionRow.h"
#include "ItemAuxiliaryRows.h"
#include "ItemContentRows.h"
#include "ItemInstanceData.h"
#include "ItemDatabaseSubsystem.generated.h"

/** One problem found by ValidateData (section 28). */
USTRUCT(BlueprintType)
struct FItemValidationIssue
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly, Category = "Validation")
	FName ItemId;

	UPROPERTY(BlueprintReadOnly, Category = "Validation")
	FString Message;

	UPROPERTY(BlueprintReadOnly, Category = "Validation")
	bool bIsError = true;
};

/**
 * Authoritative, read-only access point for all static item data (section 35 stage 1).
 *
 * Loads the configured data tables once, flattens them into fast lookup maps and
 * exposes definition/property/feature/interaction queries plus redirect resolution
 * and a data validator. Lives on the GameInstance so it is available on both
 * server and clients; the tables themselves are never replicated (section 32).
 */
UCLASS()
class ITEMDATABASE_API UItemDatabaseSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()

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

	/** Convenience accessor from any world context. */
	static UItemDatabaseSubsystem* Get(const UObject* WorldContext);

	/** (Re)load every configured table and rebuild the lookup caches. */
	void RebuildCache();

	// --- Definition lookup ---

	/** Returns the definition for an item (after redirects), or null if unknown. C++ fast path. */
	const FItemDefinitionRow* GetItemDefinition(FName ItemId) const;

	UFUNCTION(BlueprintCallable, Category = "Item|Database")
	bool FindItemDefinition(FName ItemId, FItemDefinitionRow& OutDefinition) const;

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	bool HasItem(FName ItemId) const;

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	void GetAllItemIds(TArray<FName>& OutItemIds) const;

	/** Applies the redirect chain; returns the input unchanged if no redirect exists. */
	UFUNCTION(BlueprintPure, Category = "Item|Database")
	FName ResolveRedirect(FName ItemId) const;

	// --- Instances ---

	/**
	 * Builds a fresh instance of ItemId with a new GUID and its default runtime
	 * properties applied. The single correct way to mint an item (server-side).
	 */
	UFUNCTION(BlueprintCallable, Category = "Item|Database")
	FItemInstanceData MakeItemInstance(FName ItemId, int32 Quantity = 1) const;

	/** Writes the item's default runtime properties into Out (does not clear first). */
	void ApplyDefaultProperties(FName ItemId, FItemRuntimeProperties& Out) const;

	/** Adds only default properties that are missing (save migration, section 26.2). */
	void ApplyMissingDefaultProperties(FName ItemId, FItemRuntimeProperties& Out) const;

	/** Current schema version stamped onto fresh/migrated instances. */
	static constexpr int32 CurrentItemDataVersion = 1;

	/** Applies redirects + version migration to a loaded instance (section 26). */
	UFUNCTION(BlueprintCallable, Category = "Item|Database")
	void MigrateLoadedItem(UPARAM(ref) FItemInstanceData& Item) const;

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	int32 GetMaxStack(FName ItemId) const;

	// --- Auxiliary data ---

	void GetPropertyRows(FName ItemId, TArray<FItemPropertyRow>& Out) const;
	void GetFeatureRows(FName ItemId, TArray<FItemFeatureRow>& Out) const;

	/** Feature rows for one specific feature tag (e.g. all Rotting params of raw_meat). */
	void GetFeatureRows(FName ItemId, const FGameplayTag& Feature, TArray<FItemFeatureRow>& Out) const;

	void GetInteractionRows(FName ItemId, TArray<FItemInteractionRow>& Out) const;

	/** Per-property replication policy default (section 22), or None if unspecified. */
	EItemPropertyReplicationPolicy GetPropertyPolicy(FName ItemId, const FGameplayTag& PropertyTag) const;

	/** Property tags on an item that are flagged ServerOnly (never sent to clients). */
	void GetServerOnlyProperties(FName ItemId, TArray<FGameplayTag>& Out) const;

	// --- Containers / world slots / loot / recipes (section 6) ---

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	bool FindContainerDef(FGameplayTag ContainerTag, FContainerDefRow& OutDef) const;

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	bool FindWorldSlotDef(FGameplayTag SlotTag, FWorldSlotDefRow& OutDef) const;

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	bool FindRecipe(FName RecipeId, FCraftingRecipeRow& OutRecipe) const;

	UFUNCTION(BlueprintPure, Category = "Item|Database")
	void GetAllRecipes(TArray<FCraftingRecipeRow>& OutRecipes) const;

	void GetLootEntries(FName TableId, TArray<FLootEntryRow>& Out) const;

	/** Rolls a loot table into a list of fresh item instances (server). */
	UFUNCTION(BlueprintCallable, Category = "Item|Database")
	void RollLoot(FName TableId, int32 RollCount, TArray<FItemInstanceData>& OutItems) const;

	// --- Validation (section 28) ---

	UFUNCTION(BlueprintCallable, Category = "Item|Database")
	void ValidateData(TArray<FItemValidationIssue>& OutIssues) const;

private:
	void LoadDefinitions(UDataTable* Table);
	void LoadProperties(UDataTable* Table);
	void LoadFeatures(UDataTable* Table);
	void LoadInteractions(UDataTable* Table);
	void LoadRedirects(UDataTable* Table);
	void LoadContainers(UDataTable* Table);
	void LoadWorldSlots(UDataTable* Table);
	void LoadLoot(UDataTable* Table);
	void LoadRecipes(UDataTable* Table);

	/** ItemId -> definition. */
	TMap<FName, FItemDefinitionRow> Definitions;

	/** ItemId -> default property rows. */
	TMap<FName, TArray<FItemPropertyRow>> PropertiesByItem;

	/** ItemId -> feature parameter rows. */
	TMap<FName, TArray<FItemFeatureRow>> FeaturesByItem;

	/** ItemId -> interaction rows. */
	TMap<FName, TArray<FItemInteractionRow>> InteractionsByItem;

	/** Old -> new id. */
	TMap<FName, FName> Redirects;

	TMap<FGameplayTag, FContainerDefRow> ContainerDefs;
	TMap<FGameplayTag, FWorldSlotDefRow> WorldSlotDefs;
	TMap<FName, TArray<FLootEntryRow>> LootByTable;
	TMap<FName, FCraftingRecipeRow> RecipesById;

	bool bCacheBuilt = false;
};
