local D={}local Y=require(script:FindFirstAncestor("\080\108\117\103\105\110\070\114\097\109\101\119\111\114\107"))local R=game:GetService("\082\117\110\083\101\114\118\105\099\101")local y local c local i local v local H local I=R:IsServer()local p=R:IsClient()local g="\083\111\099\107\101\116\080\108\117\103\105\110\082\101\109\111\116\101\070\117\110\099\116\105\111\110"local F="\095\083\111\099\107\101\116\068\105\114\101\099\116\111\114\121\070\111\114\067\108\105\101\110\116\115"local m function D.RunTransfer(D)if I then local D=y.Folders.Directory local Y=D:Clone()Y.Name=F Y.Parent=game.ReplicatedStorage elseif p then local D=H:WaitForChild(game.ReplicatedStorage,F)for D,Y in pairs(y.Folders.Macros:GetChildren())do Y:Destroy()end for D,Y in pairs(y.Folders.LocalMacros:GetChildren())do Y:Destroy()end for D,Y in pairs(y.Folders.Utils:GetChildren())do Y:Destroy()end for D,Y in pairs(D.Utils:GetChildren())do Y.Parent=y.Folders.Utils end for D,Y in pairs(D.Macros:GetChildren())do Y.Parent=y.Folders.Macros end for D,Y in pairs(D.LocalMacros:GetChildren())do Y.Parent=y.Folders.LocalMacros end D:Destroy()end end function D.SetupCommunication(D)m=game.ReplicatedStorage:FindFirstChild(g)if not m then m=Instance.new("\082\101\109\111\116\101\070\117\110\099\116\105\111\110")m.Name=g m.Parent=game.ReplicatedStorage end local function Y(D,Y,R)local H=y.Folders.Macros local I={}for R,y in pairs(H:GetDescendants())do local c=y.Name==D and(y.Parent.Name==Y and not y.Parent:IsA("\077\111\100\117\108\101\083\099\114\105\112\116"))if c then table.insert(I,y)end end local p=#I if p==0 then c:Warn(("\067\111\117\108\100 \110\111\116 \102\105\110\100 \109\097\099\114\111 \116\104\101 \109\097\099\114\111 \116\111 \105\110\118\111\107\101 \040\037\115\041"):format(D))return end if p>1 then c:Warn(("\070\111\117\110\100 \109\117\108\116\105\112\108\101 \109\097\099\114\111\115 \111\102 \110\097\109\101 \037\115 \105\110 \100\105\114\101\099\116\111\114\121 \037\115\046 \080\108\101\097\115\101 \099\104\111\111\111\115\101 \109\111\114\101 \117\110\105\113\117\101 \110\097\109\101\115 \116\111 \098\101 \097\098\108\101 \116\111 \105\110\118\111\107\101 \097\099\114\111\115\115 \115\101\114\118\101\114\047\099\108\105\101\110\116"):format(D,Y))return end local g=I[1]local F=i:GetMacro(g)if not F then c:Warn(("\067\111\117\108\100 \110\111\116 \102\105\110\100 \109\097\099\114\111 \037\115 \105\110\116\101\114\110\097\108\108\121 \111\110 \105\110\118\111\107\101"):format(D))end F.State.FieldValues=R return v:RunMacro(F)end if I then m.OnServerInvoke=function(D,R,y,c)return Y(R,y,c)end elseif p then m.OnClientInvoke=function(D,R,y)return Y(D,R,y)end end end function D.RunMacroOnClient(R,D,Y)return m:InvokeClient(D,Y._script.Name,Y._script.Parent.Name,Y.State.FieldValues)end function D.RunMacroOnServer(Y,D)return m:InvokeServer(D._script.Name,D._script.Parent.Name,D.State.FieldValues)end function D.FrameworkInit(D)y=Y:Require("\083\116\117\100\105\111\072\097\110\100\108\101\114")i=Y:Require("\083\111\099\107\101\116\067\111\110\116\114\111\108\108\101\114")c=Y:Require("\076\111\103\103\101\114")v=Y:Require("\077\097\099\114\111\072\101\108\112\101\114")H=Y:Require("\073\110\115\116\097\110\099\101\085\116\105\108")end function D.FrameworkStart(D) end return D
---
---Handles server/client communication when running
---
---@class MacroClientServer
---
local MacroClientServer = {}

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

--------------------------------------------------
-- Dependencies
local PluginFramework = require(script:FindFirstAncestor("PluginFramework")) ---@type Framework
local RunService = game:GetService("RunService") ---@type RunService
local StudioHandler ---@type StudioHandler
local Logger ---@type Logger
local SocketController ---@type SocketController
local MacroHelper ---@type MacroHelper
local InstanceUtil ---@type InstanceUtil

--------------------------------------------------
-- Constants
local IS_SERVER = RunService:IsServer()
local IS_CLIENT = RunService:IsClient()
local REMOTE_FUNCTION_NAME = "SocketPluginRemoteFunction"
local CLONED_DIRECTORY_FOLDER_NAME = "_SocketDirectoryForClients"

--------------------------------------------------
-- Members
local remoteFunction ---@type RemoteFunction

