local mDts = require('Modulo:Dts/pagipadasan') -- the module to be tested
local Dts = mDts._exportClasses().Dts -- the Dts class
local ScribuntoUnit = require('Modulo:ScribuntoUnit')
local suite = ScribuntoUnit:new()

-------------------------------------------------------------------------------
-- Helper functions and values
-------------------------------------------------------------------------------

suite.currentYear = os.date('*t').year
suite.nowrapPattern = '^<span data%-sort%-value="[^"<>]-" style="white%-space:nowrap">[^<>]-</span>$'

suite.offValues = {'off', 'no', 'NO', 'n', 'false', false}
suite.onValues = {'on', 'yes', 'YES', 'y', 'true', true}
local function makeOnOffAsserts(fragment)
	suite['assert' .. fragment .. 'WhenOn'] = function (self, first, func, ...)
		for i, on in ipairs(suite.onValues) do
			self['assert' .. fragment](self, first, func(on), ...)
		end
	end
	suite['assert' .. fragment .. 'WhenOff'] = function (self, first, func, ...)
		for i, off in ipairs(suite.offValues) do
			self['assert' .. fragment](self, first, func(off), ...)
		end
	end
end
makeOnOffAsserts('Equals')
makeOnOffAsserts('StringContains')
makeOnOffAsserts('NotStringContains')

function suite:assertErrorContains(pattern, func, plain)
	local success, msg = pcall(func)
	self:assertFalse(success)
	self:assertStringContains(pattern, msg, plain)
end

function suite:assertDateEquals(year, month, day, args)
	local dtsObj = Dts.new(args)
	self:assertEquals(year, dtsObj.year)
	self:assertEquals(month, dtsObj.month)
	self:assertEquals(day, dtsObj.day)
end

function suite:assertSortKeyEquals(expected, args)
	local dtsObj = Dts.new(args)
	self:assertEquals(expected, dtsObj:makeSortKey())
end

function suite:assertDisplayEquals(expected, args)
	local dtsObj = Dts.new(args)
	self:assertEquals(expected, dtsObj:makeDisplay())
end

-------------------------------------------------------------------------------
-- Date parameter tests
-------------------------------------------------------------------------------

function suite:testDateYMD()
	self:assertDateEquals(2000, 1, 27, {2000, 1, 27})
end

function suite:testDateYMonthD()
	self:assertDateEquals(2000, 1, 27, {2000, 'Enero', 27})
	self:assertDateEquals(2000, 1, 27, {2000, 'enero', 27})
end

function suite:testDateYMonD()
	self:assertDateEquals(2000, 1, 27, {2000, 'Ene', 27})
	self:assertDateEquals(2000, 1, 27, {2000, 'ene', 27})
end

function suite:testDateYM()
	self:assertDateEquals(2000, 1, nil, {2000, 1})
end

function suite:testDateYD()
	self:assertDateEquals(2000, nil, 27, {2000, nil, 27})
end

function suite:testDateMD()
	self:assertDateEquals(nil, 1, 27, {nil, 1, 27})
end

function suite:testDateY()
	self:assertDateEquals(2000, nil, nil, {2000})
end

function suite:testDateM()
	self:assertDateEquals(nil, 1, nil, {nil, 1})
end

function suite:testDateD()
	self:assertDateEquals(nil, nil, 27, {nil, nil, 27})
end

function suite:testDateNegativeYear()
	self:assertDateEquals(-2000, 1, 27, {-2000, 1, 27})
end

function suite:testDateString()
	self:assertDateEquals(2000, 1, 27, {'2000', '1', '27'})
end

function suite:testDateAllMonths()
	self:assertDateEquals(nil, 1, nil, {nil, 'Enero', nil})
	self:assertDateEquals(nil, 2, nil, {nil, 'Pebrero', nil})
	self:assertDateEquals(nil, 3, nil, {nil, 'Marso', nil})
	self:assertDateEquals(nil, 4, nil, {nil, 'Abril', nil})
	self:assertDateEquals(nil, 5, nil, {nil, 'Mayo', nil})
	self:assertDateEquals(nil, 6, nil, {nil, 'Hunio', nil})
	self:assertDateEquals(nil, 7, nil, {nil, 'Hulio', nil})
	self:assertDateEquals(nil, 8, nil, {nil, 'Agosto', nil})
	self:assertDateEquals(nil, 9, nil, {nil, 'Septiembre', nil})
	self:assertDateEquals(nil, 10, nil, {nil, 'Oktubre', nil})
	self:assertDateEquals(nil, 11, nil, {nil, 'Nobiembre', nil})
	self:assertDateEquals(nil, 12, nil, {nil, 'Disiembre', nil})
