local createVersion = require("/apis/libversion")

local SUCCESS_MARKER = "__TRAPOS_TEST_OK__"
local DEFAULT_REPORT_PATH = "/trapos-test-report"

local function printUsage()
	print("runtest usage:")
	print()
	print("\t\truntest [--pretty] [--verbose] [--output <path>] [--timeout <seconds>] [--no-timeout] [--shutdown] [test ...]")
	print("\t\truntest --version")
	print("\t\truntest --help")
end

local function parseArgs(args)
	local opts = {
		pretty = false,
		verbose = false,
		shutdown = false,
		outputPath = nil,
		timeout = nil,
		noTimeout = false,
		tests = {},
	}

	local i = 1
	while i <= #args do
		local arg = args[i]
		if arg == "--version" or arg == "-version" then
			print("v" .. createVersion().forSelf())
			return nil
		elseif arg == "--help" or arg == "-help" then
			printUsage()
			return nil
		elseif arg == "--pretty" then
			opts.pretty = true
		elseif arg == "--verbose" or arg == "-v" then
			opts.pretty = true
			opts.verbose = true
		elseif arg == "--output" then
			opts.outputPath = args[i + 1]
			i = i + 1
		elseif arg == "--timeout" then
			opts.timeout = args[i + 1]
			i = i + 1
		elseif arg == "--no-timeout" then
			opts.noTimeout = true
		elseif arg == "--shutdown" then
			opts.shutdown = true
		elseif string.sub(arg, 1, 1) == "-" then
			print("Unknown option: " .. arg)
			printUsage()
			return nil
		else
			opts.tests[#opts.tests + 1] = arg
		end
		i = i + 1
	end

	return opts
end

local function normalizeTestPath(path)
	if string.sub(path, 1, 1) == "/" then
		return path
	end
	return "/" .. path
end

local function discoverTests()
	local tests = {}
	if not fs.exists("/tests") then
		return tests
	end

	for _, name in ipairs(fs.list("/tests")) do
		local path = "/tests/" .. name
		if not fs.isDir(path) and string.sub(name, -4) == ".lua" then
			tests[#tests + 1] = path
		end
	end
	table.sort(tests)
	return tests
end

local function readLines(path)
	local lines = {}
	local file = fs.open(path, "r")
	if not file then
		return lines
	end

	while true do
		local line = file.readLine()
		if line == nil then
			break
		end
		lines[#lines + 1] = line
	end
	file.close()
	return lines
end

local function createEmitter(outputPath)
	local file = nil
	if outputPath then
		file = fs.open(outputPath, "w")
	end

	local function emit(line)
		if file then
			file.writeLine(line)
		else
			print(line)
		end
	end

	local function close()
		if file then
			file.close()
		end
	end

	return emit, close
end

local function renderReport(emit, script, reportLines, ok, verbose, color)
	local green = color and string.char(27) .. "[32m" or ""
	local red = color and string.char(27) .. "[31m" or ""
	local dim = color and string.char(27) .. "[2m" or ""
	local reset = color and string.char(27) .. "[0m" or ""
	local displayScript = string.sub(script, 1, 1) == "/" and string.sub(script, 2) or script

	emit(displayScript)
	if verbose then
		emit("  " .. dim .. "report: " .. DEFAULT_REPORT_PATH .. reset)
	end

	if #reportLines == 0 then
		if ok then
			emit("  " .. green .. "[OK]" .. reset .. " script completed")
		else
			emit("  " .. red .. "[KO]" .. reset .. " script failed before reporting a case")
		end
		return
	end

	for _, line in ipairs(reportLines) do
		if string.sub(line, 1, 3) == "OK " then
			emit("  " .. green .. "[OK]" .. reset .. " " .. string.sub(line, 4))
		elseif string.sub(line, 1, 3) == "KO " then
			emit("  " .. red .. "[KO]" .. reset .. " " .. string.sub(line, 4))
		elseif verbose and string.sub(line, 1, 4) == "LOG " then
			emit("  " .. dim .. "[log] " .. string.sub(line, 5) .. reset)
		end
	end
end

local opts = parseArgs({ ... })
if not opts then
	return
end

local tests = opts.tests
if #tests == 0 then
	tests = discoverTests()
else
	for i, path in ipairs(tests) do
		tests[i] = normalizeTestPath(path)
	end
end

if #tests == 0 then
	print("FAIL: no tests found")
	if opts.shutdown then
		os.shutdown()
	end
	return
end

local emit, closeOutput = createEmitter(opts.outputPath)
local suiteOk = true

for _, script in ipairs(tests) do
	fs.delete(DEFAULT_REPORT_PATH)

	local scriptArgs = { "--no-marker", "--report", DEFAULT_REPORT_PATH }
	if opts.verbose then
		scriptArgs[#scriptArgs + 1] = "--verbose"
	elseif opts.pretty then
		scriptArgs[#scriptArgs + 1] = "--pretty"
	end
	if opts.noTimeout then
		scriptArgs[#scriptArgs + 1] = "--no-timeout"
	elseif opts.timeout then
		scriptArgs[#scriptArgs + 1] = "--timeout"
		scriptArgs[#scriptArgs + 1] = opts.timeout
	end

	local ok = shell.run(script, table.unpack(scriptArgs))
	local reportLines = readLines(DEFAULT_REPORT_PATH)

	if opts.pretty then
		renderReport(emit, script, reportLines, ok, opts.verbose, opts.outputPath ~= nil)
	end

	if not ok then
		suiteOk = false
		break
	end
end

closeOutput()

if suiteOk then
	print(SUCCESS_MARKER)
else
	print("FAIL: CraftOS integration tests failed")
end

if opts.shutdown then
	os.shutdown()
end
