-- ROBLOX FIXME LUAU: Casting to any to prevent unwanted type narrowing
local CustomConsole = setmetatable({}, { __index = Console }) :: any
CustomConsole.__index = CustomConsole

function CustomConsole.new(stdout: unknown, stderr: unknown, formatBuffer_: Formatter?): CustomConsole
	local self = setmetatable((Console.new(stdout, stderr) :: any) :: CustomConsolePrivate, CustomConsole)

	local formatBuffer: Formatter = if formatBuffer_ ~= nil
		then formatBuffer_
		else function(_: LogType, message: LogMessage)
			return message
		end

	self._counters = {}
	self._timers = {}
	self._groupDepth = 0
	self.Console = Console

	self._stdout = stdout
	self._stderr = stderr

	self._formatBuffer = formatBuffer

	return (self :: any) :: CustomConsole
end

function CustomConsole._log(self: CustomConsolePrivate, type: LogType, message: string)
	clearLine(self._stdout);
	-- ROBLOX FIXME: Find a better way to handle super calls
	(Console.log :: any)(self, self._formatBuffer(type, ("  "):rep(self._groupDepth) .. message))
end

function CustomConsole._logError(self: CustomConsolePrivate, type: LogType, message: string)
	clearLine(self._stderr);
	-- ROBLOX FIXME: Find a better way to handle super calls
	(Console.error :: any)(self, self._formatBuffer(type, ("  "):rep(self._groupDepth) .. message))
end

function CustomConsole.assert(self: CustomConsolePrivate, value: unknown, message: (string | Error)?)
	xpcall(function()
		assert(value)
	end, function(error_)
		local msg = ""
		if message ~= nil then
			msg = " " .. tostring(message)
		end

		self:_logError("assert", tostring(error_) .. msg)
	end)
end

function CustomConsole.count(self: CustomConsolePrivate, label_: string?)
	local label: string = if label_ ~= nil then label_ else "default"

	if self._counters[label] == nil then
		self._counters[label] = 0
	end

	self._counters[label] += 1
	self:_log("count", format("%s: %s", label, self._counters[label]))
end

function CustomConsole.countReset(self: CustomConsolePrivate, label_: string?)
	local label: string = if label_ ~= nil then label_ else "default"
	self._counters[label] = 0
end

function CustomConsole.debug(self: CustomConsolePrivate, firstArg: unknown, ...: any)
	self:_log("debug", format(firstArg, ...))
end

function CustomConsole.dir(self: CustomConsolePrivate, firstArg: unknown, options_: InspectOptions?)
	local options: InspectOptions = options_ or {}
	local representation = inspect(firstArg, options)
	self:_log("dir", formatWithOptions(options, representation))
end

function CustomConsole.dirxml(self: CustomConsolePrivate, firstArg: unknown, ...: any)
	self:_log("dirxml", format(firstArg, ...))
end

function CustomConsole.error(self: CustomConsolePrivate, firstArg: unknown, ...: any)
	self:_logError("error", format(firstArg, ...))
end

function CustomConsole.group(self: CustomConsolePrivate, title: string?, ...: any)
	local args = { ... }
	self._groupDepth += 1
	if Boolean.toJSBoolean(title) or #args > 0 then
		self:_log("group", chalk.bold(format(title, ...)))
	end
end

function CustomConsole.groupCollapsed(self: CustomConsolePrivate, title: string?, ...: any)
	local args = { ... }
	self._groupDepth += 1
	if Boolean.toJSBoolean(title) or #args > 0 then
		self:_log("groupCollapsed", chalk.bold(format(title, ...)))
	end
end

function CustomConsole.groupEnd(self: CustomConsolePrivate)
	if self._groupDepth > 0 then
		self._groupDepth -= 1
	end
end

function CustomConsole.info(self: CustomConsolePrivate, firstArg: unknown, ...: any)
	self:_log("info", format(firstArg, ...))
end

function CustomConsole.log(self: CustomConsolePrivate, firstArg: unknown, ...: any)
	self:_log("log", format(firstArg, ...))
end

function CustomConsole.time(self: CustomConsolePrivate, label_: string?)
	local label: string = if label_ ~= nil then label_ else "default"
	if self._timers[label] ~= nil then
		return
	end
	self._timers[label] = DateTime.now()
end

function CustomConsole.timeEnd(self: CustomConsolePrivate, label_: string?)
	local label: string = if label_ ~= nil then label_ else "default"
	local startTime = self._timers[label]
	if Boolean.toJSBoolean(startTime) then
		local endTime = DateTime.now()
		local time = endTime.UnixTimestampMillis - startTime.UnixTimestampMillis
		self:_log("time", format("%s: %s", label, formatTime(time)))
		self._timers[label] = nil
	end
end

function CustomConsole.timeLog(self: CustomConsolePrivate, label_: string?, ...: any)
	local label: string = if label_ ~= nil then label_ else "default"
	local startTime = self._timers[label]
	if Boolean.toJSBoolean(startTime) then
		local endTime = DateTime.now()
		local time = endTime.UnixTimestampMillis - startTime.UnixTimestampMillis
		self:_log("time", format("%s: %s", label, formatTime(time), ...))
	end
end

function CustomConsole.warn(self: CustomConsolePrivate, firstArg: unknown, ...: any)
	self:_logError("warn", format(firstArg, ...))
end

function CustomConsole:getBuffer()
	return nil
end

exports.default = CustomConsole
return exports