end

function suite:testDateAllMons()
	self:assertDateEquals(nil, 1, nil, {nil, 'Ene', nil})
	self:assertDateEquals(nil, 2, nil, {nil, 'Peb', nil})
	self:assertDateEquals(nil, 3, nil, {nil, 'Mar', nil})
	self:assertDateEquals(nil, 4, nil, {nil, 'Abr', nil})
	self:assertDateEquals(nil, 5, nil, {nil, 'May', nil})
	self:assertDateEquals(nil, 6, nil, {nil, 'Hun', nil})
	self:assertDateEquals(nil, 7, nil, {nil, 'Hul', nil})
	self:assertDateEquals(nil, 8, nil, {nil, 'Ago', nil})
	self:assertDateEquals(nil, 9, nil, {nil, 'Sep', nil})
	self:assertDateEquals(nil, 10, nil, {nil, 'Okt', nil})
	self:assertDateEquals(nil, 11, nil, {nil, 'Nob', nil})
	self:assertDateEquals(nil, 12, nil, {nil, 'Dis', nil})
end

-------------------------------------------------------------------------------
-- parseDate tests
-------------------------------------------------------------------------------

function suite:testParseYear()
	self:assertDateEquals(2000, nil, nil, {'2000'})
end

function suite:testParseNegativeYear()
	self:assertDateEquals(-2000, nil, nil, {'-2000'})
end

function suite:testParseSmallYear()
	self:assertDateEquals(12, nil, nil, {'12'})
end

function suite:testParseYYYYMMDD()
	self:assertDateEquals(2000, 1, 27, {'2000-01-27'})
	self:assertDateEquals(2000, 1, 27, {'2000-1-27'})
	self:assertDateEquals(2000, 1, 6, {'2000-01-06'})
	self:assertDateEquals(2000, 1, 6, {'2000-01-06'})
end

function suite:testParseDDMMYYYY()
	self:assertDateEquals(2000, 1, 27, {'27-01-2000'})
	self:assertDateEquals(2000, 1, 27, {'27-1-2000'})
	self:assertDateEquals(2000, 1, 6, {'06-01-2000'})
end

function suite:testParseYYYYMM()
	self:assertDateEquals(2000, 1, nil, {'2000-01'})
	self:assertDateEquals(2000, 1, nil, {'2000-1'})
end

function suite:testParseMonthYYYY()
	self:assertDateEquals(1418, 2, nil, {'Pebrero 1418'})
end

function suite:testParseMonYYYY()
	self:assertDateEquals(1418, 2, nil, {'Peb 1418'})
end

function suite:testParseDDMonthYYYY()
	self:assertDateEquals(1975, 4, 24, {'24 Abril 1975'})
end

function suite:testParseDDMonYYYY()
	self:assertDateEquals(1975, 4, 24, {'24 Abr 1975'})
end

function suite:testParseMonthDDYYYY()
	self:assertDateEquals(1975, 4, 24, {'Abril 24, 1975'})
	self:assertDateEquals(1975, 4, 24, {'Abril 24 1975'})
end

function suite:testParseMonDDYYYY()
	self:assertDateEquals(1975, 4, 24, {'Abr 24, 1975'})
	self:assertDateEquals(1975, 4, 24, {'Abr 24 1975'})
end

function suite:testParseMonth()
	self:assertDateEquals(nil, 4, nil, {'Abril'})
	self:assertDateEquals(nil, 4, nil, {'abril'})
end

function suite:testParseMon()
	self:assertDateEquals(nil, 4, nil, {'Abr'})
	self:assertDateEquals(nil, 4, nil, {'abr'})
end

function suite:testParseDDMonth()
	self:assertDateEquals(nil, 11, 12, {'12 Nobiembre'})
end

function suite:testParseDDMon()
	self:assertDateEquals(nil, 11, 12, {'12 Nob'})
end

