-- ROBLOX deviation START: defining picomatch as callable table

local picomatch_
local picomatch = setmetatable({}, {
	__call = function(_self: any, glob: any, options: Object?, returnState: boolean?)
		return picomatch_(glob, options, returnState)
	end,
})

function picomatch_(
	glob: any,
	options: Object?,
	returnState_: boolean?
): typeof(setmetatable({ state = {} :: any }, {
	__call = function(_self: any, str: string, returnObject: boolean?): any
		return nil :: any
	end,
})) -- ROBLOX deviation END
	local returnState = returnState_ or false

	if Array.isArray(glob) then
		local fns = Array.map(glob, function(input)
			return picomatch(input, options, returnState)
		end)
		-- ROBLOX deviation START: defining arrayMatcher as callable table
		local arrayMatcher_
		local arrayMatcher = setmetatable({}, {
			__call = function(_self: any, str: string)
				return arrayMatcher_(str)
			end,
		})

		function arrayMatcher_(str: string) -- ROBLOX deviation: END
			for _, isMatch in ipairs(fns) do
				local state = isMatch(str)
				if state then
					return state
				end
			end
			return false
		end
		return arrayMatcher
	end

	local isState = isObject(glob) and Boolean.toJSBoolean(glob.tokens) and Boolean.toJSBoolean(glob.input)

	if glob == "" or typeof(glob) ~= "string" and not isState then
		error(Error.new("TypeError: Expected pattern to be a non-empty string"))
	end

	local opts = options or {} :: Object
	local posix = utils.isWindows(options)
	-- ROBLOX Luau FIXME: needs normalization to avoid Type 'RegExp' could not be converted into 'RegExp & {| state: any? |}'
	local regex: RegExp & { state: any? } = if isState
		then picomatch.compileRe(glob, options) :: any
		else picomatch.makeRe(glob, options, false, true)

	local state = regex.state
	regex.state = nil

	local isIgnored: (str: string) -> boolean
	function isIgnored(_)
		return false
	end
	if Boolean.toJSBoolean(opts.ignore) then
		local ignoreOpts = Object.assign(
			{},
			options,
			{ ignore = Object.None, onMatch = Object.None, onResult = Object.None }
		)
		-- ROBLOX FIXME Luau: Callable table can't be assigned to a function
		isIgnored = picomatch(opts.ignore, ignoreOpts, returnState) :: any
	end

	-- ROBLOX deviation START: defining matcher as callable table
	local matcher_
	local matcher = setmetatable({}, {
		__call = function(_self: any, input: string, returnObject: boolean?)
			return matcher_(input, returnObject)
		end,
	})

	function matcher_(input: string, returnObject_: boolean?) -- ROBLOX deviation END
		local returnObject = returnObject_ or false

		local ref = picomatch.test(input, regex, options, { glob = glob, posix = posix })
		local isMatch, match, output = ref.isMatch, ref.match, ref.output
		local result = {
			glob = glob,
			state = state,
			regex = regex,
			posix = posix,
			input = input,
			output = output,
			match = match,
			isMatch = isMatch,
		}

		if typeof(opts.onResult) == "function" then
			opts.onResult(result)
		end

		if isMatch == false then
			result.isMatch = false
			return if returnObject then result else false
		end

		if isIgnored(input) then
			if typeof(opts.onIgnore) == "function" then
				opts.onIgnore(result)
			end
			result.isMatch = false
			return if returnObject then result else false
		end

		if typeof(opts.onMatch) == "function" then
			opts.onMatch(result)
		end
		return if returnObject then result else true
	end

	if Boolean.toJSBoolean(returnState) then
		matcher.state = state
	end
	return matcher
end
