-- Server Script: SkiraSystem

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

-- Require the SkiraManager module
local SkiraManager = require(game.ServerScriptService.SkiraManager)

-- Remote Functions and Events
local SkiraUpdateEvent = ReplicatedStorage:FindFirstChild("SkiraUpdateEvent")
if not SkiraUpdateEvent then
	SkiraUpdateEvent = Instance.new("RemoteEvent")
	SkiraUpdateEvent.Name = "SkiraUpdateEvent"
	SkiraUpdateEvent.Parent = ReplicatedStorage
end

local GetPlayerCurrencyFunction = ReplicatedStorage:FindFirstChild("GetPlayerCurrency")
if not GetPlayerCurrencyFunction then
	GetPlayerCurrencyFunction = Instance.new("RemoteFunction")
	GetPlayerCurrencyFunction.Name = "GetPlayerCurrency"
	GetPlayerCurrencyFunction.Parent = ReplicatedStorage
end

local PurchaseItemEvent = ReplicatedStorage:FindFirstChild("PurchaseItem")
if not PurchaseItemEvent then
	PurchaseItemEvent = Instance.new("RemoteEvent")
	PurchaseItemEvent.Name = "PurchaseItem"
	PurchaseItemEvent.Parent = ReplicatedStorage
end

local PurchaseFeedbackEvent = ReplicatedStorage:FindFirstChild("PurchaseFeedback")
if not PurchaseFeedbackEvent then
	PurchaseFeedbackEvent = Instance.new("RemoteEvent")
	PurchaseFeedbackEvent.Name = "PurchaseFeedback"
	PurchaseFeedbackEvent.Parent = ReplicatedStorage
end

local GetShopItemsFunction = ReplicatedStorage:FindFirstChild("GetShopItems")
if not GetShopItemsFunction then
	GetShopItemsFunction = Instance.new("RemoteFunction")
	GetShopItemsFunction.Name = "GetShopItems"
	GetShopItemsFunction.Parent = ReplicatedStorage
end

local IsLimitedItemAvailableFunction = ReplicatedStorage:FindFirstChild("IsLimitedItemAvailable")
if not IsLimitedItemAvailableFunction then
	IsLimitedItemAvailableFunction = Instance.new("RemoteFunction")
	IsLimitedItemAvailableFunction.Name = "IsLimitedItemAvailable"
	IsLimitedItemAvailableFunction.Parent = ReplicatedStorage
end

local LimitedItemStatusChangedEvent = ReplicatedStorage:FindFirstChild("LimitedItemStatusChanged")
if not LimitedItemStatusChangedEvent then
	LimitedItemStatusChangedEvent = Instance.new("RemoteEvent")
	LimitedItemStatusChangedEvent.Name = "LimitedItemStatusChanged"
	LimitedItemStatusChangedEvent.Parent = ReplicatedStorage
end

-- Define Skira accumulation rate
local SKIRA_RATE = 1 -- 1 Skira
local TIME_INTERVAL = 15 -- Every 15 seconds

-- Table to keep track of limited item stocks
local LimitedItemStocks = {}

-- Function to update Skira amount to the client
local function updateClientSkira(player)
	local skiraAmount = SkiraManager:GetPlayerSkira(player)
	SkiraUpdateEvent:FireClient(player, skiraAmount)
end

-- Function to increment Skira every 15 seconds
local function startSkiraAccumulation(player)
	local function accumulateSkira()
		while player.Parent do
			wait(TIME_INTERVAL)
			SkiraManager:AddPlayerSkira(player, SKIRA_RATE)
			-- Update client
			updateClientSkira(player)
		end
	end
	-- Start the accumulation in a separate thread
	coroutine.wrap(accumulateSkira)()
end

-- OnServerInvoke for GetPlayerCurrencyFunction (RemoteFunction)
GetPlayerCurrencyFunction.OnServerInvoke = function(player)
	return SkiraManager:GetPlayerSkira(player)
end

