--!strict

local LuauPolyfill = script.Parent
local arrayForEach = require(LuauPolyfill.Array.forEach)
local arrayMap = require(LuauPolyfill.Array.map)
local types = require(LuauPolyfill.types)
type Object = types.Object
type Array<T> = types.Array<T>
type Table<T, V> = types.Table<T, V>
type Tuple<T, V> = types.Tuple<T, V>
type mapCallbackFn<K, V> = types.mapCallbackFn<K, V>
type mapCallbackFnWithThisArg<K, V> = types.mapCallbackFnWithThisArg<K, V>
type Map<K, V> = types.Map<K, V>

type Map_Statics = {
	new: <K, V>(iterable: Array<Array<any>>?) -> Map<K, V>,
}

local Map: Map<any, any> & Map_Statics = ({} :: any) :: Map<any, any> & Map_Statics

function Map.new<K, V>(iterable: Array<Array<any>>?): Map<K, V>
	local array
	local map = {}
	if iterable ~= nil then
		if _G.__DEV__ then
			local iterableType = typeof(iterable)
			if iterableType == "table" then
				if #iterable > 0 and typeof(iterable[1]) ~= "table" then
					error("cannot create Map from {K, V} form, it must be { {K, V}... }")
				end
			else
				error(("cannot create array from value of type `%s`"):format(iterableType))
			end
		end

		local arrayFromIterable = table.clone(iterable) :: Array<Array<any>>
		array = table.create(#arrayFromIterable)
		for _, entry in arrayFromIterable do
			local key = entry[1]
			if _G.__DEV__ then
				if key == nil then
					error("cannot create Map from a table that isn't an array.")
				end
			end
			local val = entry[2]
			-- only add to array if new
			if map[key] == nil then
				table.insert(array, key)
			end
			-- always assign
			map[key] = val
		end
	else
		array = {}
	end

	return (setmetatable({
		size = #array,
		_map = map,
		_array = array,
	}, Map) :: any) :: Map<K, V>
end
