local getArgs = require('Module:Arguments').getArgs

local p = {}

local months = {'1월', '2월', '3월', '4월', '5월', '6월',
	'7월', '8월', '9월', '10월', '11월', '12월'}

local aliasesQ = {
 RottenTomatoes = "Q105584",
 RottenTomatoesScore = "Q108403393",
 RottenTomatoesAverage = "Q108403540",
 Fandango = "Q5433722",

local aliasesP = {
	RottenTomatoesId = "P1258",
	reviewScore = "P444",
	reviewScoreBy = "P447",
	numberOfReviews = "P7887",
	pointInTime = "P585",
	determinationMethod = "P459",
 author = "P50",
 publisher = "P123",
 statedIn = "P248",
 language = "P407",
 retrieved = "P813",
 referenceURL = "P854",
 archiveURL = "P1065",
 title = "P1476",
 formatterURL = "P1630",
 archiveDate = "P2960",

-- Helper functions ------------------------------------------------------------
local function falsy(x)
	return x == false or x == nil or x == '' or x == 0 or type(x) == 'table' and next(x) == nil
end

-- copied from Module:wd
local function parseDate
 precision = precision or "d"

 local i, j, index, ptr
 local parts = {nil, nil, nil}

 if dateStr == nil then
 return parts[1], parts[2], parts[3] -- year, month, day
 end

 -- 'T' for snak values, '/' for outputs with '/Julian' attached
 i, j = dateStr:find("[T/]")

 if i then
 dateStr = dateStr:sub
 end

 local from = 1

 if dateStr:sub == "-" then
 -- this is a negative number, look further ahead
 from = 2
 end

 index = 1
 ptr = 1

 i, j = dateStr:find

 if i then
 -- year
 parts[index] = tonumber(mw.ustring.gsub(dateStr:sub, "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)

 if parts[index] == -0 then
 parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
 end

 if precision == "y" then
 -- we're done
 return parts[1], parts[2], parts[3] -- year, month, day
 end

 index = index + 1
 ptr = i + 1

 i, j = dateStr:find

 if i then
 -- month
 parts[index] = tonumber(dateStr:sub, 10)

 if precision == "m" then
 -- we're done
 return parts[1], parts[2], parts[3] -- year, month, day
 end

 index = index + 1
 ptr = i + 1
 end
 end

 if dateStr:sub(ptr) ~= "" then
 -- day if we have month, month if we have year, or year
 parts[index] = tonumber(dateStr:sub(ptr), 10)
 end

 return parts[1], parts[2], parts[3] -- year, month, day
end

-- nil dates precede all reasonable dates since year becomes 1
local function datePrecedesDate
 aY, aM, aD = aY or 1, aM or 1, aD or 1
 bY, bM, bD = bY or 1, bM or 1, bD or 1
 if aY < bY then return true end
 if aY > bY then return false end
 if aM < bM then return true end
 if aM > bM then return false end
 if aD < bD then return true end
 return false
end

-- format options: 'dmy', 'mdy', 'ymd', 'iso'
local function format_date
	format = format or 'MDY'
	local s = (D or '') .. (months[M] or '') .. (Y or '')
	return mw.getCurrentFrame:expandTemplate{title='Date', args={s, format}}
end

-- Returns either QID, true, or ErrorString, false
local function getentityID(args)
	local entityID = args.qid
	if falsy(entityID) then
		local title = args.title
		if falsy(title) then
			local currentID = mw.wikibase.getEntityIdForCurrentPage
			if currentID then
				return currentID, true
			end
			return Error.error({'No Wikidata item connected to current page. Need qid or title argument.'}), false
		else
			-- if not mw.title.makeTitle.exists then
			-- 	return Error.error({'Article ' .. title .. ' does not exist.'}), false
			-- end
			entityID = mw.wikibase.getEntityIdForTitle(title)
			if not entityID then
				return Error.error({'Article "' .. title .. '" does not exist or has no Wikidata item.'}), false
			end
			return entityID, true
		end
	end
	--At this point we should have an entityID. Check if valid.
	if not mw.wikibase.isValidEntityId(entityID) then
		return Error.error({'Invalid Q-identifier.'}), false
	end
	if not mw.wikibase.entityExists(entityID) then
		return Error.error({'Wikidata item ' .. entityID .. ' does not exist.'}), false
	end
	return entityID, true
end

local function point_in_time(statement)
	if not statement.qualifiers then
		return nil, nil, nil
	end
	local pointintime = statement.qualifiers[aliasesP.pointInTime]
	if pointintime then
		return parseDate(pointintime[1].datavalue.value.time)
	end
	return nil, nil, nil
end

local function access_date(statement)
	if statement.references then
		local accessdate = statement.references[1].snaks[aliasesP.retrieved]
		if accessdate then
			return parseDate(accessdate[1].datavalue.value.time)
		end
	end
	return nil, nil, nil
end

local function date_from_statement(statement)
	local Y, M, D = point_in_time(statement)
	if Y then
		return Y, M, D
	end
	Y, M, D = access_date(statement)
	if Y then
		return Y, M, D
	end
	if statement.rank == 'preferred' then
		return 1, 1, 3
	elseif statement.rank == 'normal' then
		return 1, 1, 2
	end
	return 1, 1, 1
end

local function reviewedby_RT(statement)
	if not statement.qualifiers then return false end
	local x = statement.qualifiers[aliasesP.reviewScoreBy]
	return x and x[1].datavalue.value.id == aliasesQ.RottenTomatoes
end

local function score_type(statement)
	local x = nil
	if statement.qualifiers then
		x = statement.qualifiers[aliasesP.determinationMethod]
	end
	if x then
		x = x[1].datavalue.value.id
	end
	local y = ''
	if statement.mainsnak.snaktype == 'value' then
		y = statement.mainsnak.datavalue.value
	end
	if x == aliasesQ.RottenTomatoesScore then
		return 'percent'
	elseif x == aliasesQ.RottenTomatoesAverage then
		return 'average'
	elseif string.match or string.match or string.match then
		return 'percent'
	elseif string.match or string.match or string.match then
		return 'percent'
	elseif string.match or string.match then
		return 'average'
	elseif string.match or string.match then
		return 'average'
	end
	return nil
end

local function most_recent_score_statement
	scoretype = scoretype or 'percent'
	local score_statements = mw.wikibase.getAllStatements
	local newest, nY, nM, nD
	for i, v in ipairs(score_statements) do
		local Y, M, D = date_from_statement(v)
		if v.rank ~= 'deprecated' and v.mainsnak.snaktype == 'value'
				and reviewedby_RT(v) and score_type(v)==scoretype
				and not datePrecedesDate then
			nY, nM, nD = Y, M, D
			newest = v
		end
	end
	return newest
end

local function get_score
	scoretype = scoretype or 'percent'
	local x = most_recent_score_statement
	if x == nil then
		return nil
	end
	return x.mainsnak.datavalue.value
end

local function get_count
	local x = most_recent_score_statement(entityID)
	if x == nil then
		return nil
	end
	local y = x.qualifiers[aliasesP.numberOfReviews]
	if y == nil then
		return nil
	end
	local retval = string.match -- dont get sign
	if args ~= nil and args.spell then
		local s = {[1]=retval}
		for key, val in pairs(args) do
			if key == 1 or key == 'qid' or key == 'title' then

			elseif type(key) == 'number' then
				
			else
				s[key] = val
			end
		end
		return mw.getCurrentFrame:expandTemplate{title='Spellnum per MOS', args=s}
	end
	return retval
end

local function get_rtid
	local rtid_statements = mw.wikibase.getBestStatements
	local newest, nY, nM, nD
	for i, v in ipairs(rtid_statements) do
		local Y, M, D = date_from_statement(v)
		if not datePrecedesDate then
			nY, nM, nD = Y, M, D
			newest = v
		end
	end
	if newest == nil then
		return nil
	end
	newest = newest.mainsnak.datavalue.value
	if noprefix then
		newest = string.sub(newest, string.find + 1)
	end
	return newest
end

local function get_url(entityID)
	local rtid = get_rtid(entityID)
	if rtid == nil then
		return nil
	end
	local x = mw.wikibase.getBestStatements
	return (string.gsub)
end

local function get_date
	local z = most_recent_score_statement(entityID)
	if z == nil then
		return nil
	end
	local Y, M, D = date_from_statement(z)
	if part == 'year' then
		return Y or ''
	elseif part == 'month' then
		return months[M] or ''
	elseif part == 'day' then
		return D or ''
	end
	return format_date
end

local function get_access_date
	local z = most_recent_score_statement(entityID)
	if z == nil then
		return nil
	end
	local Y, M, D = access_date(z)
	if not Y then
		Y, M, D = point_in_time(z)
	end
	return format_date
end

local function get_asof
	local s = {}
	for key, val in pairs(args) do
		if key == 1 or key == 'qid' or key == 'title' then
			
		elseif key == 2 then
			s[1] = get_date
		elseif key == 3 then
			s[2] = get_date
		elseif key == 4 then
			s[3] = get_date
		elseif type(key) == 'number' then
			s[key-1] = val
		else
			s[key] = val
		end
	end
	return mw.getCurrentFrame:expandTemplate{title='As of', args=s}
end

local function get_rtprose
	local s = {get_score(entityID), get_score, get_count(entityID)}
	s[1] = string.match
	s[2] = string.match or string.match
	return mw.getCurrentFrame:expandTemplate{title='Rotten Tomatoes prose', args=s}
end

local function get_edit_icon(entityID)
	return mw.getCurrentFrame:expandTemplate{title='EditAtWikidata', args={qid=entityID, pid='P444'}}
end

local function get_table(entityID)
	return get_score(entityID) .. ' (' .. get_count(entityID) .. ' reviews)'
end

function p.main(frame)
	local args = getArgs(frame, {
		wrappers = 'Template:Rotten Tomatoes data',
		removeBlanks = false,
	return p._main(args)
end

function p._main(args)
	local entityID, is_good = getentityID(args)
	if not is_good then
		return entityID -- which is the error message in this case
	end
	local command = args[1]
	if falsy(command) then
		return Error.error({'Missing command.'})
	end
	command = string.lower(command)
	local retval
	if command == 'score' then
		retval = get_score
	elseif command == 'average' then
		retval = get_score
	elseif command == 'count' then
		retval = get_count
	elseif command == 'rtid' then
		retval = get_rtid
	elseif command == 'url' then
		retval = get_url(entityID)
	elseif command == 'date' then
		retval = get_date
	elseif command == 'year' then
		retval = get_date
	elseif command == 'month' then
		retval = get_date
	elseif command == 'day' then
		retval = get_date
	elseif command == 'access date' or command == 'accessdate' or command == 'access-date' then
		retval = get_access_date
	elseif command == 'as of' or command == 'asof' then
		retval = get_asof
	elseif command == 'prose' then
		retval = get_rtprose
	elseif command == 'edit' then
		retval = get_edit_icon(entityID)
	elseif command == 'table' then
		retval = get_table(entityID)
	else
		return Error.error({'Invalid command.'})
	end
	if falsy(retval) then
		return Error.error({'RT data for "' .. command .. '" unavailable.'})
	end
	return retval
end