local fs = require("@lune/fs")

local moonwave = require("./moonwave")
local logger = require("./log")

local function writeSectionHeader(buf: string, title: string)
	buf ..= `## {title}\n`
	return buf
end

local function writeRef(buf: string, name: string, fragment: string?)
	buf ..= `\n[{name}]: #{fragment or name}\n`
	return buf
end

local function writeClass(buf: string, name: string, desc: string)
	buf ..= `# \`{name}\`\n`
	buf ..= desc
	buf ..= `\n\n`
	return buf
end

local function writeDeclaration(buf: string, name: string, fields: { moonwave.Property })
	buf ..= `\`\`\`luau\n`
	buf ..= `export type {name} = \{\n`
	for _, field in fields do
		buf ..= `\t{field.name}: {field.lua_type},\n`
	end
	buf ..= "}\n"
	buf ..= `\`\`\`\n`
	return buf
end

local function writeProperty(buf: string, name: string, desc: string, type: string)
	-- buf ..= `- **\`{name}: {type}\`** - {desc}\n`
	buf ..= `- **{name}** - {desc}\n`
	return buf
end

local function writeFunction(
	buf: string,
	class: string,
	type: string,
	name: string,
	desc: string,
	params: { moonwave.FunctionParam },
	returns: { moonwave.FunctionReturn },
	private: boolean
)
	local sep = if type == "method" then ":" else "."
	local declaredSignature = `{class}{sep}{name}`
	buf ..= `### \`{name}\`\n`

	if private then
		buf ..= `> [!IMPORTANT]\n`
		buf ..= `> This is a private API. It may be exported publicly, but try to avoid\n`
		buf ..= `> using this API, since it can have breaking changes at any time without\n`
		buf ..= `> warning.\n\n`
	end

	buf ..= `{desc}\n`
	buf ..= `\`\`\`luau\n`
	buf ..= `{declaredSignature}(`
	if #params > 0 then
		buf ..= "\n"
		for _, param in params do
			buf ..= `\t{param.name}: {param.lua_type}, -- {param.desc}\n`
		end
	end
	buf ..= `)`

	if #returns > 0 then
		if #returns == 1 then
			buf ..= `: {returns[1].lua_type}\n`
		else
			for pos, ret in returns do
				buf ..= `({ret.lua_type}`
				if pos ~= #returns then
					buf ..= `, `
				end
			end
			buf ..= `)`
		end
	end

	buf ..= `\n\`\`\`\n`
	buf = writeRef(buf, declaredSignature, name)
	return buf
end

local function writeType(buf: string, name: string, desc: string, type: string)
	buf ..= `\`\`\`luau\n`
	buf ..= `export type {name} = {type}\n`
	buf ..= `\`\`\`\n`
	return buf
end

local function writeMarkdown(path: string, items: { moonwave.Item })
	local start = os.clock()
	local buf = "<!-- This file was @generated by `lune run docsgen`, please do not edit this manually, changes will be overwritten. -->\n"
  buf ..= "# Reference"

	for _, item in items do
		logger.log("info", "Generating docs for", item.name)
		buf = writeClass(buf, item.name, item.desc)

		local props: { moonwave.Property } = {}
		for pos, type in item.types do
			if type.name == item.name then
				table.remove(item.types, pos)
				props = type.fields
			end
		end

		buf = writeDeclaration(buf, item.name, props)
		buf = writeSectionHeader(buf, "Properties")
		for _, prop in props do
			if prop.ignore then
				continue
			end

			buf = writeProperty(buf, prop.name, prop.desc, prop.lua_type)
		end
		buf ..= "\n"

		buf = writeSectionHeader(buf, "API")
		for _, func in item.functions do
			if func.ignore then
				continue
			end

			buf = writeFunction(
				buf,
				item.name,
				func.function_type,
				func.name,
				func.desc,
				func.params,
				func.returns,
				func.private
			)
		end
		buf ..= "\n"

		buf = writeSectionHeader(buf, "Types")
		for _, type in item.types do
			if type.ignore then
				continue
			end

			buf ..= `### \`{type.name}\`\n`
			if type.private then
				buf ..= `> [!IMPORTANT]\n`
				buf ..= `> This is a private type. It may be exported publicly, but try to avoid\n`
				buf ..= `> using it, since its definition can have a breaking change at any time\n`
				buf ..= `> without warning.\n\n`
			end
			buf ..= `{type.desc}\n`
			if type.lua_type ~= nil then
				buf = writeType(buf, type.name, type.desc, type.lua_type)
			else
				local fields: { moonwave.Property } = type.fields or {}
				buf = writeDeclaration(buf, type.name, fields)
				for _, field in fields do
					buf = writeProperty(buf, field.name, field.desc, field.lua_type)
				end
			end
			buf = writeRef(buf, type.name)
		end

		buf = writeRef(buf, item.name)
	end

	logger.log("info", string.format("Generated docs in %.2fms", (os.clock() - start) * 1000))
	logger.log("info", "Writing to", path)
	fs.writeFile(path, buf)
end

return writeMarkdown