-- local function arrayBufferEquality(
-- 	a: unknown,
-- 	b: unknown
-- ): boolean | nil
-- 	if
-- 		not Boolean.toJSBoolean(
-- 			error('not implemented') --[[ ROBLOX TODO: Unhandled node for type: BinaryExpression ]] --[[ a instanceof ArrayBuffer ]]
-- 		)
-- 		or not Boolean.toJSBoolean(
-- 			error('not implemented') --[[ ROBLOX TODO: Unhandled node for type: BinaryExpression ]] --[[ b instanceof ArrayBuffer ]]
-- 		)
-- 	then
-- 		return nil
-- 	end
-- 	local dataViewA = DataView.new(a)
-- 	local dataViewB = DataView.new(b)
-- 	-- Buffers are not equal when they do not have the same byte length
-- 	if dataViewA.byteLength ~= dataViewB.byteLength then
-- 		return false
-- 	end
-- 	-- Check if every byte value is equal to each other
-- 	local i = 0
-- 	while i < dataViewA.byteLength do
-- 		if dataViewA:getUint8(i) ~= dataViewB:getUint8(i) then
-- 			return false
-- 		end
-- 		i += 1
-- 	end
-- 	return true
-- end
-- ROBLOX deviation END

local function sparseArrayEquality(a: any, b: any): boolean | nil
	if not Array.isArray(a) or not Array.isArray(b) then
		return nil
	end

	local aKeys = Object.keys(a)
	local bKeys = Object.keys(b)

	return  --[[
			ROBLOX TODO: (ADO-1217) replace the line below
			once Map/Set functionality is implemented

			--equals(a, b, {iterableEquality, typeEquality}, true) and
		]]equals(a, b, { typeEquality }, true) and equals(aKeys, bKeys)
end

local function partition<T>(items: Array<T>, predicate: (T) -> boolean): { [number]: Array<T> }
	local result = { {}, {} }

	for _, item in ipairs(items) do
		table.insert(result[predicate(item) and 1 or 2], item)
	end

	return result
end

function pathAsArray(propertyPath: string): Array<any>
	-- will match everything that's not a dot or a bracket, and "" for consecutive dots.
	local pattern = RegExp("[^.[\\]]+|(?=(?:\\.)(?:\\.|$))") -- ROBLOX TODO: add 'g' flag when supported
	local properties: Array<string | number> = {}

	-- Because the regex won't match a dot in the beginning of the path, if present.
	if propertyPath:sub(1, 1) == "." then
		table.insert(properties, "")
	end

	--[[
		ROBLOX deviation: implement the following logic without using String.protorype.replace functionality
		original code:
		propertyPath.replace(pattern, match => {
		  properties.push(match);
		  return match;
		});
	]]
	local totalLength = 0
	local propertyPath_ = propertyPath
	local match = pattern:exec(propertyPath_)
	while match ~= nil and totalLength < #propertyPath do
		local wholeMatch = match[1]
		local wholeMatchEnd = match.index + #wholeMatch
		local matchIndex = totalLength + match.index
		local prevChar = propertyPath:sub(matchIndex - 1, matchIndex - 1)

		-- ROBLOX NOTE: for `[digit]` syntax we need to parse to number so that array indexing works correctly in Lua
		if prevChar == "[" then
			local matchNumber = tonumber(wholeMatch, 10)
			if matchNumber then
				table.insert(properties, matchNumber)
			else
				table.insert(properties, wholeMatch)
			end
		else
			table.insert(properties, wholeMatch)
		end
		totalLength += wholeMatchEnd

		propertyPath_ = propertyPath_:sub(wholeMatchEnd + 1)
		match = pattern:exec(propertyPath_)
	end
	-- ROBLOX deviation END

	return properties
end

local function isError(value: any): any
	-- Although strings and tables are not errors, they are the types that
	-- can be used to trigger an error in Lua so we treat them as the "error"
	-- types
	if typeof(value) == "string" or typeof(value) == "table" then
		return true
	end

	return false
end

local function emptyObject(obj: any): boolean
	return typeof(obj) == "table" and #Object.keys(obj) == 0 or false
end

local MULTILINE_REGEXP = "[\r\n]"

local function isOneline(expected: any, received: any): boolean
	return typeof(expected) == "string"
		and typeof(received) == "string"
		and (not received:match(MULTILINE_REGEXP) or not expected:match(MULTILINE_REGEXP))
end