-- MainPluginScript.lua

local plugin = plugin

local UI = require(script.Parent.UI)
local Serializer = require(script.Parent.Serializer)
local Rechecker = require(script.Parent.Rechecker) -- Require the Rechecker module

-- Create the UI
local uiElements = UI.createWidget(plugin)

-- Handle the button click to open/close the widget
uiElements.button.Click:Connect(function()
	uiElements.widget.Enabled = not uiElements.widget.Enabled
end)

-- Function to collect selected objects from the UI
local function getSelectedObjects()
	local selectedObjects = {}
	for _, obj in ipairs(uiElements.objectList or {}) do
		local checkboxInfo = uiElements.objectCheckboxes[obj]
		if checkboxInfo and checkboxInfo.isSelected() then
			table.insert(selectedObjects, obj)
		end
	end
	return selectedObjects
end

-- Function to collect all game objects for serialization
local function getAllGameObjects()
	local allObjects = {}

	local servicesToInclude = {
		"Workspace",
		"Lighting",
		"ReplicatedFirst",
		"ReplicatedStorage",
		"ServerScriptService",
		"ServerStorage",
		"StarterGui",
		"StarterPack",
		"StarterPlayer",
		-- Add other services as needed
	}

	for _, serviceName in ipairs(servicesToInclude) do
		local success, service = pcall(function()
			return game:GetService(serviceName)
		end)
		if success and service then
			table.insert(allObjects, service)
		else
			warn("Failed to get service:", serviceName)
		end
	end

	return allObjects
end