function suite:testParseMonthDD()
	self:assertDateEquals(nil, 11, 12, {'Nobiembre 12'})
end

function suite:testParseMonDD()
	self:assertDateEquals(nil, 11, 12, {'Nob 12'})
end

function suite:testParseDDMonthYYY()
	self:assertDateEquals(100, 1, 27, {'27 Enero 100'})
end

function suite:testParseDDMonth0YYY()
	self:assertDateEquals(100, 1, 27, {'27 Enero 0100'})
end

function suite:testParseDDMonth000Y()
	self:assertDateEquals(3, 1, 27, {'27 Enero 0003'})
end

function suite:testParseAllMonths()
	self:assertDateEquals(nil, 1, nil, {'Enero'})
	self:assertDateEquals(nil, 2, nil, {'Pebrero'})
	self:assertDateEquals(nil, 3, nil, {'Marso'})
	self:assertDateEquals(nil, 4, nil, {'Abril'})
	self:assertDateEquals(nil, 5, nil, {'Mayo'})
	self:assertDateEquals(nil, 6, nil, {'Hunio'})
	self:assertDateEquals(nil, 7, nil, {'Hulio'})
	self:assertDateEquals(nil, 8, nil, {'Agosto'})
	self:assertDateEquals(nil, 9, nil, {'Septiembre'})
	self:assertDateEquals(nil, 10, nil, {'Oktubre'})
	self:assertDateEquals(nil, 11, nil, {'Nobiembre'})
	self:assertDateEquals(nil, 12, nil, {'Disiembre'})
end

function suite:testParseAllMons()
	self:assertDateEquals(nil, 1, nil, {'Ene'})
	self:assertDateEquals(nil, 2, nil, {'Peb'})
	self:assertDateEquals(nil, 3, nil, {'Mar'})
	self:assertDateEquals(nil, 4, nil, {'Abr'})
	self:assertDateEquals(nil, 5, nil, {'May'})
	self:assertDateEquals(nil, 6, nil, {'Hun'})
	self:assertDateEquals(nil, 7, nil, {'Hul'})
	self:assertDateEquals(nil, 8, nil, {'Ago'})
	self:assertDateEquals(nil, 9, nil, {'Sep'})
	self:assertDateEquals(nil, 10, nil, {'Okt'})
	self:assertDateEquals(nil, 11, nil, {'Nob'})
	self:assertDateEquals(nil, 12, nil, {'Dis'})
end

function suite:testParseSept()
	self:assertDateEquals(nil, 9, nil, {'Sept'})
	self:assertDateEquals(nil, 9, nil, {'sept'})
	self:assertDateEquals(2012, 9, 1, {'1 Sept 2012'})
	self:assertDateEquals(2012, 9, 1, {2012, 'Sept', 1})
end

-------------------------------------------------------------------------------
-- Date error tests
-------------------------------------------------------------------------------

function suite:testInvalidDateError()
	self:assertErrorContains("Ti 'foo' ket saan nga umiso a petsa", function ()
		Dts.new{'foo'}
	end)
end

function suite:testZeroYearError()
	self:assertErrorContains('dagiti tawen ket saan a mabalin a sero', function ()
		Dts.new{0}
	end)
end

function suite:testYearNonIntegerError()
	self:assertErrorContains('dagiti tawen ket nasken a sibubukel a bilang', function ()
		Dts.new{2015.5}
	end)
end

function suite:testYearLowRangeError()
	self:assertErrorContains(
		'dagiti tawen ket saan a mabalin a basbassit ngem −999,999,999,999',
		function ()
			Dts.new{-1000000000000}
		end,
		true -- plain match
	)
end

function suite:testYearHighRangeError()
	self:assertErrorContains('dagiti tawen ket saan a mabalin nga dakdakkel ngem 999,999,999,999', function ()
		Dts.new{1000000000000}
	end)
end

function suite:testMonthError()
	self:assertErrorContains('dagiti bulan ket nasken a sibubukel a bilang iti pagbaetan ti 1 ken 12', function ()
		Dts.new{2000, 0}
	end)
	self:assertErrorContains('dagiti bulan ket nasken a sibubukel a bilang iti pagbaetan ti 1 ken 12', function ()
		Dts.new{2000, 13}
	end)
end