-- OnServerEvent for PurchaseItemEvent (RemoteEvent)
PurchaseItemEvent.OnServerEvent:Connect(function(player, itemName)
	-- Validate the itemName, check if it exists, and get its price
	local itemData = nil
	for _, item in ipairs(ShopItems) do
		if item.Name == itemName then
			itemData = item
			break
		end
	end

	if not itemData then
		-- Item not found
		PurchaseFeedbackEvent:FireClient(player, "Item not found.")
		return
	end

	-- For limited items, check availability
	if itemData.Limited then
		local stock = LimitedItemStocks[itemData.Name] or 0
		if stock <= 0 then
			-- Item is sold out
			PurchaseFeedbackEvent:FireClient(player, "This item is sold out.")
			return
		end
	end

	local playerSkira = SkiraManager:GetPlayerSkira(player)
	if playerSkira >= itemData.Price then
		-- Deduct the price
		SkiraManager:AddPlayerSkira(player, -itemData.Price)
		-- Grant the item to the player
		-- [Your code to give the item to the player]

		-- Decrease the stock if the item is limited
		if itemData.Limited then
			LimitedItemStocks[itemData.Name] = LimitedItemStocks[itemData.Name] - 1
			-- Notify all clients about the limited item status change
			local itemAvailable = LimitedItemStocks[itemData.Name] > 0
			LimitedItemStatusChangedEvent:FireAllClients(itemData.Name, itemAvailable)
		end

		-- Inform the player
		PurchaseFeedbackEvent:FireClient(player, "You have purchased "..itemData.Name.."!")
		-- Update client currency display
		updateClientSkira(player)
	else
		-- Inform the player they don't have enough Skira
		PurchaseFeedbackEvent:FireClient(player, "You do not have enough Skira to purchase "..itemData.Name..".")
	end
end)

-- OnServerInvoke for GetShopItemsFunction (RemoteFunction)
GetShopItemsFunction.OnServerInvoke = function(player)
	return ShopItems
end

-- OnServerInvoke for IsLimitedItemAvailableFunction (RemoteFunction)
IsLimitedItemAvailableFunction.OnServerInvoke = function(player, itemName)
	local itemData = nil
	for _, item in ipairs(ShopItems) do
		if item.Name == itemName and item.Limited then
			itemData = item
			break
		end
	end

	if itemData then
		local stock = LimitedItemStocks[itemData.Name] or 0
		return stock > 0
	else
		-- If the item is not limited or doesn't exist, return true
		return true
	end
end

-- Function to load initial limited item stocks
local function initializeLimitedItemStocks()
	for _, item in ipairs(ShopItems) do
		if item.Limited then
			-- Set initial stock value
			LimitedItemStocks[item.Name] = item.Stock or 0
		end
	end
end

-- Player Added event
Players.PlayerAdded:Connect(function(player)
	SkiraManager:PlayerAdded(player)
	-- Wait a brief moment for the GUI to be created
	wait(1)
	-- Send initial Skira amount to client
	updateClientSkira(player)
	startSkiraAccumulation(player)
end)

-- Player Removing event
Players.PlayerRemoving:Connect(function(player)
	SkiraManager:PlayerRemoving(player)
end)

-- Bind saving data on server shutdown
game:BindToClose(function()
	SkiraManager:SaveAllPlayerData()
end)

-- Initialize limited item stocks
initializeLimitedItemStocks()


-- ShopHandler (Clientsided)

-- Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

-- Configuration
local CURRENCY_NAME = "Skira"
local CAMERA_ROTATION_SPEED = 0.1 -- Rotations per second

-- Remote Functions and Events
local PurchaseItemEvent = ReplicatedStorage:WaitForChild("PurchaseItem")
local IsLimitedItemAvailableFunction = ReplicatedStorage:WaitForChild("IsLimitedItemAvailable")
local LimitedItemStatusChangedEvent = ReplicatedStorage:WaitForChild("LimitedItemStatusChanged")
local GetPlayerCurrencyFunction = ReplicatedStorage:WaitForChild("GetPlayerCurrency")
local PurchaseFeedbackEvent = ReplicatedStorage:WaitForChild("PurchaseFeedback")
local GetShopItemsFunction = ReplicatedStorage:WaitForChild("GetShopItems")
local SkiraUpdateEvent = ReplicatedStorage:WaitForChild("SkiraUpdateEvent")

-- Variables
local ITEMS_FOLDER = ReplicatedStorage:WaitForChild("Spawnable items")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local displayGui = playerGui:WaitForChild("Display")
local TEMPLATE_ELEMENT = displayGui.Frame.SDS.Frame.List.ItemUI
local ELEMENTS_CONTAINER = displayGui.Frame.SDS.Frame.List

-- Required UI elements
local REQUIRED_UI_ELEMENTS = {
	"Name",
	"Description",
	"Optional",
	"Buy",
	"ItemDisplay"
}