-- Function to handle serialization
local function onSerializeButtonClicked()
	local isAllGameConversionEnabled = uiElements.isAllGameConversionEnabled()

	local objectsToSerialize = {}

	if isAllGameConversionEnabled then
		-- Get all objects in the game
		objectsToSerialize = getAllGameObjects()
	else
		-- Get selected objects
		objectsToSerialize = getSelectedObjects()
		if #objectsToSerialize == 0 then
			warn("No objects selected for serialization.")
			uiElements.progressLabel.Text = "No objects selected for serialization."
			return
		end
	end

	print("Number of initial objects to serialize:", #objectsToSerialize)

	-- Prepare the serialization queue
	local instancesQueue = {}
	for _, obj in ipairs(objectsToSerialize) do
		table.insert(instancesQueue, obj)
	end

	local instanceMap = {}        -- Map to store instance references
	local serializedData = {}     -- List to keep serialized data
	local skippedInstances = 0    -- Count of instances that were skipped due to errors
	local totalObjectsProcessed = 0  -- We will increment this as we process instances
	local totalObjectsToProcess = #instancesQueue

	uiElements.progressLabel.Text = "Serialization in progress..."
	uiElements.progressBar.Size = UDim2.new(0, 0, 1, 0)  -- Reset progress bar

	local RunService = game:GetService("RunService")
	local startTime = tick()

	-- Function to proceed to generating reconstruction scripts
	local function proceedToGenerateScripts()
		if #serializedData == 0 then
			warn("No data was serialized. Serialization aborted.")
			uiElements.progressLabel.Text = "Serialization aborted. No data was serialized."
			return
		end

		-- Generate reconstruction scripts (multiple scripts if necessary)
		local maxScriptSize = 199000  -- Adjust as needed (Roblox's script size limit is around 200,000 characters)
		local reconstructionScriptsData = Serializer.generateReconstructionScripts(serializedData, maxScriptSize)

		-- Create the reconstruction scripts within the Plugin's parent (e.g., ServerScriptService)
		local scriptsFolder = Instance.new("Folder")
		scriptsFolder.Name = "ReconstructionScripts"
		scriptsFolder.Parent = game:GetService("ServerScriptService")  -- Placing in ServerScriptService to run on server

		-- Create a BoolValue to signal when reconstruction is complete
		local reconstructionCompleteSignal = Instance.new("BoolValue")
		reconstructionCompleteSignal.Name = "ReconstructionComplete"
		reconstructionCompleteSignal.Value = false
		reconstructionCompleteSignal.Parent = scriptsFolder

		-- Create and parent each reconstruction script
		for i, scriptData in ipairs(reconstructionScriptsData) do
			local reconstructionScript = Instance.new("Script")
			reconstructionScript.Name = "ReconstructionScript_" .. tostring(i)
			reconstructionScript.Source = scriptData.Source
			reconstructionScript.Disabled = false  -- Enable script
			reconstructionScript.Parent = scriptsFolder
		end

		-- Wait for deserialization to complete
		local deserializationStartTime = tick()
		local maxWaitTime = 600  -- Maximum wait time in seconds (adjust as needed)
		local deserializationComplete = false

		while not deserializationComplete and (tick() - deserializationStartTime) < maxWaitTime do
			RunService.Heartbeat:Wait()
			deserializationComplete = reconstructionCompleteSignal.Value
		end

		if deserializationComplete then
			print("Deserialization complete. Game is ready to load.")
		else
			warn("Deserialization did not complete within the expected time.")
		end

		-- Display time taken
		local endTime = tick()
		local duration = endTime - startTime
		local minutes = math.floor(duration / 60)
		local seconds = duration % 60
		uiElements.progressLabel.Text = string.format("Serialization complete in %d minutes, %.2f seconds.", minutes, seconds)
		uiElements.progressBar.Size = UDim2.new(1, 0, 1, 0)  -- Fill progress bar

		-- Notify user that serialization is complete
		print("Serialization complete. Reconstruction scripts have been generated.")
		if skippedInstances > 0 then
			print(skippedInstances .. " instances were skipped due to errors.")
		end
	end

	-- Serialize instances
	local function serializeNextInstance(onComplete)
		if #instancesQueue == 0 then
			-- Serialization complete
			if onComplete then
				onComplete()
			end
		else
			local instancesPerFrame = 100  -- Adjust this number as needed
			local count = 0

			while count < instancesPerFrame and #instancesQueue > 0 do
				count = count + 1
				local instance = table.remove(instancesQueue, 1)

				-- Serialize the instance
				local success, errorMsg = pcall(function()
					Serializer.serializeInstance(instance, instanceMap, serializedData, instancesQueue)
				end)

				if not success then
					skippedInstances = skippedInstances + 1
					warn("Failed to serialize instance: " .. tostring(instance:GetFullName()) .. ". Error: " .. tostring(errorMsg))
				end

				totalObjectsProcessed = totalObjectsProcessed + 1

				-- Update progress in UI
				local progressPercentage = math.floor((totalObjectsProcessed / totalObjectsToProcess) * 100)
				uiElements.progressLabel.Text = string.format("Serializing... %d%% complete", progressPercentage)
				uiElements.progressBar.Size = UDim2.new(totalObjectsProcessed / totalObjectsToProcess, 0, 1, 0)
			end

			-- Schedule next batch after yielding
			RunService.Heartbeat:Wait()
			serializeNextInstance(onComplete)
		end
	end

	-- Start the serialization process
	serializeNextInstance(function()
		-- After serialization is complete
		-- Perform recheck to find any missing instances
		local missingInstances = Rechecker.verifySerialization(objectsToSerialize, instanceMap)
		if #missingInstances > 0 then
			warn("Some instances were not serialized. Attempting to serialize missing instances.")

			-- Attempt to serialize missing instances
			Serializer.handleMissingInstances(missingInstances, instanceMap, serializedData, instancesQueue)

			-- Re-run serialization if there are instances left in the queue
			if #instancesQueue > 0 then
				totalObjectsToProcess = totalObjectsToProcess + #instancesQueue
				serializeNextInstance(function()
					-- After re-serialization, check again for missing instances
					missingInstances = Rechecker.verifySerialization(objectsToSerialize, instanceMap)
					if #missingInstances > 0 then
						warn("Could not serialize all instances after retries.")
					else
						print("All instances have been successfully serialized after retries.")
					end

					-- Proceed to generate reconstruction scripts
					proceedToGenerateScripts()
				end)
			else
				-- Proceed to generate reconstruction scripts even if some instances are missing
				proceedToGenerateScripts()
			end
		else
			print("All instances have been successfully serialized.")
			-- Proceed to generate reconstruction scripts
			proceedToGenerateScripts()
		end
	end)
end

-- Connect the serialize button in the UI to the function
uiElements.serializeButton.MouseButton1Click:Connect(onSerializeButtonClicked)
 -- ScriptGenerator.lua

local ScriptGenerator = {}
local HttpService = game:GetService("HttpService")

function ScriptGenerator.getScriptTemplate()
	local scriptTemplate = [[
-- Reconstruction Script - Part CHUNK_INDEX of TOTAL_CHUNKS

local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")

_G.instanceRegistry = _G.instanceRegistry or {}
_G.parentDataMap = _G.parentDataMap or {}
local instanceRegistry = _G.instanceRegistry
local parentDataMap = _G.parentDataMap

local propertiesToSkip = {
    ["ClassName"] = true,
    ["Parent"] = true,
    ["Archivable"] = true,
}

local function recreateValue(value)
    if type(value) == "table" and value.__type then
        local valueType = value.__type
        if valueType == "EnumItem" then
            local enumFullName = value.Value
            local enumTypeName, enumName = enumFullName:match("Enum%.(%w+)%.(.+)")
            if enumTypeName and enumName then
                return Enum[enumTypeName][enumName]
            else
                warn("Failed to reconstruct EnumItem:", value.Value)
                return nil
            end
        elseif valueType == "BrickColor" then
            return BrickColor.new(value.Number)
        elseif valueType == "Color3" then
            return Color3.new(value.R, value.G, value.B)
        elseif valueType == "Vector3" then
            return Vector3.new(value.X, value.Y, value.Z)
        elseif valueType == "Vector2" then
            return Vector2.new(value.X, value.Y)
        elseif valueType == "CFrame" then
            return CFrame.new(unpack(value.Components))
        elseif valueType == "UDim2" then
            return UDim2.new(value.XScale, value.XOffset, value.YScale, value.YOffset)
        elseif valueType == "UDim" then
            return UDim.new(value.Scale, value.Offset)
        elseif valueType == "NumberRange" then
            return NumberRange.new(value.Min, value.Max)
        elseif valueType == "NumberSequence" then
            local keypoints = {}
            for _, kp in ipairs(value.Keypoints) do
                table.insert(keypoints, NumberSequenceKeypoint.new(kp.Time, kp.Value, kp.Envelope))
            end
            return NumberSequence.new(keypoints)
        elseif valueType == "ColorSequence" then
            local keypoints = {}
            for _, kp in ipairs(value.Keypoints) do
                local color = Color3.new(kp.Value.R, kp.Value.G, kp.Value.B)
                table.insert(keypoints, ColorSequenceKeypoint.new(kp.Time, color))
            end
            return ColorSequence.new(keypoints)
        elseif valueType == "Rect" then
            return Rect.new(value.Min.X, value.Min.Y, value.Max.X, value.Max.Y)
        elseif valueType == "PhysicalProperties" then
            return PhysicalProperties.new(value.Density, value.Friction, value.Elasticity, value.FrictionWeight, value.ElasticityWeight)
        elseif valueType == "Font" then
            return Font.new(value.Family, Enum.FontWeight[value.Weight], Enum.FontStyle[value.Style])
        elseif valueType == "Faces" then
            -- Reconstruct Faces from table of booleans
            local faces = {}
            for face, included in pairs(value) do
                if included then
                    table.insert(faces, Enum.NormalId[face])
                end
            end
            return Faces.new(unpack(faces))
        elseif valueType == "Axes" then
            -- Reconstruct Axes from table of booleans
            local axes = {}
            for axis, included in pairs(value) do
                if included then
                    table.insert(axes, Enum.Axis[axis])
                end
            end
            return Axes.new(unpack(axes))
        elseif valueType == "Instance" then
            return instanceRegistry[value.__ref]
        else
            return nil
        end
    else
        return value
    end
end

local function deserializeInstance(data)
    local instance

    -- Handle non-creatable classes
    local nonCreatableClasses = {
        Workspace = function() return game:GetService("Workspace") end,
        Terrain = function() return workspace.Terrain end,
        Lighting = function() return game:GetService("Lighting") end,
        Players = function() return game:GetService("Players") end,
        ReplicatedFirst = function() return game:GetService("ReplicatedFirst") end,
        ReplicatedStorage = function() return game:GetService("ReplicatedStorage") end,
        ServerScriptService = function() return game:GetService("ServerScriptService") end,
        ServerStorage = function() return game:GetService("ServerStorage") end,
        StarterGui = function() return game:GetService("StarterGui") end,
        StarterPack = function() return game:GetService("StarterPack") end,
        StarterPlayer = function() return game:GetService("StarterPlayer") end,
        -- Add other non-creatable classes as needed
    }

    local getInstanceFunction = nonCreatableClasses[data.ClassName]
    if getInstanceFunction then
        instance = getInstanceFunction()
    else
        local success, result = pcall(function()
            return Instance.new(data.ClassName)
        end)
        if success then
            instance = result
        else
            warn("Failed to create instance of class:", data.ClassName, "-", result)
            return nil
        end
    end

    instanceRegistry[data.__id] = instance

    -- Set properties (excluding Parent)
    for property, value in pairs(data.Properties or {}) do
        if not propertiesToSkip[property] then
            local recreatedValue = recreateValue(value)
            local successProp, errorMsg = pcall(function()
                instance[property] = recreatedValue
            end)
            if not successProp then
                warn("Failed to set property:", property, "on instance:", instance:GetFullName(), "-", errorMsg)
            end
        end
    end

    -- Set attributes
    if data.Attributes then
        for attribute, value in pairs(data.Attributes) do
            local recreatedValue = recreateValue(value)
            local successAttr, errorMsg = pcall(function()
                instance:SetAttribute(attribute, recreatedValue)
            end)
            if not successAttr then
                warn("Failed to set attribute:", attribute, "on instance:", instance:GetFullName(), "-", errorMsg)
            end
        end
    end

    -- Store parent data for later parenting
    parentDataMap[instance] = data
end

local function attemptToSetParents()
    local unresolved = {}
    for instance, data in pairs(parentDataMap) do
        local parentInstance = nil
        local canSetParent = true

        if data.ParentId then
            parentInstance = instanceRegistry[data.ParentId]
            if parentInstance then
                -- Avoid setting Parent of non-parentable instances
                if instance == workspace.Terrain or instance:IsA("Terrain") then
                    canSetParent = false
                end
                if canSetParent then
                    instance.Parent = parentInstance
                else
                    unresolved[instance] = data
                end
            else
                unresolved[instance] = data  -- Parent not yet available
            end
        elseif data.ParentIsGame then
            if instance == workspace.Terrain or instance:IsA("Terrain") then
                canSetParent = false
            end
            if canSetParent then
                instance.Parent = game
            end
        elseif data.ParentIsService and data.ParentClassName then
            parentInstance = game:GetService(data.ParentClassName)
            if parentInstance then
                -- Avoid setting Parent of non-parentable instances
                if instance == workspace.Terrain or instance:IsA("Terrain") then
                    canSetParent = false
                end
                if canSetParent then
                    instance.Parent = parentInstance
                end
            else
                warn("Service not found:", data.ParentClassName)
            end
        elseif data.ParentName and data.ParentClassName then
            local service = game:GetService(data.ParentClassName)
            if service then
                parentInstance = service:FindFirstChild(data.ParentName)
                if parentInstance then
                    -- Avoid setting Parent of non-parentable instances
                    if instance == workspace.Terrain or instance:IsA("Terrain") then
                        canSetParent = false
                    end
                    if canSetParent then
                        instance.Parent = parentInstance
                    else
                        unresolved[instance] = data
                    end
                else
                    unresolved[instance] = data  -- Parent not found yet
                end
            else
                warn("Service not found:", data.ParentClassName)
            end
        else
            -- Default parent
            if instance == workspace.Terrain or instance:IsA("Terrain") then
                canSetParent = false
            end
            if canSetParent then
                instance.Parent = workspace
            end
        end
    end
    parentDataMap = unresolved
end

-- Data string assignment
DATA_STRING_ASSIGNMENT

local data = HttpService:JSONDecode(dataString)

-- Deserialize instances
local totalInstances = #data
local currentIndex = 1

local function deserializeBatch()
    local batchSize = 100  -- Adjust as needed
    local endIndex = math.min(currentIndex + batchSize - 1, totalInstances)
    for i = currentIndex, endIndex do
        local instanceData = data[i]
        deserializeInstance(instanceData)
    end
    currentIndex = endIndex + 1
    if currentIndex <= totalInstances then
        RunService.Heartbeat:Wait()
        deserializeBatch()
    else
        -- All instances created, now set parents
        attemptToSetParents()
        -- Retry setting parents for unresolved instances
        for attempt = 1, 10 do
            if next(parentDataMap) then
                RunService.Heartbeat:Wait()
                attemptToSetParents()
            else
                break
            end
        end
        if next(parentDataMap) then
            for instance, data in pairs(parentDataMap) do
                warn("Unresolved parent for instance:", instance:GetFullName())
                -- Avoid setting Parent of non-parentable instances
                if instance ~= workspace.Terrain and not instance:IsA("Terrain") then
                    instance.Parent = workspace  -- Assign to workspace as fallback
                end
            end
        end
        -- Clean up shared data
        _G.instanceRegistry = nil
        _G.parentDataMap = nil
        print("Deserialization complete.")

        -- Signal that deserialization is complete
        local reconstructionCompleteSignal = script.Parent:FindFirstChild("ReconstructionComplete")
        if reconstructionCompleteSignal then
            reconstructionCompleteSignal.Value = true
        end
    end
end

-- Start deserialization
deserializeBatch()
]]

	return scriptTemplate
end

function ScriptGenerator.generateScript(chunkIndex, totalChunks, dataChunk)
	local scriptTemplate = ScriptGenerator.getScriptTemplate()

	-- Prepare the data string assignment
	local dataStringAssignment = 'local dataString = ' .. string.format('%q', dataChunk)
	-- Escape any '%' in dataStringAssignment
	dataStringAssignment = dataStringAssignment:gsub("%%", "%%%%")

	-- Replace placeholders in the script template
	scriptTemplate = scriptTemplate:gsub("CHUNK_INDEX", tostring(chunkIndex))
	scriptTemplate = scriptTemplate:gsub("TOTAL_CHUNKS", tostring(totalChunks))
	scriptTemplate = scriptTemplate:gsub("DATA_STRING_ASSIGNMENT", dataStringAssignment)

	return scriptTemplate
end

function ScriptGenerator.generateReconstructionScripts(serializedData, maxScriptSize)
	local scripts = {}

	local dataSet = serializedData

	local dataChunks = {}
	local totalInstances = #dataSet
	local index = 1

	-- Get the script template for size estimation
	local emptyDataString = 'local dataString = ""'
	local scriptTemplateWithoutData = ScriptGenerator.getScriptTemplate()
	-- Replace placeholders with sample values
	scriptTemplateWithoutData = scriptTemplateWithoutData:gsub("DATA_STRING_ASSIGNMENT", emptyDataString)
	scriptTemplateWithoutData = scriptTemplateWithoutData:gsub("CHUNK_INDEX", "1")
	scriptTemplateWithoutData = scriptTemplateWithoutData:gsub("TOTAL_CHUNKS", "1")

	-- Estimate base script size
	local baseScriptSize = #scriptTemplateWithoutData

	local dataChunk = {}
	local dataSize = 0

	while index <= totalInstances do
		local instanceData = dataSet[index]
		local instanceDataString = HttpService:JSONEncode(instanceData)
		local instanceDataSize = #instanceDataString

		local tempDataChunk = { table.unpack(dataChunk) }
		table.insert(tempDataChunk, instanceData)
		local dataStringAssignmentSize = #('local dataString = ' .. string.format('%q', HttpService:JSONEncode(tempDataChunk)))

		local estimatedScriptSize = baseScriptSize - #('local dataString = ""') + dataStringAssignmentSize

		if estimatedScriptSize > maxScriptSize then
			if #dataChunk == 0 then
				-- Single instance too large to fit into a script, skip it
				warn("Instance at index " .. index .. " is too large to fit into a script and will be skipped.")
				index = index + 1
			else
				-- Finalize current dataChunk
				table.insert(dataChunks, dataChunk)
				-- Reset dataChunk and dataSize
				dataChunk = {}
				dataSize = 0
			end
		else
			-- Add instanceData to dataChunk
			dataChunk = tempDataChunk
			dataSize = dataSize + instanceDataSize
			index = index + 1
		end
	end

	-- Add any remaining dataChunk
	if #dataChunk > 0 then
		table.insert(dataChunks, dataChunk)
	end

	local totalChunks = #dataChunks

	-- Now generate scripts
	for chunkIndex, dataChunk in ipairs(dataChunks) do
		local dataString = HttpService:JSONEncode(dataChunk)
		local scriptSource = ScriptGenerator.generateScript(chunkIndex, totalChunks, dataString)
		table.insert(scripts, { Source = scriptSource })
	end

	return scripts
end

return ScriptGenerator
-- Serializer.lua

local Serializer = {}

local HttpService = game:GetService("HttpService")
local ScriptGenerator = require(script.Parent.ScriptGenerator)
local RobloxApiDump = require(script.Parent.RobloxApiDump)

-- Function to serialize a value
function Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
	local valueType = typeof(value)

	-- Handle unsupported types
	local unsupportedTypes = {
		["function"] = true,
		["userdata"] = true,
		["RBXScriptConnection"] = true,
		["RBXScriptSignal"] = true,
		["thread"] = true,
	}

	if unsupportedTypes[valueType] then
		return nil
	end

	-- Handle specific types
	if valueType == "EnumItem" then
		return { __type = "EnumItem", Value = tostring(value) }
	elseif valueType == "BrickColor" then
		return { __type = "BrickColor", Number = value.Number }
	elseif valueType == "Color3" then
		return { __type = "Color3", R = value.R, G = value.G, B = value.B }
	elseif valueType == "Vector3" then
		return { __type = "Vector3", X = value.X, Y = value.Y, Z = value.Z }
	elseif valueType == "Vector2" then
		return { __type = "Vector2", X = value.X, Y = value.Y }
	elseif valueType == "CFrame" then
		return { __type = "CFrame", Components = { value:GetComponents() } }
	elseif valueType == "UDim2" then
		return { __type = "UDim2", XScale = value.X.Scale, XOffset = value.X.Offset, YScale = value.Y.Scale, YOffset = value.Y.Offset }
	elseif valueType == "UDim" then
		return { __type = "UDim", Scale = value.Scale, Offset = value.Offset }
	elseif valueType == "NumberRange" then
		return { __type = "NumberRange", Min = value.Min, Max = value.Max }
	elseif valueType == "NumberSequence" then
		local keypoints = {}
		for _, keypoint in ipairs(value.Keypoints) do
			table.insert(keypoints, { Time = keypoint.Time, Value = keypoint.Value, Envelope = keypoint.Envelope })
		end
		return { __type = "NumberSequence", Keypoints = keypoints }
	elseif valueType == "ColorSequence" then
		local keypoints = {}
		for _, keypoint in ipairs(value.Keypoints) do
			table.insert(keypoints, {
				Time = keypoint.Time,
				Value = { R = keypoint.Value.R, G = keypoint.Value.G, B = keypoint.Value.B },
			})
		end
		return { __type = "ColorSequence", Keypoints = keypoints }
	elseif valueType == "Rect" then
		return { __type = "Rect", Min = { X = value.Min.X, Y = value.Min.Y }, Max = { X = value.Max.X, Y = value.Max.Y } }
	elseif valueType == "PhysicalProperties" then
		return {
			__type = "PhysicalProperties",
			Density = value.Density,
			Friction = value.Friction,
			Elasticity = value.Elasticity,
			FrictionWeight = value.FrictionWeight,
			ElasticityWeight = value.ElasticityWeight,
		}
	elseif valueType == "Font" then
		return {
			__type = "Font",
			Family = value.Family,
			Weight = tostring(value.Weight),
			Style = tostring(value.Style),
		}
	elseif valueType == "Faces" then
		-- Serialize Faces as a table of booleans
		return {
			__type = "Faces",
			Top = value.Top,
			Bottom = value.Bottom,
			Left = value.Left,
			Right = value.Right,
			Front = value.Front,
			Back = value.Back,
		}
	elseif valueType == "Axes" then
		-- Serialize Axes as a table of booleans
		return {
			__type = "Axes",
			X = value.X,
			Y = value.Y,
			Z = value.Z,
		}
	elseif valueType == "Instance" then
		-- Handle instance references
		if instanceMap[value] then
			-- Reference to a serialized instance
			return { __type = "Instance", __ref = instanceMap[value] }
		else
			-- The instance has not been serialized yet
			-- We need to serialize it now
			local success, err = pcall(function()
				Serializer.serializeInstance(value, instanceMap, serializedData, instancesQueue)
			end)
			if success then
				return { __type = "Instance", __ref = instanceMap[value] }
			else
				-- Could not serialize the instance; return nil or handle appropriately
				warn("Failed to serialize instance reference:", tostring(value), "-", err)
				return nil
			end
		end
	else
		-- For basic types like string, number, boolean, Content, etc.
		return value
	end
end

-- Function to serialize an instance
function Serializer.serializeInstance(instance, instanceMap, serializedData, instancesQueue)
	-- Early exit if instance is already processed
	if instanceMap[instance] then
		return
	end

	-- Exclude instances that should not be serialized
	if instance == game or instance:IsA("CoreScript") then
		return
	end

	local success, className = pcall(function()
		return instance.ClassName
	end)

	if not success then
		warn("Failed to get ClassName for instance:", tostring(instance))
		return
	end

	-- Generate a unique ID for the instance
	local instanceId = HttpService:GenerateGUID(false)
	instanceMap[instance] = instanceId

	local serialized = {
		ClassName = className,
		__id = instanceId,
		Properties = {},
		Attributes = {},
	}

	-- Serialize properties
	local properties = Serializer.getSerializableProperties(instance)
	for _, propertyData in ipairs(properties) do
		local propertyName = propertyData.Name

		if propertyName ~= "Parent" then
			local successProp, value = pcall(function()
				return instance[propertyName]
			end)
			if successProp then
				local serializedValue = Serializer.serializeValue(value, instanceMap, serializedData, instancesQueue)
				if serializedValue ~= nil then
					serialized.Properties[propertyName] = serializedValue
				else
					-- Handle serialization failure of property value
					warn("Property value could not be serialized:", propertyName, "on instance:", instance:GetFullName())
				end
			else
				-- Handle property get errors gracefully
				warn("Failed to get property:", propertyName, "on instance:", tostring(instance))
			end
		end
	end

	-- Serialize attributes
	local successAttr, attributes = pcall(function()
		return instance:GetAttributes()
	end)
	if successAttr and attributes then
		for attributeName, attributeValue in pairs(attributes) do
			local serializedAttributeValue = Serializer.serializeValue(attributeValue, instanceMap, serializedData, instancesQueue)
			if serializedAttributeValue ~= nil then
				serialized.Attributes[attributeName] = serializedAttributeValue
			end
		end
	else
		warn("Failed to get attributes for instance:", tostring(instance))
	end

	-- Store ParentId to reconstruct the hierarchy
	local successParent, parent = pcall(function()
		return instance.Parent
	end)
	if successParent and parent then
		if instanceMap[parent] then
			-- Parent is a serialized instance
			serialized.ParentId = instanceMap[parent]
		else
			-- Parent is an external instance or service
			if parent == game then
				serialized.ParentIsGame = true
			else
				-- Attempt to get service if parent is a service
				local successService, parentService = pcall(game.GetService, game, parent.ClassName)
				if successService and parentService == parent then
					serialized.ParentIsService = true
					serialized.ParentClassName = parent.ClassName
				else
					serialized.ParentName = parent.Name
					serialized.ParentClassName = parent.ClassName
				end
			end
		end
	else
		-- Instance has no parent; default to game during deserialization
		serialized.ParentIsGame = true
	end

	-- Add serialized data to the list
	table.insert(serializedData, serialized)

	-- Add immediate children to the queue to process, if any
	local canAccess, children = pcall(function()
		return instance:GetChildren()
	end)
	if canAccess and children then
		for _, child in ipairs(children) do
			if not instanceMap[child] then
				table.insert(instancesQueue, child)
			end
		end
	end
end

-- Function to get serializable properties of an instance
function Serializer.getSerializableProperties(instance)
	local properties = {}
	local className = instance.ClassName
	local classData = RobloxApiDump.Classes[className]

	-- Define collectProperties function
	local function collectProperties(clsData)
		if not clsData then
			-- No class data provided
			return
		end

		-- Recursively collect properties from superclass
		if clsData.Superclass and RobloxApiDump.Classes[clsData.Superclass] then
			collectProperties(RobloxApiDump.Classes[clsData.Superclass])
		end

		-- Process properties
		if clsData.Properties then
			for _, propertyData in ipairs(clsData.Properties) do
				local propertyName = propertyData.Name

				-- Filter out properties based on security and attributes
				local isNotBrowsable = false
				local deprecated = false
				local attributes = propertyData.Attributes or {}

				for _, attribute in ipairs(attributes) do
					if attribute == "NotBrowsable" then
						isNotBrowsable = true
					elseif attribute == "Deprecated" then
						deprecated = true
					end
				end

				-- Include specific properties for UnionOperation
				local isUnionOperationSpecialProperty = false
				if instance:IsA("UnionOperation") then
					if propertyName == "Material" or propertyName == "RenderFidelity" or propertyName == "CenterOfMass" or propertyName == "AssemblyCenterOfMass" then
						isUnionOperationSpecialProperty = true
					end
				end

				-- Include properties that are readable at script security level or special UnionOperation properties
				local canRead = propertyData.Security and (propertyData.Security.Read == "None" or propertyData.Security.Read == "PluginSecurity")
				if (canRead and not isNotBrowsable and not deprecated) or isUnionOperationSpecialProperty then
					table.insert(properties, propertyData)
				end
			end
		end
	end

	if classData then
		collectProperties(classData)
	else
		-- If class data is not available, use default properties
		properties = {
			{ Name = "Name", Security = { Read = "None" } },
			{ Name = "Archivable", Security = { Read = "None" } },
			-- Add other common properties if needed
		}
	end

	return properties
end

-- Function to generate reconstruction scripts
function Serializer.generateReconstructionScripts(serializedData, maxScriptSize)
	-- Use the ScriptGenerator to generate the scripts
	local scripts = ScriptGenerator.generateReconstructionScripts(serializedData, maxScriptSize)
	return scripts
end

-- Function to handle missing instances during serialization
function Serializer.handleMissingInstances(missingInstances, instanceMap, serializedData, instancesQueue)
	for _, instance in ipairs(missingInstances) do
		if not instanceMap[instance] then
			table.insert(instancesQueue, instance)
		end
	end
end

return Serializer
make it when its serializing instances if theres found meshparts, it moves them to replicatedstorage with random cframe positions, when joining the game with the reconstructionscripts, the meshparts that were previously stored in replicatedstorage moves back to their own logical, inital position at the workspace. make sure theres no bugs  and send full code