local W={}local f=game:GetService("\082\117\110\083\101\114\118\105\099\101")local e=game:GetService("\067\104\097\110\103\101\072\105\115\116\111\114\121\083\101\114\118\105\099\101")local R=game:GetService("\083\116\117\100\105\111\083\101\114\118\105\099\101")local r=require(script.Parent.Logger)local N=require(script.Parent.StudioUtil)local o="\095\083\111\099\107\101\116\073\110\115\116\097\110\099\101\115"function W.IntroduceInstance(N,f,R)local o=W:GetDirectory(true)if R then f.Parent=o return end local n=e:TryBeginRecording("\073\110\115\116\097\110\099\101\085\116\105\108\058\073\110\116\114\111\100\117\099\101\073\110\115\116\097\110\099\101")if n then f.Parent=o e:FinishRecording(n,Enum.FinishRecordingOperation.Commit)else r:Warn("\082\101\099\111\114\100\105\110\103 \070\097\105\108\101\100",a,b,c)end end function W.ClearInstance(R,f,e)local N=W:GetDirectory(false)if not N then r:Warn(("\073\110\115\116\097\110\099\101 \037\115 \119\097\115 \110\111\116 \105\110\116\114\111\100\117\099\101\100\063"):format(f:GetFullName()))return end f.Parent=nil if e then f:Destroy()end if#N:GetChildren()==0 then W:Cleanup()end end function W.GetDirectory(f,W)local e=game.Workspace:FindFirstChild(o,true)if not e and W then e=Instance.new("\067\111\110\102\105\103\117\114\097\116\105\111\110")e.Name=o e.Parent=game.Workspace end local R=e and e:FindFirstChild(N:GetUserIdentifier())if not R and W then R=Instance.new("\067\111\110\102\105\103\117\114\097\116\105\111\110")R.Name=N:GetUserIdentifier()R.Parent=e end return R end function W.Cleanup(W)local f=game.Workspace:FindFirstChild(o,true)local e=f and f:FindFirstChild(N:GetUserIdentifier())if e then e:Destroy()end if f and#f:GetChildren()==0 then f:Destroy()end end function W.WaitForChild(r,W,f,e,R)local N=tick()R=R or math.huge while true do local r=W:FindFirstChild(f,e)if r then return r end if tick()>N+R then return end task.wait()end end return W
---
---Nice utility functions to handle multiple instances of Socket running in Team Create
---
---@class InstanceUtil
---
local InstanceUtil = {}

--------------------------------------------------
-- Types
-- ...

--------------------------------------------------
-- Dependencies
local RunService = game:GetService("RunService") ---@type RunService
local ChangeHistoryService = game:GetService("ChangeHistoryService") ---@type ChangeHistoryService
local StudioService = game:GetService("StudioService") ---@type StudioService
local Logger = require(script.Parent.Logger)
local StudioUtil = require(script.Parent.StudioUtil)

--------------------------------------------------
-- Constants
local WORKSPACE_DIRECTORY_NAME = "_SocketInstances"

--------------------------------------------------
-- Members

---
---Brings an instance into the workspace in the context of Socket and the user calling this function.
---By default, sets a ChangeHistoryService waypoint.
---@param instance Instance
---@param dontSetWaypoint boolean
---
---@overload fun(instance:Instance)
---
function InstanceUtil:IntroduceInstance(instance, dontSetWaypoint)
    local directory = InstanceUtil:GetDirectory(true)

    if dontSetWaypoint then
        instance.Parent = directory
        return
    end

    local recording = ChangeHistoryService:TryBeginRecording("InstanceUtil:IntroduceInstance")
    if recording then
        instance.Parent = directory

        ChangeHistoryService:FinishRecording(recording, Enum.FinishRecordingOperation.Commit)
    else
        Logger:Warn("Recording Failed", a, b, c)
    end
end

---
---Clears an instance from our context of Socket and the user calling this function
---@param instance Instance
---@param doDestroy boolean
---
function InstanceUtil:ClearInstance(instance, doDestroy)
    -- Get directory this was introduced into
    local directory = InstanceUtil:GetDirectory(false)
    if not directory then
        Logger:Warn(("Instance %s was not introduced?"):format(instance:GetFullName()))
        return
    end

    -- Manage instance
    instance.Parent = nil
    if doDestroy then
        instance:Destroy()
    end

    -- Manage directory
    if #directory:GetChildren() == 0 then
        InstanceUtil:Cleanup()
    end
end

---
---Gets the directory to place instances in for this user
---@param doCreate boolean
---@return Configuration
---
function InstanceUtil:GetDirectory(doCreate)
    local workspaceDirectory = game.Workspace:FindFirstChild(WORKSPACE_DIRECTORY_NAME, true)
    if not workspaceDirectory and doCreate then
        workspaceDirectory = Instance.new("Configuration") ---@type Configuration
        workspaceDirectory.Name = WORKSPACE_DIRECTORY_NAME
        workspaceDirectory.Parent = game.Workspace
    end

    local userDirectory = workspaceDirectory and workspaceDirectory:FindFirstChild(StudioUtil:GetUserIdentifier())
    if not userDirectory and doCreate then
        userDirectory = Instance.new("Configuration") ---@type Configuration
        userDirectory.Name = StudioUtil:GetUserIdentifier()
        userDirectory.Parent = workspaceDirectory
    end

    return userDirectory
end

---
---Cleans up as much as possible for this User
---
function InstanceUtil:Cleanup()
    local workspaceDirectory = game.Workspace:FindFirstChild(WORKSPACE_DIRECTORY_NAME, true)
    local userDirectory = workspaceDirectory and workspaceDirectory:FindFirstChild(StudioUtil:GetUserIdentifier())

    if userDirectory then
        userDirectory:Destroy()
    end

    if workspaceDirectory and #workspaceDirectory:GetChildren() == 0 then
        workspaceDirectory:Destroy()
    end
end

---
---WaitForChild, but silenty!
---@param parent Instance
---@param instanceName string
---@param recursive boolean
---@param timeout number|nil
---
function InstanceUtil:WaitForChild(parent, instanceName, recursive, timeout)
    -- Setup params
    local startTick = tick()
    timeout = timeout or math.huge

    -- Checker Loop
    while true do
        -- Get Instance
        local child = parent:FindFirstChild(instanceName, recursive)
        if child then
            return child
        end

        -- Timeout
        if tick() > (startTick + timeout) then
            return
        end

        -- Yield
        task.wait()
    end
end

return InstanceUtil
