--[[
local warn = function(...)
	warn(...)
end
]]
local cPcall = function(func, ...)
	local ran, err = pcall(coroutine.resume, coroutine.create(func), ...)

	if err then
		warn(":: ADONIS_ERROR ::", err)
		logError(tostring(err))
	end

	return ran, err
end

local Pcall = function(func, ...)
	local ran, err = pcall(func, ...)

	if err then
		logError(tostring(err))
	end

	return ran, err
end

local Routine = function(func, ...)
	return coroutine.resume(coroutine.create(func), ...)
end

local Immutable = function(...)
	local mut = coroutine.wrap(function(...)
		while true do
			coroutine.yield(...)
		end
	end)
	mut(...)
	return mut
end

local Kill
local Fire, Detected = nil, nil
do
	local wrap = coroutine.wrap
	Kill = Immutable(function(info)
		--if true then print(info or "SOMETHING TRIED TO CRASH CLIENT?") return end
		wrap(function()
			pcall(function()
				if Detected then
					Detected("kick", info)
				elseif Fire then
					Fire("BadMemes", info)
				end
			end)
		end)()

		wrap(function()
			pcall(function()
				task.wait(1)
				service.Player:Kick(info)
			end)
		end)()

		wrap(function()
			pcall(function()
				task.wait(5)
				while true do
					pcall(task.spawn, function()
						task.spawn(Kill())
						-- memes
					end)
				end
			end)
		end)()
	end)
end

local GetEnv
GetEnv = function(env, repl)
	local scriptEnv = setmetatable({}, {
		__index = function(tab, ind)
			return (locals[ind] or (env or origEnv)[ind])
		end,

		__metatable = unique,
	})

	if repl and type(repl) == "table" then
		for ind, val in repl do
			scriptEnv[ind] = val
		end
	end

	return scriptEnv
end

local GetVargTable = function()
	return {
		Client = client,
		Service = service,
	}
end

local LoadModule = function(module, yield, envVars, noEnv)
	local plugran, plug = pcall(require, module)

	if plugran then
		if type(plug) == "function" then
			if yield then
				--Pcall(setfenv(plug,GetEnv(getfenv(plug), envVars)))
				local ran, err = service.TrackTask(
					`Plugin: {module}`,
					(noEnv and plug) or setfenv(plug, GetEnv(getfenv(plug), envVars)),
					GetVargTable(),
					GetEnv
				)

				if not ran then
					warn(`Module encountered an error while loading: {module}`)
					warn(tostring(err))
				end
			else
				-- service.Threads.RunTask(`PLUGIN: {module,setfenv(plug,GetEnv(getfenv(plug), envVars))}`)
				local ran, err = service.TrackTask(
					`Thread: Plugin: {module}`,
					(noEnv and plug) or setfenv(plug, GetEnv(getfenv(plug), envVars)),
					GetVargTable(),
					GetEnv
				)

				if not ran then
					warn(`Module encountered an error while loading: {module}`)
					warn(tostring(err))
				end
			end
		else
			client[module.Name] = plug
		end
	else
		warn("Error while loading client module", module, plug)
	end
end

log("Client setmetatable")

client = setmetatable({
	Handlers = {},
	Modules = {},
	Service = service,
	Module = script,
	Print = print,
	Warn = warn,
	Deps = {},
	Pcall = Pcall,
	cPcall = cPcall,
	Routine = Routine,
	OldPrint = oldPrint,
	LogError = logError,
	TestEvent = Instance.new("RemoteEvent"),

	Disconnect = function(info)
		service.Player:Kick(info or "Disconnected from server")
		--wait(30)
		--client.Kill()(info)
	end,

	--Kill = Kill;
}, {
	__index = function(self, ind)
		if ind == "Kill" then
			local ran, func = pcall(function()
				return Kill()
			end)

			if not ran or type(func) ~= "function" then
				service.Players.LocalPlayer:Kick("Adonis (PlrClientIndexKlErr)")
				while true do
				end
			end

			return func
		end
	end,
})

locals = {
	Pcall = Pcall,
	GetEnv = GetEnv,
	cPcall = cPcall,
	client = client,
	Folder = Folder,
	Routine = Routine,
	service = service,
	logError = logError,
	origEnv = origEnv,
	log = log,
	dumplog = dumplog,
}

log("Create service metatable")

service = require(Folder.Shared.Service)(function(eType, msg, desc, ...)
	--warn(eType, msg, desc, ...)
	local extra = { ... }
	if eType == "MethodError" then
		--Kill()("Shananigans denied")
		--player:Kick("Method error")
		--service.Detected("kick", "Method change detected")
		logError("Client", `Method Error Occured: {msg}`)
	elseif eType == "ServerError" then
		logError("Client", tostring(msg))
	elseif eType == "ReadError" then
		--message("===== READ ERROR:::::::")
		--message(tostring(msg))
		--message(tostring(desc))
		--message("    ")

		Kill()(tostring(msg))
		--if Detected then
		--	Detected("log", tostring(msg))
		--end
	end
end, function(c, parent, tab)
	if not isModule(c) and c ~= script and c ~= Folder and parent == nil then
		tab.UnHook()
	end
end, ServiceSpecific, GetEnv(nil, { client = client }))