-- Client Script
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")

-- Variables
local ITEMS_FOLDER = ReplicatedStorage:WaitForChild("Spawnable items")
local TEMPLATE_ELEMENT = Players.LocalPlayer.PlayerGui:WaitForChild("Display").Frame.SDS.Frame.List.ItemUI
local ELEMENTS_CONTAINER = Players.LocalPlayer.PlayerGui:WaitForChild("Display").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 = {}

-- 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, ", ")))
		return false
	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
			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 itemData.Limited and LimitedItemStatusChangedEvent and LimitedItemStatusChangedEvent:IsA("RemoteEvent") then
			LimitedItemStatusChangedEvent.OnClientEvent:Connect(function(changedItemName)
				if changedItemName == itemData.Name then
					updateBuyButton()
				end
			end)
		elseif itemData.Limited then
			WarnMessage("LimitedItemStatusChangedEvent not found or not a RemoteEvent")
		end

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

			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)
		print("Updated CanvasSize to:", ELEMENTS_CONTAINER.CanvasSize) -- Debug print
	else
		print("Layout not found in ELEMENTS_CONTAINER")
	end
end

-- Function to initialize the shop
local function InitializeShop()
	local success, err = pcall(function()
		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

		-- Update CanvasSize after adding items
		UpdateCanvasSize()
	end)

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

-- Handle purchase feedback messages from the server
if PurchaseFeedbackEvent and PurchaseFeedbackEvent:IsA("RemoteEvent") then
	PurchaseFeedbackEvent.OnClientEvent:Connect(function(message)
		DisplayMessage(message)
	end)
else
	WarnMessage("PurchaseFeedbackEvent not found or not a RemoteEvent")
end

InitializeShop()

This is a shop handler.. can you make it a new script that will make the Skira system? 1 skira -> 30 seconds, modify Player.playergui.Player.Player2.Points.Text to be "{Number of Skira} Skira" make the money save per user id using the roblox datastore system.. make sure that it's getting saved.. make that new script and modify the shop system to accompany the changes