-- Shop items table (will be populated from the server)
local ShopItems = {}

-- Current player currency
local playerCurrency = 0

-- Function to display warnings
local function WarnMessage(message)
	warn("[Shop System] " .. message)
end

-- Function to display messages to the player
local function DisplayMessage(message)
	-- Implement your own UI message system here
	-- For example, using a TextLabel or a notification GUI
	print("[Shop Message] " .. message)
end

-- Function to validate UI elements in the template
local function ValidateUIElement(template)
	local missingElements = {}
	for _, element in ipairs(REQUIRED_UI_ELEMENTS) do
		if not template:FindFirstChild(element) then
			table.insert(missingElements, element)
		end
	end

	if #missingElements > 0 then
		WarnMessage(string.format("Missing required UI elements: %s", table.concat(missingElements, ", ")))
		-- Instead of returning false, you can provide a fallback or default value for the missing elements
		for _, element in ipairs(missingElements) do
			if element == "Name" then
				template.Name.Text = "Unknown"
			elseif element == "Description" then
				template.Description.Text = "No description available."
			elseif element == "Optional" then
				template.Optional.Visible = false
			elseif element == "Buy" then
				template.Buy.Text = "N/A"
				template.Buy.Active = false
			end
		end
		return true
	end

	return true
end
-- Function to validate shop item data
local function ValidateShopItem(itemData)
	if type(itemData) ~= "table" then
		WarnMessage("Invalid item data format")
		return false
	end
	local requiredFields = {"Name", "Description", "Price"}
	for _, field in ipairs(requiredFields) do
		if itemData[field] == nil then
			WarnMessage(string.format("Missing required field '%s' in item data", field))
			return false
		end
	end
	return true
end

-- Function to clean the item model for the viewport
local function CleanModel(model)
	local function recursiveClean(instance)
		if instance.Name == "Package" then
			instance:Destroy()
			return
		end

		if instance:IsA("Script") or 
			instance:IsA("LocalScript") or 
			instance:IsA("Sound") or 
			instance:IsA("StringValue") or 
			instance:IsA("ModuleScript") or 
			instance:IsA("RemoteEvent") or 
			instance:IsA("RemoteFunction") or 
			instance:IsA("BindableEvent") or 
			instance:IsA("BindableFunction") then
			instance:Destroy()
			return
		end

		if not (instance:IsA("Part") or 
			instance:IsA("MeshPart") or 
			instance:IsA("SpecialMesh") or 
			instance:IsA("Model") or 
			instance:IsA("Folder")) then
			instance:Destroy()
			return
		end

		for _, child in ipairs(instance:GetChildren()) do
			recursiveClean(child)
		end
	end

	recursiveClean(model)
end

-- Function to set up the item in the viewport
local function SetupItemInViewport(itemDisplay, itemModel)
	local existingViewport = itemDisplay:FindFirstChild("ViewportFrame")
	if existingViewport then
		existingViewport:Destroy()
	end

	local viewportFrame = Instance.new("ViewportFrame")
	viewportFrame.Name = "ViewportFrame"
	viewportFrame.Size = UDim2.new(1, 0, 1, 0)
	viewportFrame.BackgroundTransparency = 1
	viewportFrame.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
	viewportFrame.Parent = itemDisplay

	local camera = Instance.new("Camera")
	camera.Parent = viewportFrame
	viewportFrame.CurrentCamera = camera

	local modelClone =  itemModel:Clone()
	CleanModel(modelClone)
	modelClone.Parent = viewportFrame

	local center = Vector3.new(0, 0, 0)
	local size = Vector3.new(0, 0, 0)
	if modelClone:IsA("Model") then
		local boundingCFrame, boundingSize = modelClone:GetBoundingBox()
		center = boundingCFrame.Position
		size = boundingSize
	elseif modelClone:IsA("BasePart") then
		center = modelClone.Position
		size = modelClone.Size
	end

	local maxSize = math.max(size.X, size.Y, size.Z)
	local distance = maxSize * 2
	local cameraPosition = center + Vector3.new(0, 0, distance)
	camera.CFrame = CFrame.new(cameraPosition, center)

	local startTime = tick()
	local rotationConnection
	rotationConnection = RunService.RenderStepped:Connect(function()
		if not viewportFrame.Parent or not camera.Parent then
			rotationConnection:Disconnect()
			return
		end

		local elapsed = tick() - startTime
		local angle = elapsed * (2 * math.pi * CAMERA_ROTATION_SPEED)
		local newPosition = center + Vector3.new(
			math.cos(angle) * distance,
			0,
			math.sin(angle) * distance
		)
		camera.CFrame = CFrame.new(newPosition, center)
	end)
