-- Add individual test result to an aggregated test result
local function buildFailureTestResult(testPath: Config_Path, err: SerializableError): TestResult
	return {
		console = nil,
		displayName = nil,
		failureMessage = nil,
		leaks = false,
		numFailingTests = 0,
		numPassingTests = 0,
		numPendingTests = 0,
		numTodoTests = 0,
		openHandles = {},
		perfStats = { end_ = 0, runtime = 0, slow = false, start = 0 },
		skipped = false,
		snapshot = {
			added = 0,
			fileDeleted = false,
			matched = 0,
			unchecked = 0,
			uncheckedKeys = {},
			unmatched = 0,
			updated = 0,
		},
		testExecError = err,
		testFilePath = testPath,
		testResults = {},
	}
end

local function addResult(aggregatedResults: AggregatedResult, testResult: TestResult): ()
	-- `todos` are new as of Jest 24, and not all runners return it.
	-- Set it to `0` to avoid `NaN`
	if not Boolean.toJSBoolean(testResult.numTodoTests) then
		testResult.numTodoTests = 0
	end
	table.insert(aggregatedResults.testResults, testResult)
	aggregatedResults.numTotalTests += testResult.numPassingTests + testResult.numFailingTests + testResult.numPendingTests + testResult.numTodoTests
	aggregatedResults.numFailedTests += testResult.numFailingTests
	aggregatedResults.numPassedTests += testResult.numPassingTests
	aggregatedResults.numPendingTests += testResult.numPendingTests
	aggregatedResults.numTodoTests += testResult.numTodoTests
	if Boolean.toJSBoolean(testResult.testExecError) then
		aggregatedResults.numRuntimeErrorTestSuites += 1
	end
	if Boolean.toJSBoolean(testResult.skipped) then
		aggregatedResults.numPendingTestSuites += 1
	elseif Boolean.toJSBoolean(testResult.numFailingTests > 0 or testResult.testExecError) then
		aggregatedResults.numFailedTestSuites += 1
	else
		aggregatedResults.numPassedTestSuites += 1
	end -- Snapshot data
	if Boolean.toJSBoolean(testResult.snapshot.added) then
		aggregatedResults.snapshot.filesAdded += 1
	end
	if Boolean.toJSBoolean(testResult.snapshot.fileDeleted) then
		aggregatedResults.snapshot.filesRemoved += 1
	end
	if Boolean.toJSBoolean(testResult.snapshot.unmatched) then
		aggregatedResults.snapshot.filesUnmatched += 1
	end
	if Boolean.toJSBoolean(testResult.snapshot.updated) then
		aggregatedResults.snapshot.filesUpdated += 1
	end
	aggregatedResults.snapshot.added += testResult.snapshot.added
	aggregatedResults.snapshot.matched += testResult.snapshot.matched
	aggregatedResults.snapshot.unchecked += testResult.snapshot.unchecked
	if
		Boolean.toJSBoolean(
			if Boolean.toJSBoolean(testResult.snapshot.uncheckedKeys)
				then #testResult.snapshot.uncheckedKeys > 0
				else testResult.snapshot.uncheckedKeys
		)
	then
		table.insert(
			aggregatedResults.snapshot.uncheckedKeysByFile,
			{ filePath = testResult.testFilePath, keys = testResult.snapshot.uncheckedKeys }
		)
	end
	aggregatedResults.snapshot.unmatched += testResult.snapshot.unmatched
	aggregatedResults.snapshot.updated += testResult.snapshot.updated
	aggregatedResults.snapshot.total += testResult.snapshot.added + testResult.snapshot.matched + testResult.snapshot.unmatched + testResult.snapshot.updated
end

local function createEmptyTestResult(): TestResult
	return {
		leaks = false, -- That's legacy code, just adding it as needed for typing
		numFailingTests = 0,
		numPassingTests = 0,
		numPendingTests = 0,
		numTodoTests = 0,
		openHandles = {},
		perfStats = { end_ = 0, runtime = 0, slow = false, start = 0 },
		skipped = false,
		snapshot = {
			added = 0,
			fileDeleted = false,
			matched = 0,
			unchecked = 0,
			uncheckedKeys = {},
			unmatched = 0,
			updated = 0,
		},
		testFilePath = "",
		testResults = {},
	}
end

return {
	makeEmptyAggregatedTestResult = makeEmptyAggregatedTestResult,
	buildFailureTestResult = buildFailureTestResult,
	addResult = addResult,
	createEmptyTestResult = createEmptyTestResult,
}