-- ROBLOX deviation: expected does not have RegExp type annotation

function toThrowExpectedRegExp(
	matcherName: string,
	options: JestMatcherUtils.MatcherHintOptions,
	thrown: Thrown | nil,
	expected: RegExp
): SyncExpectationResult
	local pass = thrown ~= nil and expected:test(thrown.message)

	local message
	if pass then
		message = function()
			local retval = matcherHint(matcherName, nil, nil, options)
				.. "\n\n"
				.. formatExpected("Expected pattern: never ", expected)

			if thrown ~= nil and thrown.hasMessage and not thrown.value["$$robloxInternalJestError"] then
				retval = retval
					.. formatReceived("Received message:       ", thrown, "message", expected)
					.. formatStack(thrown)
			else
				retval = retval .. formatReceived("Received value:         ", thrown, "message") .. formatStack(thrown)
			end

			return retval
		end
	else
		message = function()
			local retval = matcherHint(matcherName, nil, nil, options)
				.. "\n\n"
				.. formatExpected("Expected pattern: ", expected)

			if thrown == nil then
				retval = retval .. "\n" .. DID_NOT_THROW
			else
				if thrown.hasMessage and not thrown.value["$$robloxInternalJestError"] then
					retval = retval .. formatReceived("Received message: ", thrown, "message") .. formatStack(thrown)
				else
					retval = retval .. formatReceived("Received value:   ", thrown, "message") .. formatStack(thrown)
				end
			end

			return retval
		end
	end

	return { message = message, pass = pass }
end

type AsymmetricMatcher = {
	asymmetricMatch: (any, any) -> boolean,
}

function toThrowExpectedAsymmetric(
	matcherName: string,
	options: JestMatcherUtils.MatcherHintOptions,
	thrown: Thrown | nil,
	expected: AsymmetricMatcher
): SyncExpectationResult
	local pass = thrown ~= nil and expected:asymmetricMatch(thrown.value)

	local message
	if pass then
		message = function()
			local retval = matcherHint(matcherName, nil, nil, options)
				.. "\n\n"
				.. formatExpected("Expected asymmetric matcher: never ", expected)
				.. "\n"

			if thrown ~= nil and thrown.hasMessage and not thrown.value["$$robloxInternalJestError"] then
				retval = retval
					.. formatReceived("Received name:    ", thrown, "name")
					.. formatReceived("Received message: ", thrown, "message")
					.. formatStack(thrown)
			else
				retval = retval .. formatReceived("Thrown value: ", thrown, "message") .. formatStack(thrown)
			end

			return retval
		end
	else
		message = function()
			local retval = matcherHint(matcherName, nil, nil, options)
				.. "\n\n"
				.. formatExpected("Expected asymmetric matcher: ", expected)
				.. "\n"

			if thrown == nil then
				retval = retval .. DID_NOT_THROW
			else
				if thrown.hasMessage and not thrown.value["$$robloxInternalJestError"] then
					retval = retval
						.. formatReceived("Received name:    ", thrown, "name")
						.. formatReceived("Received message: ", thrown, "message")
						.. formatStack(thrown)
				else
					retval = retval .. formatReceived("Thrown value: ", thrown, "message") .. formatStack(thrown)
				end
			end

			return retval
		end
	end

	return { message = message, pass = pass }
end