end

-- Function to set up the initial ViewportFrame in the template
local function SetupTemplateViewport()
	local template = TEMPLATE_ELEMENT
	if not template then
		warn("Template not found!")
		return
	end

	local itemDisplay = template:FindFirstChild("ItemDisplay")
	if not itemDisplay then
		itemDisplay = Instance.new("Frame")
		itemDisplay.Name = "ItemDisplay"
		itemDisplay.Size = UDim2.new(1, 0, 1, 0)
		itemDisplay.BackgroundTransparency = 1
		itemDisplay.Parent = template
	end

	local viewportFrame = Instance.new("ViewportFrame")
	viewportFrame.Name = "ViewportFrame"
	viewportFrame.Size = UDim2.new(1, 0, 1, 0)
	viewportFrame.BackgroundTransparency = 1
	viewportFrame.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
	viewportFrame.Parent = itemDisplay

	local camera = Instance.new("Camera")
	camera.Parent = viewportFrame
	viewportFrame.CurrentCamera = camera
end

-- Function to get the player's currency
local function GetPlayerCurrency()
	if GetPlayerCurrencyFunction and GetPlayerCurrencyFunction:IsA("RemoteFunction") then
		local success, result = pcall(function()
			return GetPlayerCurrencyFunction:InvokeServer()
		end)
		if success then
			playerCurrency = result -- Update the local variable
			return result
		else
			WarnMessage("Failed to get player currency")
			return 0
		end
	else
		WarnMessage("GetPlayerCurrency RemoteFunction not found")
		return 0
	end
end

-- Function to check limited item availability
local function CheckLimitedItemAvailability(itemName)
	if IsLimitedItemAvailableFunction and IsLimitedItemAvailableFunction:IsA("RemoteFunction") then
		local success, result = pcall(function()
			return IsLimitedItemAvailableFunction:InvokeServer(itemName)
		end)
		if success then
			return result
		else
			WarnMessage("Failed to get availability for item: " .. itemName)
			return false
		end
	else
		WarnMessage("IsLimitedItemAvailable RemoteFunction not found")
		return false
	end
end