function suite:testDayError()
	self:assertErrorContains('dagiti aldaw ket nasken a sibubukel a bilang iti pagbaetan ti 1 ken 31', function ()
		Dts.new{2000, 1, 0}
	end)
	self:assertErrorContains('dagiti aldaw ket nasken a sibubukel a bilang iti pagbaetan ti 1 ken 31', function ()
		Dts.new{2000, 1, 32}
	end)
end

function suite:testBCError()
	self:assertErrorContains(
		"Ti 'foo' ket saan nga umiso a kodigo ti panawen (manamnama ti 'BC', 'BCE', 'AD' wenno 'CE')",
		function ()
			Dts.new{2000, 1, 27, 'foo'}
		end,
		true -- plain match
	)
end

-------------------------------------------------------------------------------
-- Sort key tests
-------------------------------------------------------------------------------

function suite:testSortKeyYMD()
	suite:assertSortKeyEquals('000000002000-05-27-0000', {2000, 5, 27})
	suite:assertSortKeyEquals('000000002000-01-27-0000', {2000, 1, 27})
	suite:assertSortKeyEquals('000000002000-01-01-0000', {2000, 1, 1})
	suite:assertSortKeyEquals('000000000001-01-01-0000', {1, 1, 1})
end

function suite:testSortKeyYM()
	suite:assertSortKeyEquals('000000002000-05-01-0000', {2000, 5, nil})
end

function suite:testSortKeyYD()
	suite:assertSortKeyEquals('000000002000-01-27-0000', {2000, nil, 27})
end

function suite:testSortKeyMD()
	suite:assertSortKeyEquals('00000000' .. suite.currentYear .. '-05-27-0000', {nil, 5, 27})
end

function suite:testSortKeyY()
	suite:assertSortKeyEquals('000000002000-01-01-0000', {2000, nil, nil})
end

function suite:testSortKeyM()
	suite:assertSortKeyEquals('00000000' .. suite.currentYear .. '-05-01-0000', {nil, 5, nil})
end

function suite:testSortKeyD()
	suite:assertSortKeyEquals('00000000' .. suite.currentYear .. '-01-27-0000', {nil, nil, 27})
end

function suite:testSortKeyNegative()
	suite:assertSortKeyEquals('-999999999955-05-27-0000', {-45, 5, 27})
end

function suite:testSortKeyMaxYear()
	suite:assertSortKeyEquals('999999999999-01-01-0000', {999999999999})
end

function suite:testSortKeyMinYear()
	suite:assertSortKeyEquals('-000000000001-01-01-0000', {-999999999999})
end

function suite:testSortKeyBlank()
	suite:assertSortKeyEquals('999999999999-99-99-0000', {})
end

-------------------------------------------------------------------------------
-- addkey tests
-------------------------------------------------------------------------------

function suite:testAddkey()
	suite:assertSortKeyEquals('000000002000-05-27-0003', {2000, 5, 27, addkey = 3})
	suite:assertSortKeyEquals('000000002000-05-27-0003', {2000, 5, 27, addkey = '3'})
end

function suite:testAddkeyError()
	local msg = "ti parametro ti 'addkey' ket nasken a sibubukel a bilang iti pagbaetan ti 0 ken 9999"
	self:assertErrorContains(msg, function ()
		Dts.new{2000, 5, 27, addkey = 3.5}
	end)
	self:assertErrorContains(msg, function ()
		Dts.new{2000, 5, 27, addkey = -1}
	end)
	self:assertErrorContains(msg, function ()
		Dts.new{2000, 5, 27, addkey = 10000}
	end)
end

-------------------------------------------------------------------------------
-- Display tests
-------------------------------------------------------------------------------

function suite:testFormatDefault()
	suite:assertDisplayEquals('Enero 27, 2000', {2000, 1, 27})
end

function suite:testFormatDMY()
	suite:assertDisplayEquals('27 Enero 2000', {2000, 1, 27, format = 'dmy'})
end

function suite:testFormatMDY()
	suite:assertDisplayEquals('Enero 27, 2000', {2000, 1, 27, format = 'mdy'})
end

function suite:testFormatDM()
	suite:assertDisplayEquals('27 Enero', {2000, 1, 27, format = 'dm'})
end

function suite:testFormatMD()
	suite:assertDisplayEquals('Enero 27', {2000, 1, 27, format = 'md'})