---
---Call on Server + Client to ensure identical macros on each
---
function MacroClientServer:RunTransfer()
    -- Send over the macros+utils to the client
    if IS_SERVER then
        local directoryFolder = StudioHandler.Folders.Directory
        local directoryFolderClone = directoryFolder:Clone()
        directoryFolderClone.Name = CLONED_DIRECTORY_FOLDER_NAME
        directoryFolderClone.Parent = game.ReplicatedStorage
    elseif IS_CLIENT then
        local clonedDirectoryFolder = InstanceUtil:WaitForChild(game.ReplicatedStorage, CLONED_DIRECTORY_FOLDER_NAME) ---@type Folder

        -- Clear client
        for _, child in pairs(StudioHandler.Folders.Macros:GetChildren()) do
            child:Destroy()
        end
        for _, child in pairs(StudioHandler.Folders.LocalMacros:GetChildren()) do
            child:Destroy()
        end
        for _, child in pairs(StudioHandler.Folders.Utils:GetChildren()) do
            child:Destroy()
        end

        -- Populate from server
        for _, child in pairs(clonedDirectoryFolder.Utils:GetChildren()) do
            child.Parent = StudioHandler.Folders.Utils
        end
        for _, child in pairs(clonedDirectoryFolder.Macros:GetChildren()) do
            child.Parent = StudioHandler.Folders.Macros
        end
        for _, child in pairs(clonedDirectoryFolder.LocalMacros:GetChildren()) do
            child.Parent = StudioHandler.Folders.LocalMacros
        end
        clonedDirectoryFolder:Destroy()
    end
end

---
---Call on Server + Client to setup remote functions
---
function MacroClientServer:SetupCommunication()
    -- Setup remote function
    remoteFunction = game.ReplicatedStorage:FindFirstChild(REMOTE_FUNCTION_NAME)
    if not remoteFunction then
        remoteFunction = Instance.new("RemoteFunction") ---@type RemoteFunction
        remoteFunction.Name = REMOTE_FUNCTION_NAME
        remoteFunction.Parent = game.ReplicatedStorage
    end

    ---@param macroScriptName string
    ---@param macroScriptParentName string
    ---@param macroFieldValues table
    ---@return MacroState
    local function runMacroFromMacroScript(macroScriptName, macroScriptParentName, macroFieldValues)
        -- Try find macro from this
        local macroDirectory = StudioHandler.Folders.Macros

        -- Grab Possible macros
        local possibleMacros = {}
        for _, descendant in pairs(macroDirectory:GetDescendants()) do
            local isPossibleMacro = descendant.Name == macroScriptName
                and descendant.Parent.Name == macroScriptParentName
                and not descendant.Parent:IsA("ModuleScript")
            if isPossibleMacro then
                table.insert(possibleMacros, descendant)
            end
        end

        -- RETURN: No macros found
        local totalPossibleMacros = #possibleMacros
        if totalPossibleMacros == 0 then
            Logger:Warn(("Could not find macro the macro to invoke (%s)"):format(macroScriptName))
            return
        end

        -- RETURN: More than 1 macro found
        if totalPossibleMacros > 1 then
            Logger:Warn(
                (
                    "Found multiple macros of name %s in directory %s. Please chooose more unique names to be able to invoke across server/client"
                ):format(macroScriptName, macroScriptParentName)
            )
            return
        end

        -- Get Macro
        local macroScript = possibleMacros[1]
        local macro = SocketController:GetMacro(macroScript)
        if not macro then
            Logger:Warn(("Could not find macro %s internally on invoke"):format(macroScriptName))
        end

        -- Load on our field values
        macro.State.FieldValues = macroFieldValues

        return MacroHelper:RunMacro(macro)
    end

    if IS_SERVER then
        remoteFunction.OnServerInvoke = function(player, macroScriptName, macroScriptParentName, macroFieldValues)
            return runMacroFromMacroScript(macroScriptName, macroScriptParentName, macroFieldValues)
        end
    elseif IS_CLIENT then
        remoteFunction.OnClientInvoke = function(macroScriptName, macroScriptParentName, macroFieldValues)
            return runMacroFromMacroScript(macroScriptName, macroScriptParentName, macroFieldValues)
        end
    end
end

---
---@param player Player
---@param macro MacroDefinition
---@return MacroState
---
function MacroClientServer:RunMacroOnClient(player, macro)
    return remoteFunction:InvokeClient(player, macro._script.Name, macro._script.Parent.Name, macro.State.FieldValues)
end

---
---@param macro MacroDefinition
---@return MacroState
---
function MacroClientServer:RunMacroOnServer(macro)
    return remoteFunction:InvokeServer(macro._script.Name, macro._script.Parent.Name, macro.State.FieldValues)
end

---
---@private
---
---Asynchronously called with all other FrameworkInit()
---
function MacroClientServer:FrameworkInit()
    StudioHandler = PluginFramework:Require("StudioHandler")
    SocketController = PluginFramework:Require("SocketController")
    Logger = PluginFramework:Require("Logger")
    MacroHelper = PluginFramework:Require("MacroHelper")
    InstanceUtil = PluginFramework:Require("InstanceUtil")
end

---
---@private
---
---Synchronously called, one after the other, with all other FrameworkStart()
---
function MacroClientServer:FrameworkStart() end

return MacroClientServer