-- Function to create a shop item UI element
local function CreateShopItem(template, itemData)
	if not template then
		WarnMessage("Shop item template is nil")
		return nil
	end

	if not ValidateShopItem(itemData) then
		WarnMessage(string.format("Invalid shop item data for item: %s", itemData.Name or "Unknown"))
		return nil
	end

	if not ValidateUIElement(template) then
		WarnMessage("Template validation failed")
		return nil
	end

	local newItem = template:Clone()
	newItem.Visible = true

	-- Set the size of the newItem to a fixed height
	newItem.Size = UDim2.new(1, 0, 0, 150) -- Adjust the height as needed

	-- Set up item UI elements (Name, Description, Buy button, etc.)
	local itemDisplay = newItem:FindFirstChild("ItemDisplay")
	if itemDisplay then
		local itemModel = ITEMS_FOLDER:FindFirstChild(itemData.Name)
		if itemModel then
			SetupItemInViewport(itemDisplay, itemModel)
		else
			WarnMessage("Item model not found: " .. itemData.Name)
		end
	end

	local nameLabel = newItem:FindFirstChild("Name")
	local descriptionLabel = newItem:FindFirstChild("Description")
	local buyButton = newItem:FindFirstChild("Buy")
	local optionalLabel = newItem:FindFirstChild("Optional")

	if nameLabel then nameLabel.Text = itemData.Name end
	if descriptionLabel then descriptionLabel.Text = itemData.Description end
	if buyButton then buyButton.Text = tostring(itemData.Price) .. " " .. CURRENCY_NAME end

	if optionalLabel then
		if itemData.New then
			optionalLabel.Text = "New!"
			optionalLabel.Visible = true
		elseif itemData.Limited then
			optionalLabel.Text = "Limited"
			optionalLabel.BackgroundColor3 = Color3.new(0.690196, 0.443137, 0.145098)
			optionalLabel.Visible = true
		else
			optionalLabel.Visible = false
		end
	end

	local originalBuyButtonColor
	if buyButton then
		originalBuyButtonColor = buyButton.BackgroundColor3  -- Store the original color

		local function updateBuyButton()
			local itemAvailable = true
			if itemData.Limited then
				itemAvailable = CheckLimitedItemAvailability(itemData.Name)
			end

			if itemAvailable then
				-- Item is available
				buyButton.Text = tostring(itemData.Price) .. " " .. CURRENCY_NAME
				buyButton.Active = true
				buyButton.AutoButtonColor = true
				buyButton.BackgroundColor3 = originalBuyButtonColor
			else
				-- Item is sold out
				buyButton.Text = "Out of Stock"
				buyButton.Active = false
				buyButton.AutoButtonColor = false
				buyButton.BackgroundColor3 = Color3.fromRGB(128, 128, 128)
			end
		end

		updateBuyButton()

		-- Listen for LimitedItemStatusChangedEvent if the item is limited
		if LimitedItemStatusChangedEvent and LimitedItemStatusChangedEvent:IsA("RemoteEvent") then
			LimitedItemStatusChangedEvent.OnClientEvent:Connect(function(changedItemName, availability)
				if changedItemName == itemData.Name then
					updateBuyButton()
				end
			end)
		else
			WarnMessage("LimitedItemStatusChangedEvent not found or not a RemoteEvent")
		end

		-- Set up buy button behavior
		buyButton.MouseButton1Click:Connect(function()
			playerCurrency = GetPlayerCurrency() -- Update the local currency variable

			if playerCurrency < itemData.Price then
				-- Display message to the player that they don't have enough money
				local insufficientFundsMessage = "You don't have enough " .. CURRENCY_NAME .. " to buy " .. itemData.Name .. "."
				DisplayMessage(insufficientFundsMessage)
				return
			end

			-- For limited items, check availability again before buying
			if itemData.Limited then
				local itemAvailable = CheckLimitedItemAvailability(itemData.Name)
				if not itemAvailable then
					-- Item is sold out
					DisplayMessage("This item is sold out.")
					updateBuyButton()
					return
				end
			end

			if PurchaseItemEvent and PurchaseItemEvent:IsA("RemoteEvent") then
				PurchaseItemEvent:FireServer(itemData.Name)
				-- Optionally, disable the buy button to prevent multiple clicks
				-- buyButton.Active = false
			else
				WarnMessage("PurchaseItem RemoteEvent not found or is not a RemoteEvent")
			end
		end)
	end

	newItem.Parent = ELEMENTS_CONTAINER -- Ensure items are parented to the ScrollingFrame

	return newItem
end

-- Function to update the CanvasSize of the ScrollingFrame
local function UpdateCanvasSize()
	local layout = ELEMENTS_CONTAINER:FindFirstChildWhichIsA("UIGridStyleLayout")
	if layout then
		local contentSize = layout.AbsoluteContentSize
		local padding = ELEMENTS_CONTAINER:FindFirstChildOfClass("UIPadding")
		if padding then
			contentSize = Vector2.new(
				contentSize.X + padding.PaddingLeft.Offset + padding.PaddingRight.Offset,
				contentSize.Y + padding.PaddingTop.Offset + padding.PaddingBottom.Offset
			)
		end
		ELEMENTS_CONTAINER.CanvasSize = UDim2.new(0, contentSize.X, 0, contentSize.Y + 400)
	else
		print("Layout not found in ELEMENTS_CONTAINER")
	end
end

-- Function to clear existing shop items
local function ClearShopItems()
	for _, child in ipairs(ELEMENTS_CONTAINER:GetChildren()) do
		if child:IsA("Frame") and child ~= TEMPLATE_ELEMENT then
			child:Destroy()
		end
	end
end

