local z=require(script.Parent.Parent.Shared.Util)local s={Years=31556926,Months=2629744,Weeks=604800,Days=86400,Hours=3600,Minutes=60,Seconds=1}local B={}for z,s in pairs(s)do table.insert(B,z)end local o=z.MakeFuzzyFinder(B)local function X(z)if z==nil or z==""then return nil end local B=tonumber(z)if B and B==0 then return 0,0,true end local X=z:gsub("-\063\037\100\043\037\097\043","")local G=X:match("-\063\037\100\043")if G then return nil,tonumber(G),true end local Y=nil local r,O for z in z:gmatch("-\063\037\100\043\037\097\043")do r,O=z:match("\040-\063\037\100\043\041\040\037\097\043\041")local B=o(O,false,true)if#B==0 then return nil,tonumber(r)end if Y==nil then Y=0 end Y=Y+(O:lower()=="\109"and 60 or s[B[1]])*tonumber(r)end if Y==nil then return nil else return Y,tonumber(r)end end local function G(z,s,B,o)o=o or 1 local X={}for z,G in pairs(z)do if B==1 then X[z]=s..G:sub(o,#G-1)else X[z]=s..G:sub(o)end end return X end local Y={Transform=function(z)return z,X(z)end,Validate=function(z,s)return s~=nil end;Autocomplete=function(z,s,B,X,Y)local r={}if X or Y then local s=X==true and o("",false,true)or Y if X==true then r=G(s,z,B)else local B=(z:match("\094\046\042\040\037\097\043\041\036")):len()r=G(s,z,B+1)end elseif s~=nil then local s=z:match("\094\046\042-\063\037\100\043\040\037\097\043\041\037\115\063\036")local X=o(s,false,true)r=G(X,z,B,#s+1)table.sort(r)end return r end;Parse=function(z,s)return s end}return function(s)s:RegisterType("\100\117\114\097\116\105\111\110",Y)s:RegisterType("\100\117\114\097\116\105\111\110\115",z.MakeListableType(Y))end
local Util = require(script.Parent.Parent.Shared.Util)

local unitTable = {
	Years = 31556926,
	Months = 2629744,
	Weeks = 604800,
	Days = 86400,
	Hours = 3600,
	Minutes = 60,
	Seconds = 1,
}

local searchKeyTable = {}
for key, _ in pairs(unitTable) do
	table.insert(searchKeyTable, key)
end
local unitFinder = Util.MakeFuzzyFinder(searchKeyTable)

local function stringToSecondDuration(stringDuration)
	-- The duration cannot be null or an empty string.
	if stringDuration == nil or stringDuration == "" then
		return nil
	end
	-- Allow 0 by itself (without a unit) to indicate 0 seconds
	local durationNum = tonumber(stringDuration)
	if durationNum and durationNum == 0 then
		return 0, 0, true
	end
	-- The duration must end with a unit,
	-- if it doesn't then return true as the fourth value to indicate the need to offer autocomplete for units.
	local endOnlyString = stringDuration:gsub("-?%d+%a+", "")
	local endNumber = endOnlyString:match("-?%d+")
	if endNumber then
		return nil, tonumber(endNumber), true
	end
	local seconds = nil
	local rawNum, rawUnit
	for rawComponent in stringDuration:gmatch("-?%d+%a+") do
		rawNum, rawUnit = rawComponent:match("(-?%d+)(%a+)")
		local unitNames = unitFinder(rawUnit, false, true)
		-- There were no matching units, it's invalid. Return the parsed number to be used for autocomplete
		if #unitNames == 0 then
			return nil, tonumber(rawNum)
		end
		if seconds == nil then
			seconds = 0
		end
		-- While it was already defaulting to use minutes when using just "m", this does it without worrying
		-- about any consistency between list ordering.
		seconds = seconds + (rawUnit:lower() == "m" and 60 or unitTable[unitNames[1]]) * tonumber(rawNum)
	end
	-- If no durations were provided, return nil.
	if seconds == nil then
		return nil
	else
		return seconds, tonumber(rawNum)
	end
end

local function mapUnits(units, rawText, lastNumber, subStart)
	subStart = subStart or 1
	local returnTable = {}
	for i, unit in pairs(units) do
		if lastNumber == 1 then
			returnTable[i] = rawText .. unit:sub(subStart, #unit - 1)
		else
			returnTable[i] = rawText .. unit:sub(subStart)
		end
	end
	return returnTable
end

local durationType = {
	Transform = function(text)
		return text, stringToSecondDuration(text)
	end,

	Validate = function(_, duration)
		return duration ~= nil
	end,

	Autocomplete = function(rawText, duration, lastNumber, isUnitMissing, matchedUnits)
		local returnTable = {}
		if isUnitMissing or matchedUnits then
			local unitsTable = isUnitMissing == true and unitFinder("", false, true) or matchedUnits
			if isUnitMissing == true then
				-- Concat the entire unit name to existing text.
				returnTable = mapUnits(unitsTable, rawText, lastNumber)
			else
				-- Concat the rest of the unit based on what already exists of the unit name.
				local existingUnitLength = rawText:match("^.*(%a+)$"):len()
				returnTable = mapUnits(unitsTable, rawText, existingUnitLength + 1)
			end
		elseif duration ~= nil then
			local endingUnit = rawText:match("^.*-?%d+(%a+)%s?$")
			-- Assume there is a singular match at this point
			local fuzzyUnits = unitFinder(endingUnit, false, true)
			-- List all possible fuzzy matches. This is for the Minutes/Months ambiguity case.
			returnTable = mapUnits(fuzzyUnits, rawText, lastNumber, #endingUnit + 1)
			-- Sort alphabetically in the Minutes/Months case, so Minutes are displayed on top.
			table.sort(returnTable)
		end
		return returnTable
	end,

	Parse = function(_, duration)
		return duration
	end,
}

return function(registry)
	registry:RegisterType("duration", durationType)
	registry:RegisterType("durations", Util.MakeListableType(durationType))
end
