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

-- Configuration
local SPAWN_POSITION = Vector3.new(-15, 19, -63)
local CURRENCY_NAME = "Skira"
local CAMERA_ROTATION_SPEED = 0.1 -- Rotations per second

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

		if instance:IsA("BasePart") then
			local originalCFrame = instance:GetAttribute("OriginalCFrame")
			local originalPosition = instance:GetAttribute("OriginalPosition")

			if originalCFrame then
				if typeof(originalCFrame) == "CFrame" then
					instance.CFrame = originalCFrame
				elseif typeof(originalCFrame) == "Vector3" then
					instance.Position = originalCFrame
				end
			elseif originalPosition then
				if typeof(originalPosition) == "Vector3" then
					instance.Position = originalPosition
				elseif typeof(originalPosition) == "CFrame" then
					instance.CFrame = originalPosition
				end
			end
		end

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

	recursiveClean(model)
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

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

local function WarnMessage(message)
	warn("[Shop System] " .. message)
end

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

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

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 (150) 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.BackgroundColor = 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

		-- Handle Limited items
		if itemData.Limited then
			local spawnedItemsFolder = workspace:FindFirstChild("SpawnedItems")
			local function updateLimitedItem()
				local itemExists = false
				spawnedItemsFolder = workspace:FindFirstChild("SpawnedItems")
				if spawnedItemsFolder then
					-- Check if any descendants have the same name
					for _, item in ipairs(spawnedItemsFolder:GetDescendants()) do
						if item.Name == itemData.Name then
							itemExists = true
							break
						end
					end
				end

				if itemExists then
					-- Item is already spawned, disable buy button
					buyButton.Text = "Out of Stock"
					buyButton.Active = false
					buyButton.AutoButtonColor = false
					buyButton.BackgroundColor3 = Color3.fromRGB(128, 128, 128)
				else
					-- Item is available
					buyButton.Text = tostring(itemData.Price) .. " " .. CURRENCY_NAME
					buyButton.Active = true
					buyButton.AutoButtonColor = true
					buyButton.BackgroundColor3 = originalBuyButtonColor
				end
			end

			-- Connect to changes in the 'SpawnedItems' folder to update availability
			local spawnedItemsFolderEvent
			local function setupSpawnedItemsListener()
				spawnedItemsFolder = workspace:FindFirstChild("SpawnedItems")
				if spawnedItemsFolder then
					-- Update initially
					updateLimitedItem()
					-- Connect
					spawnedItemsFolder.ChildAdded:Connect(updateLimitedItem)
					spawnedItemsFolder.ChildRemoved:Connect(updateLimitedItem)
				else
					-- Wait for 'SpawnedItems' folder to appear
					spawnedItemsFolderEvent = workspace.ChildAdded:Connect(function(child)
						if child.Name == "SpawnedItems" then
							spawnedItemsFolderEvent:Disconnect()
							setupSpawnedItemsListener()
						end
					end)
				end
			end

			setupSpawnedItemsListener()
		end

		-- Set up buy button behavior
		buyButton.MouseButton1Click:Connect(function()
			local player = Players.LocalPlayer
			local playerMoney = 250 -- Replace with actual money getting logic

			if playerMoney < itemData.Price then
				return
			end

			-- For limited items, the availability is handled above
			
			local purchaseEvent = ReplicatedStorage:FindFirstChild("PurchaseItem")
			if purchaseEvent and purchaseEvent:IsA("RemoteEvent") then
				purchaseEvent:FireServer(itemData.Name)
			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 + 350)
	end
end

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) -- Optional: Adjust as needed
			layout.Parent = ELEMENTS_CONTAINER
		end

		-- Optional: Add UIPadding
		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 AbsoluteContentSize change
		layout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(UpdateCanvasSize)

		-- 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

InitializeShop()

can you make it so that i cant buy limited items that are already sold? when i click on limited items with an amout of item 1 or more, i can still click to buy them, secondly, make the script a bit more robust and add QOL features if you want, be creative and do your best