end

function suite:testFormatMY()
	suite:assertDisplayEquals('Enero 2000', {2000, 1, 27, format = 'my'})
end

function suite:testFormatY()
	suite:assertDisplayEquals('2000', {2000, 1, 27, format = 'y'})
end

function suite:testFormatM()
	suite:assertDisplayEquals('Enero', {2000, 1, 27, format = 'm'})
end

function suite:testFormatD()
	suite:assertDisplayEquals('27', {2000, 1, 27, format = 'd'})
end

function suite:testFormatHide()
	suite:assertDisplayEquals('', {2000, 1, 27, format = 'hide'})
end

function suite:testBCParam()
	local result = 'Enero 27, 2000&nbsp;BC'
	suite:assertDisplayEquals(result, {2000, 1, 27, 'BC'})
	suite:assertDisplayEquals(result, {2000, 1, 27, 'BCE'})
	suite:assertDisplayEquals(result, {2000, 1, 27, 'bc'})
	suite:assertDisplayEquals(result, {2000, 1, 27, 'bce'})
end

function suite:testBCNegativeYear()
	suite:assertDisplayEquals('Enero 27, 2000&nbsp;BC', {-2000, 1, 27})
end

function suite:testLargeYearDisplay()
	suite:assertDisplayEquals('1,000,000', {1000000})
end

function suite:testLargeNegativeYearDisplay()
	suite:assertDisplayEquals('1,000,000&nbsp;BC', {-1000000})
end

-------------------------------------------------------------------------------
-- Blank tests
-------------------------------------------------------------------------------

function suite:testBlank()
	self:assertStringContains(
		'^<span data%-sort%-value="[^"<>]-"></span>$',
		mDts._main{}
	)
end

-------------------------------------------------------------------------------
-- Nowrap tests
-------------------------------------------------------------------------------

function suite:testNoWrapDefault()
	self:assertStringContains(
		self.nowrapPattern,		
		mDts._main{2000, 1, 1}
	)
end

function suite:testNoWrapOff()
	self:assertStringContainsWhenOff(
		'^<span data%-sort%-value="[^"<>]-">[^>]-</span>$',
		function (off)
			return mDts._main{2000, 1, 1, nowrap = off}
		end
	)
	self:assertNotStringContainsWhenOff(
		'white%-space%s*:%s*nowrap',
		function (off)
			return mDts._main{2000, 1, 1, nowrap = off}
		end
	)
end

function suite:testNoWrapOn()
	self:assertStringContainsWhenOn(
		self.nowrapPattern,
		function (on)
			return mDts._main{2000, 1, 1, nowrap = on}
		end
	)
end

-------------------------------------------------------------------------------
-- Abbr tests
-------------------------------------------------------------------------------

function suite:testAbbrDefault()
	self:assertStringContains(
		'Enero 1, 2000',
		mDts._main{2000, 1, 1}
	)
end

function suite:testAbbrOn()
	self:assertStringContainsWhenOn(
		'Ene 1, 2000',
		function (on)
			return mDts._main{2000, 1, 1, abbr = on}
		end
	)
end

function suite:testAbbrOff()
	self:assertStringContainsWhenOff(
		'Enero 1, 2000',
		function (off)
			return mDts._main{2000, 1, 1, abbr = off}
		end
	)
end

-------------------------------------------------------------------------------
-- Tracking category tests
-------------------------------------------------------------------------------

--function suite:testTrackingCategory()
--	self:assertStringContains(
--		'[[Kategoria:Dagiti plantilia ti Dts nga addaan kadagiti nabaliwanen a parametro]]',
--		mDts._main{2000, 1, 1, link = 'off'},
--		true -- plain match
--	)
--end

-------------------------------------------------------------------------------
-- Main tests
-------------------------------------------------------------------------------

function suite:testMain()
	local errorPattern = '^<strong class="error">Biddut iti %[%[Plantilia:Dts%]%]: .-</strong>'
	self:assertStringContains(errorPattern, mDts.main{'foo'})
	self:assertNotStringContains(errorPattern, mDts.main{2000})
	self:assertStringContains(errorPattern, mDts._main{'foo'})
	self:assertNotStringContains(errorPattern, mDts._main{2000})
end

return suite