local function InitializeShop()
	print("Initializing shop...")
	local success, err = pcall(function()
		ClearShopItems()
		print("Cleared existing shop items")

		local template = TEMPLATE_ELEMENT
		if not template then
			error("Shop template not found")
		end

		SetupTemplateViewport()
		template.Visible = false
		-- Ensure a layout exists
		local layout = ELEMENTS_CONTAINER:FindFirstChildWhichIsA("UIGridStyleLayout")
		if not layout then
			layout = Instance.new("UIListLayout")
			layout.FillDirection = Enum.FillDirection.Vertical
			layout.SortOrder = Enum.SortOrder.LayoutOrder
			layout.Padding = UDim.new(0, 5)
			layout.Parent = ELEMENTS_CONTAINER
		end

		-- Add UIPadding if not present
		local padding = ELEMENTS_CONTAINER:FindFirstChildOfClass("UIPadding")
		if not padding then
			padding = Instance.new("UIPadding")
			padding.PaddingTop = UDim.new(0, 5)
			padding.PaddingBottom = UDim.new(0, 5)
			padding.PaddingLeft = UDim.new(0, 5)
			padding.PaddingRight = UDim.new(0, 5)
			padding.Parent = ELEMENTS_CONTAINER
		end

		-- Connect to changes in AbsoluteContentSize to update CanvasSize
		layout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(UpdateCanvasSize)

		-- Request shop items from the server
		if GetShopItemsFunction and GetShopItemsFunction:IsA("RemoteFunction") then
			local success, result = pcall(function()
				return GetShopItemsFunction:InvokeServer()
			end)
			if success and type(result) == "table" then
				ShopItems = result
			else
				WarnMessage("Failed to get shop items from server")
				return
			end
		else
			WarnMessage("GetShopItems RemoteFunction not found")
			return
		end

		-- Sort items: New items first, then Limited, then others
		table.sort(ShopItems, function(a, b)
			if a.New ~= b.New then
				return a.New
			elseif a.Limited ~= b.Limited then
				return a.Limited
			else
				return a.Name < b.Name
			end
		end)


		-- Populate the shop items
		for _, itemData in ipairs(ShopItems) do
			local item = CreateShopItem(template, itemData)
			if not item then
				WarnMessage(string.format("Failed to create shop item: %s", itemData.Name or "Unknown"))
			end
		end

		print("Shop items populated")
		UpdateCanvasSize()
		print("Canvas size updated")
	end)

	if not success then
		WarnMessage(string.format("Failed to initialize shop: %s", err))
	else
		print("Shop initialized successfully")
	end
end

-- Handle purchase feedback messages from the server
if PurchaseFeedbackEvent and PurchaseFeedbackEvent:IsA("RemoteEvent") then
	PurchaseFeedbackEvent.OnClientEvent:Connect(function(message)
		DisplayMessage(message)
		-- Update the player's currency display if you have one
		playerCurrency = GetPlayerCurrency()
	end)
else
	WarnMessage("PurchaseFeedbackEvent not found or not a RemoteEvent")
end

-- Handle Skira currency updates from the server
if SkiraUpdateEvent and SkiraUpdateEvent:IsA("RemoteEvent") then
	SkiraUpdateEvent.OnClientEvent:Connect(function(newSkiraAmount)
		playerCurrency = newSkiraAmount
		-- Update any currency display UI here
	end)
else
	WarnMessage("SkiraUpdateEvent not found or not a RemoteEvent")
end

local function OnCharacterAdded(character)
	print("Character added, waiting for PlayerGui...")
	playerGui:WaitForChild("Display")
	print("PlayerGui loaded, reinitializing shop...")
	InitializeShop()
end

player.CharacterAdded:Connect(OnCharacterAdded)

-- Initialize the shop when the script first runs
InitializeShop()

-- Also handle the case where the character is already loaded when the script runs
if player.Character then
	OnCharacterAdded(player.Character)
end

-- ServerShop (Serverside)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

-- Configuration
local SPAWN_POSITION = Vector3.new(-15, 19, -63)
local CURRENCY_NAME = "Skira"

-- Remote Events and Functions
local PurchaseItemEvent = ReplicatedStorage:FindFirstChild("PurchaseItem") or Instance.new("RemoteEvent", ReplicatedStorage)
PurchaseItemEvent.Name = "PurchaseItem"

local IsLimitedItemAvailableFunction = ReplicatedStorage:FindFirstChild("IsLimitedItemAvailable") or Instance.new("RemoteFunction", ReplicatedStorage)
IsLimitedItemAvailableFunction.Name = "IsLimitedItemAvailable"

local LimitedItemStatusChangedEvent = ReplicatedStorage:FindFirstChild("LimitedItemStatusChanged") or Instance.new("RemoteEvent", ReplicatedStorage)
LimitedItemStatusChangedEvent.Name = "LimitedItemStatusChanged"

local GetPlayerCurrencyFunction = ReplicatedStorage:FindFirstChild("GetPlayerCurrency") or Instance.new("RemoteFunction", ReplicatedStorage)
GetPlayerCurrencyFunction.Name = "GetPlayerCurrency"

