-- lx-bundle:true

-- This file is part of Lx, created by Liqube <www.liqube.com>
-- Copyright Liqube Audio. All rights reserved.
-- Lx Interim License. See LICENSE for details.

local core = require("core")

local test = {}
test._current_unit_path = nil
test._last_finished = nil

-- Derives default unit label from test file path (basename without .lua)
local function default_label_from_path(path)
	if type(path) ~= "string" or path == "" then
		return "test"
	end
	local name = path:match("([^/\\]+)$") or path
	return (name:gsub("%.lua$", ""))
end

-- Sets currently running unit path for automatic test-suite labels
function test._set_current_unit(path)
	test._current_unit_path = path
end

-- Returns and clears the last suite finish payload for the runner
function test._take_finished_result()
	local out = test._last_finished
	test._last_finished = nil
	return out
end

-- Creates an isolated test state for one test file
function test.new(opts)
	opts = opts or {}
	local state = {
		label = opts.label or default_label_from_path(test._current_unit_path),
		total = 0,
		passed = 0,
		failed = 0,
		failures = {},
	}

	-- Shared test-case executor bound to this suite state
	local function run_bound(name, fn)
		state.total = state.total + 1
		local ok, a, b = pcall(fn)
		if not ok then
			state.failed = state.failed + 1
			-- Keep exact failing test case details for runner-level unit summaries
			state.failures[#state.failures + 1] = string.format("%s: crashed: %s", tostring(name), tostring(a))
			core.error("[Test] <%s> crashed: %s\n%s", state.label, name, tostring(a))
			return false, a
		end
		if a then
			state.passed = state.passed + 1
		else
			state.failed = state.failed + 1
			-- Keep exact failing test case details for runner-level unit summaries
			state.failures[#state.failures + 1] = string.format("%s: fail: %s", tostring(name), tostring(b or ""))
		end
		core.log("[Test] %s: %s -> %s %s", state.label, name, tostring(a), b and ("| " .. tostring(b)) or "")
		return a ~= false, b
	end

	-- Runs a test case; supports both bound ':' and unbound function usage
	function state.run(a, b, c)
		if a == state then
			return run_bound(b, c)
		end
		return run_bound(a, b)
	end

	-- Emits final suite summary and returns ok,msg for test-runner integration
	function state:finish()
		local msg
		if self.failed > 0 then
			msg = string.format("Failed %d of %d tests", self.failed, self.total)
		else
			msg = string.format("Completed %d tests (%d ok, %d fail)", self.total, self.passed, self.failed)
		end
		test._last_finished = {
			label = self.label,
			total = self.total,
			passed = self.passed,
			failed = self.failed,
			ok = (self.failed == 0),
			message = msg,
			failures = self.failures,
		}
		if self.failed > 0 then
			core.error("[Test] %s: %s", self.label, msg)
			return false, msg
		else
			core.log("[Test] %s: %s", self.label, msg)
			return true, msg
		end
	end

	return state
end

return test
