--// Init


return service.NewProxy({
	__call = function(tab, data)
		local mutex = service.RunService:FindFirstChild("__Adonis_MODULE_MUTEX")
		if mutex then
			warn("\n-----------------------------------------------"
				.."\nAdonis server-side is already running! Aborting..."
				.."\n-----------------------------------------------")
			script:Destroy()
			return "FAILED"
		else
			mutex = service.New("StringValue", {Name = "__Adonis_MODULE_MUTEX", Archivable = false, Value = "Running"})
			local mutexBackup = mutex:Clone()
			local function makePersistent(m)
				local connection1, connection2 = nil, nil
				connection1 = m:GetPropertyChangedSignal("Parent"):Connect(function()
					if not m or m.Parent ~= service.RunService then
						connection1:Disconnect()
						connection2:Disconnect()
						warn("Adonis module mutex removed; Regenerating...")
						makePersistent(mutexBackup)
						mutexBackup.Parent = service.RunService
						mutexBackup = mutexBackup:Clone()
					end
				end)
				connection2 = m:GetPropertyChangedSignal("Name"):Connect(function()
					if m and m.Name ~= "__Adonis_MODULE_MUTEX" then
						warn("Adonis module mutex renamed; Refreshing...")
						m.Name = "__Adonis_MODULE_MUTEX"
					end
				end)
			end
			makePersistent(mutex)
			mutex.Parent = service.RunService
		end

		--// Begin Script Loading
		setfenv(1, setmetatable({}, {__metatable = unique}))
		data = service.Wrap(data or {})

		if not (data and data.Loader) then
			warn("WARNING: MainModule loaded without using the loader!")
		end

		if data and data.ModuleID == 8612978896 then
			warn("Currently using Adonis Nightly MainModule; intended for testing & development only!")
		end

		--// Server Variables
		local setTab = require(server.Deps.DefaultSettings)
		server.Defaults = setTab
		server.Settings = data.Settings or setTab.Settings or {}
		server.OriginalSettings = CloneTable(server.Settings, true)
		server.Descriptions = data.Descriptions or setTab.Descriptions or {}
		server.Messages = data.Messages or setTab.Settings.Messages or {}
		server.Order = data.Order or setTab.Order or {}
		server.Data = data or {}
		server.Model = data.Model or service.New("Model")
		server.ModelParent = data.ModelParent or service.ServerScriptService;
		server.Dropper = data.Dropper or service.New("Script")
		server.Loader = data.Loader or service.New("Script")
		server.Runner = data.Runner or service.New("Script")
		server.LoadModule = LoadModule
		server.LoadPackage = LoadPackage
		server.ServiceSpecific = ServiceSpecific

		server.Shared = Folder.Shared
		server.ServerPlugins = data.ServerPlugins
		server.ClientPlugins = data.ClientPlugins
		server.Client = Folder.Parent.Client

		locals.Settings = server.Settings
		locals.CodeName = server.CodeName

		--// THIS NEEDS TO BE DONE **BEFORE** ANY EVENTS ARE CONNECTED
		if server.Settings.HideScript and data.Model then
			data.Model.Parent = nil
			script:Destroy()
		end

		--// Copy client themes, plugins, and shared modules to the client folder
		local packagesToRunWithPlugins = {}
		local shared = service.New("Folder", {
			Name = "Shared";
			Parent = server.Client;
		})

		for _, module in ipairs(Folder.Shared:GetChildren()) do
			module:Clone().Parent = shared
		end

		for _, module in pairs(data.ClientPlugins or {}) do
			module:Clone().Parent = server.Client.Plugins
		end

		for _, theme in pairs(data.Themes or {}) do
			theme:Clone().Parent = server.Client.UI
		end

		for _, pkg in pairs(data.Packages or {}) do
			LoadPackage(pkg, Folder.Parent, false)
		end

		for setting, value in pairs(server.Defaults.Settings) do
			if server.Settings[setting] == nil then
				server.Settings[setting] = value
			end
		end

		for desc, value in pairs(server.Defaults.Descriptions) do
			if server.Descriptions[desc] == nil then
				server.Descriptions[desc] = value
			end
		end

		--// Bind cleanup
		service.DataModel:BindToClose(CleanUp)
		--server.CleanUp = CleanUp;

		--// Require some dependencies
		server.Typechecker = require(server.Shared.Typechecker)
		server.Changelog = require(server.Shared.Changelog)
		server.Credits = require(server.Shared.Credits)
		do
			local MaterialIcons = require(server.Shared.MatIcons)
			server.MatIcons = setmetatable({}, {
				__index = function(self, ind)
					local materialIcon = MaterialIcons[ind]
					if materialIcon then
						self[ind] = `rbxassetid://{materialIcon}`
						return self[ind]
					end
					return ""
				end,
				__metatable = "Adonis_MatIcons"
			})
		end


		--// Load services
		for ind, serv in ipairs(SERVICES_WE_USE) do
			local temp = service[serv]
		end

		--// Load core modules
		for _, load in ipairs(CORE_LOADING_ORDER) do
			local CoreModule = Folder.Core:FindFirstChild(load)
			if CoreModule then
				LoadModule(CoreModule, true, nil, nil, true) --noenv, CoreModule
			end
		end

		--// Server Specific Service Functions
		ServiceSpecific.GetPlayers = server.Functions.GetPlayers
		--// Experimental, may have issues with Adonis tables that are protected metatables
		--ServiceSpecific.CloneTable = CloneTable

		--// Initialize Cores
		local runLast = {}
		local runAfterInit = {}
		local runAfterPlugins = {}

		for _, name in ipairs(CORE_LOADING_ORDER) do
			local core = server[name]

			if core then
				if type(core) == "table" or (type(core) == "userdata" and getmetatable(core) == "ReadOnly_Table") then
					if core.RunLast then
						table.insert(runLast, core.RunLast)
						core.RunLast = nil
					end

					if core.RunAfterInit then
						table.insert(runAfterInit, core.RunAfterInit)
						core.RunAfterInit = nil
					end

					if core.RunAfterPlugins then
						table.insert(runAfterPlugins, core.RunAfterPlugins)
						core.RunAfterPlugins = nil
					end

					if core.Init then
						core.Init(data)
						core.Init = nil
					end
				end
			end
		end

		--// Variables that rely on core modules being initialized
		server.Logs.Errors = ErrorLogs

		--// Load any afterinit functions from modules (init steps that require other modules to have finished loading)
		for _, f in pairs(runAfterInit) do
			f(data)
		end

		--// Load Plugins; enforced NoEnv policy, make sure your plugins has the 2nd argument defined!
		for _, module in ipairs(server.PluginsFolder:GetChildren()) do
			LoadModule(module, false, {script = module}, true, true) --noenv
		end

		for _, module in pairs(data.ServerPlugins or {}) do
			LoadModule(module, false, {script = module})
		end

		--// We need to do some stuff *after* plugins are loaded (in case we need to be able to account for stuff they may have changed before doing something, such as determining the max length of remote commands)
		for _, f in pairs(runAfterPlugins) do
			f(data)
		end

		--// Below can be used to determine when all modules and plugins have finished loading; service.Events.AllModulesLoaded:Connect(function() doSomething end)
		server.AllModulesLoaded = true
		service.Events.AllModulesLoaded:Fire(os.time())

		--// Queue handler
		--service.StartLoop("QueueHandler","Heartbeat",service.ProcessQueue)

		--// Stuff to run after absolutely everything else has had a chance to run and initialize and all that
		for _, f in pairs(runLast) do
			f(data)
		end

		if data.Loader then
			warn(`Loading Complete; Required by {data.Loader:GetFullName()}`)
		else
			warn("Loading Complete; No loader location provided")
		end

		if server.Logs then
			server.Logs.AddLog(server.Logs.Script, {
				Text = "Finished Loading";
				Desc = "Adonis has finished loading";
			})
		else
			warn("SERVER.LOGS TABLE IS MISSING. THIS SHOULDN'T HAPPEN! SOMETHING WENT WRONG WHILE LOADING CORE MODULES(?)");
		end
		service.Events.ServerInitialized:Fire();

		return "SUCCESS"
	end;
	__tostring = function()
		return "Adonis"
	end;
	__metatable = "Adonis";
})