local PurchaseFeedbackEvent = ReplicatedStorage:FindFirstChild("PurchaseFeedback") or Instance.new("RemoteEvent", ReplicatedStorage)
PurchaseFeedbackEvent.Name = "PurchaseFeedback"

local GetShopItemsFunction = ReplicatedStorage:FindFirstChild("GetShopItems") or Instance.new("RemoteFunction", ReplicatedStorage)
GetShopItemsFunction.Name = "GetShopItems"

-- Shop items configuration
local ShopItems = {
	["Blahaj"] = {
		Name = "Blahaj",
		Description = "Best, softest, cutest, squishy toy!",
		Price = 35,
		Limited = false,
		New = false
	},
	["Antler"] = {
		Name = "Antler",
		Description = "Stolen antler that an employee stole after hitting a person",
		Price = 75,
		Limited = false,
		New = false
	},
	["Dynamite"] = {
		Name = "Dynamite",
		Description = "An explosive stick filled with explosive powder.",
		Price = 15,
		Limited = false,
		New = true
	},
	["ECO Bricks Package"] = {
		Name = "ECO Bricks Package",
		Description = "Eco Bricks for your needs!",
		Price = 5,
		Limited = false,
		New = false
	},
	["Glaggle ball"] = {
		Name = "Glaggle ball",
		Description = "If the earth is round, how come the ball ain't even going nowhere?",
		Price = 18,
		Limited = false,
		New = false
	},
	["Mannequin"] = {
		Name = "Mannequin",
		Description = "Has feelings, do not hurt",
		Price = 18,
		Limited = false,
		New = false
	},
	["Maxwell"] = {
		Name = "Maxwell",
		Description = "Found this random cat on the road, decided to name it Maxwell",
		Price = 18,
		Limited = true,
		New = false
	},
}

-- Table to keep track of limited items availability
local LimitedItemsAvailability = {}

-- Initialize limited items availability (1 item per limited item)
for itemName, itemData in pairs(ShopItems) do
	if itemData.Limited then
		LimitedItemsAvailability[itemName] = 1 -- Quantity available
	end
end

-- Function to check if a limited item is available
local function IsLimitedItemAvailable(player, itemName)
	local availability = LimitedItemsAvailability[itemName]
	if availability and availability > 0 then
		return true
	else
		return false
	end
end

-- Bind the RemoteFunction
IsLimitedItemAvailableFunction.OnServerInvoke = IsLimitedItemAvailable

-- Function to get player's currency
local function GetPlayerCurrency(player)
	if player:FindFirstChild("leaderstats") and player.leaderstats:FindFirstChild(CURRENCY_NAME) then
		return player.leaderstats[CURRENCY_NAME].Value
	else
		-- Initialize leaderstats if not present
		local leaderstats = Instance.new("Folder")
		leaderstats.Name = "leaderstats"
		leaderstats.Parent = player

		local currencyValue = Instance.new("IntValue")
		currencyValue.Name = CURRENCY_NAME
		currencyValue.Value = 250 -- Starting amount
		currencyValue.Parent = leaderstats

		return currencyValue.Value
	end
end

-- Bind the RemoteFunction
GetPlayerCurrencyFunction.OnServerInvoke = GetPlayerCurrency

