local p = {}
local mError = require('Module:Error').error
local fullurl = require('Module:Fullurl')._fullurl

local gFrame = mw.getCurrentFrame()
local useLang = gFrame:callParserFunction{ name = 'int', args = {'Lang'} }

local isHeadingHTMLChange = false -- https://www.mediawiki.org/wiki/Heading_HTML_changes

local function buildEditLink(sectionEditLinkData)
	local sectionId, sectionName
	local title = sectionEditLinkData.title
	if sectionEditLinkData.sectionId then
		sectionId = sectionEditLinkData.sectionId
		sectionName = sectionEditLinkData.sectionName or p.getSectionNameByID(title, sectionId)
	else
		sectionName = sectionEditLinkData.sectionName
		sectionId = p.getSectionIDByName(title, sectionName)
	end
	return mw.html.create('span'):addClass('mw-editsection plainlinks')
		:node(mw.html.create('span'):addClass('mw-editsection-bracket'):wikitext('['))
		:wikitext(
			fullurl({
				title = title,
				action = 'edit',
				section = sectionId,
				text = tostring(
					mw.html.create('span')
						:attr('title', mw.message.new('editsectionhint', {sectionName}):inLanguage(useLang):plain())
						:wikitext(mw.message.new('editsection', {title}):inLanguage(useLang):plain())
						:done()
					)
			})
		)
		:node(mw.html.create('span'):addClass('mw-editsection-bracket'):wikitext(']'))
		:done()
end

local function buildFakeHOld(text, level, id, sectionEditLinkData)
	local wrapper = mw.html.create('h' .. id)
	local headline = wrapper:tag('span'):addClass('mw-headline')
	if id then
		headline = headline:attr('id', id)
	end
	if sectionEditLinkData then
		wrapper:node(buildEditLink(sectionEditLinkData))
	end
	return wrapper:done()
end

local function buildFakeHNew(text, level, id, sectionEditLinkData)
	local wrapper = mw.html.create('span'):addClass('mw-heading mw-heading' .. level)
	local headline = wrapper:tag('h' .. level)
	if id then
		headline = headline:attr('id', id)
	end
	if sectionEditLinkData then
		wrapper:node(buildEditLink(sectionEditLinkData))
	end
	return wrapper:done()
end

local function buildFakeH(...)
	return (isHeadingHTMLChange and buildFakeHNew or buildFakeHOld)(...)
end

local function indexOf(arr, str)
	if not arr then
		return -1
	end
	for i, v in ipairs(arr) do
		if str == v then
			return i
		end
	end
	return -1
end

function p.getSectionsList(title)
	title = mw.title.new(title, '')
	if title then
		if not title:getContent() then
			return nil
		end
		local wikitext = title:getContent():gsub('{{[^}]+}}', ''):gsub('{%|(.-)%|}', '')
		local sections = {}
		local match = wikitext:match('%=([^\n]+)%=')
		while true do
			if not match then
				break
			end
			match = '=' .. match .. '='
			sections[#sections + 1] = mw.text.trim(match:gsub('^%=+([^\n%=]+)%=+$', '%1'), '\t\r\n\f%s')
			wikitext = wikitext:gsub(match, '')
			match = wikitext:match('%=([^\n]+)%=')
		end
		return sections
	end
	return nil
end

function p.getSectionIDByName(title, section) 
	if section == '__FIRST_SECTION__' then
		return 0
	end
	return indexOf(p.getSectionsList(title), section) or -1
end

function p.getSectionNameByID(title, section) 
	if tonumber(section) == 0 then
		return '首段'
	end
	return p.getSectionsList(title)[section] or ''
end

function p._main(text, configs)
	local frame = mw.getCurrentFrame()
	local level, id, title, sectionEditLinkData
	if type(configs) == 'string' then
		level = tonumber(configs)
	else
		level = tonumber(configs.level)
		id = configs.id
		if configs.editlink then
			if type(configs.editlink) == type('') then
				local preTitle, preSection
				for k, v in configs.editlink:gmatch("([^#]*)#(.+)") do
					preTitle = mw.text.trim(k)
					preSection = mw.text.trim(v)
					break
				end
				if preTitle ~= nil then
					sectionEditLinkData = {
						title = preTitle ~= '' and mw.title.new(preTitle).fullText or mw.title.getCurrentTitle().fullText,
						sectionName = preSection
					}
				else
					for k, v in configs.editlink:gmatch("([^,]+),(.+)") do
						preTitle = mw.text.trim(k)
						preSection = mw.text.trim(v)
						break
					end
					if preTitle ~= nil then
						if tonumber(preSection) ~= nil then
							sectionEditLinkData = {
								title = preTitle ~= '' and mw.title.new(preTitle).fullText or mw.title.getCurrentTitle().fullText,
								sectionId = tonumber(preSection)
							}
						else
							sectionEditLinkData = {
								title = preTitle ~= '' and mw.title.new(preTitle).fullText or mw.title.getCurrentTitle().fullText,
								sectionName = preSection
							}
						end
					end
				end
			elseif configs.editlink.section then
				if tonumber(configs.editlink.section) then
					sectionEditLinkData = {
						title = configs.editlink.title ~= '' and mw.title.new(configs.editlink.title).fullText or mw.title.getCurrentTitle().fullText,
						sectionId = tonumber(configs.editlink.section)
					}
				else
					sectionEditLinkData = {
						title = configs.editlink.title ~= '' and mw.title.new(configs.editlink.title).fullText or mw.title.getCurrentTitle().fullText,
						sectionName = configs.editlink.section
					}
				end
			else
				sectionEditLinkData = configs.editlink
				sectionEditLinkData.title = sectionEditLinkData.editlink.title ~= ''
					and mw.title.new(sectionEditLinkData.editlink.title).fullText
					or mw.title.getCurrentTitle().fullText
			end
		end
	end
	if not level or indexOf({1, 2, 3, 4, 5, 6}, level) == -1 then
		return mError({[1] = '無法解釋章節等級' .. tostring(frame:extensionTag('syntaxhighlight', '<h' .. level .. '></h' .. level .. '>', {lang = 'html', inline = ''}))})
	end
	return buildFakeH(text, level, id, sectionEditLinkData)
end

function p.main(frame)
	local text = ''
	local getArgs = require('Module:Arguments').getArgs
	local configs = getArgs(frame, {
		frameOnly = true
	})
	local args = getArgs(frame, {
		removeBlanks = false,
		valueFunc = (function (key, value)
			if key == 1 then
				return value
			end
			return nil
		end),
		parentFirst = true
	})
	if not args[1] then
		text = '標題'
	else
		text = args[1]
	end
	if args['editlink'] then
		configs['editlink'] = args['editlink']
	end
	if args['id'] then
		configs['id'] = args['id']
	end
	return p._main(text, configs)
end

return p