-- Function to handle item purchases
local function HandlePurchase(player, itemName)
	if not player or not itemName then
		warn("[Shop System] Invalid purchase request")
		return
	end

	-- Validate the item
	local itemData = ShopItems[itemName]
	if not itemData then
		warn(string.format("[Shop System] Item '%s' not found in shop data", itemName))
		PurchaseFeedbackEvent:FireClient(player, "Item not found.")
		return
	end

	-- Check if the player can afford the item
	local playerCurrency = GetPlayerCurrency(player)
	if playerCurrency < itemData.Price then
		PurchaseFeedbackEvent:FireClient(player, "You don't have enough " .. CURRENCY_NAME .. " to purchase " .. itemData.Name .. ".")
		return
	end

	-- Handle limited items
	if itemData.Limited then
		local available = IsLimitedItemAvailable(player, itemName)
		if not available then
			PurchaseFeedbackEvent:FireClient(player, "Sorry, " .. itemData.Name .. " is sold out.")
			return
		else
			-- Reduce the availability count
			LimitedItemsAvailability[itemName] = LimitedItemsAvailability[itemName] - 1

			-- Notify all clients about the change in availability
			LimitedItemStatusChangedEvent:FireAllClients(itemName)
		end
	end

	-- Deduct the cost from the player's currency
	if player:FindFirstChild("leaderstats") and player.leaderstats:FindFirstChild(CURRENCY_NAME) then
		player.leaderstats[CURRENCY_NAME].Value = player.leaderstats[CURRENCY_NAME].Value - itemData.Price
	else
		warn("[Shop System] Player does not have the currency value")
		PurchaseFeedbackEvent:FireClient(player, "An error occurred while processing your purchase.")
		return
	end

	-- Spawn the item
	local spawnableItems = ReplicatedStorage:FindFirstChild("Spawnable items")
	if not spawnableItems then
		warn("[Shop System] Spawnable items folder not found")
		PurchaseFeedbackEvent:FireClient(player, "An error occurred while processing your purchase.")
		return
	end

	local spawnableItem = spawnableItems:FindFirstChild(itemData.Name)
	if not spawnableItem then
		warn(string.format("[Shop System] Failed to find item '%s' for purchase!", itemData.Name))
		PurchaseFeedbackEvent:FireClient(player, "An error occurred while processing your purchase.")
		return
	end

	-- Ensure SpawnedItems folder exists
	local spawnedItemsFolder = workspace:FindFirstChild("SpawnedItems")
	if not spawnedItemsFolder then
		spawnedItemsFolder = Instance.new("Folder")
		spawnedItemsFolder.Name = "SpawnedItems"
		spawnedItemsFolder.Parent = workspace
	end

	-- Spawn the item
	local success, newItem = pcall(function()
		local newItem = spawnableItem:Clone()
		newItem.Parent = spawnedItemsFolder

		-- Set the position of the item
		if newItem:IsA("Model") then
			if newItem.PrimaryPart then
				newItem:SetPrimaryPartCFrame(CFrame.new(SPAWN_POSITION))
			else
				-- If no PrimaryPart, move all BaseParts
				local center = newItem:GetBoundingBox().Position
				for _, part in ipairs(newItem:GetDescendants()) do
					if part:IsA("BasePart") then
						part.Position = part.Position - center + SPAWN_POSITION
					end
				end
			end
		elseif newItem:IsA("BasePart") then
			newItem.Position = SPAWN_POSITION
		else
			warn(string.format("[Shop System] Unsupported item type for '%s'", itemData.Name))
			PurchaseFeedbackEvent:FireClient(player, "An error occurred while processing your purchase.")
			return nil
		end

		return newItem
	end)

	if not success or not newItem then
		warn(string.format("[Shop System] Failed to spawn item '%s': %s", itemData.Name, tostring(newItem)))
		PurchaseFeedbackEvent:FireClient(player, "An error occurred while processing your purchase.")
	else
		print(string.format("[Shop System] Successfully spawned item '%s' for player '%s'", itemData.Name, player.Name))
		PurchaseFeedbackEvent:FireClient(player, "You have successfully purchased " .. itemData.Name .. "!")
	end
end

-- Connect the purchase event
PurchaseItemEvent.OnServerEvent:Connect(HandlePurchase)

-- Player added event to initialize currency
Players.PlayerAdded:Connect(function(player)
	-- Initialize leaderstats if not present
	if not player:FindFirstChild("leaderstats") then
		local leaderstats = Instance.new("Folder")
		leaderstats.Name = "leaderstats"
		leaderstats.Parent = player

		local currencyValue = Instance.new("IntValue")
		currencyValue.Name = CURRENCY_NAME
		currencyValue.Value = 250 -- Starting amount
		currencyValue.Parent = leaderstats
	end
end)

-- Function to provide shop items to the client
local function GetShopItems(player)
	-- Create a copy of ShopItems to send to the client
	local itemsToSend = {}
	for itemName, itemData in pairs(ShopItems) do
		-- Copy item data
		local itemCopy = {
			Name = itemData.Name,
			Description = itemData.Description,
			Price = itemData.Price,
			Limited = itemData.Limited,
			New = itemData.New
		}
		table.insert(itemsToSend, itemCopy)
	end
	return itemsToSend
end

GetShopItemsFunction.OnServerInvoke = GetShopItems

Can you make it so that the SkiraSystem only is for Skira (currency) management, and not for payment etc.. 
fix all the scripts, give the full scripts, make it the best possible