-- 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 debug = {}
local tests = require("core.debug.tests")

-- Centralized defaults for all debug-oriented config switches
local DEBUG_DEFAULTS = {
	project_scan_debug = false,
	project_scan_warn_ms = 120,
	debug_browser = false, -- Unified Browser debug toggle for root and scan logging
	debug_font_load = false, -- Enable font-load timing logs
	debug_font_load_warn_ms = 10, -- Slow font-load warning threshold in ms
	debug_escape_keymap = false,
	debug_keymap = false,
	debug_keymap_strokes = {},
	debug_auto_run_tests = true, -- Automatically run tests/*.lua on startup
	debug_syntax_render_perf = false,
}

local syntax_render_perf_stats = {
	highlighter_worker_work_cycles = 0,
	highlighter_get_line_provisional = 0,
	highlighter_get_line_exact = 0,
	editor_tokens_emitted = 0,
	editor_text_draw_calls = 0,
}

local function reset_syntax_render_perf_stats()
	for key in pairs(syntax_render_perf_stats) do
		syntax_render_perf_stats[key] = 0
	end
end

function debug.apply_config_defaults(config)
	-- Populate missing debug keys while preserving user overrides
	for key, value in pairs(DEBUG_DEFAULTS) do
		if config[key] == nil then
			config[key] = value
		end
	end
end

function debug.apply_font_load_timing(config)
	-- Apply font timing config before style fonts load so startup loads are captured
	local debug_value = config.debug_font_load and true or false -- Use canonical debug font-load flag
	local warn_value = tonumber(config.debug_font_load_warn_ms) or 10 -- Use canonical debug font-load warn threshold
	if warn_value < 0 then
		warn_value = 0
	end
	renderer.font.set_load_timing(debug_value, warn_value)
end

function debug.attach(core)
	-- Track last applied values so runtime config edits only call C when changed
	core.font_load_timing_debug = nil
	core.font_load_timing_warn_ms = nil
end

function debug.step(core, config)
	-- Sync font timing debug settings live from Lua config into renderer
	local debug_value = config.debug_font_load and true or false -- Use canonical debug font-load flag
	local warn_value = tonumber(config.debug_font_load_warn_ms) or 10 -- Use canonical debug font-load warn threshold
	if warn_value < 0 then
		warn_value = 0
	end
	if core.font_load_timing_debug ~= debug_value or core.font_load_timing_warn_ms ~= warn_value then
		renderer.font.set_load_timing(debug_value, warn_value)
		core.font_load_timing_debug = debug_value
		core.font_load_timing_warn_ms = warn_value
	end

	-- Drain C-side font-load timing logs into the standard in-app log stream
	local logs = renderer.font.drain_load_logs()
	if type(logs) == "table" then
		for i = 1, #logs do
			core.log_quiet("%s", logs[i])
		end
	end
end

function debug.snapshot_args(args)
	-- Build a stable key=value snapshot of raw CLI args for diagnostics
	local items = {}
	if type(args) == "table" then
		for k, v in pairs(args) do
			items[#items + 1] = tostring(k) .. "=" .. tostring(v)
		end
	end
	table.sort(items)
	return table.concat(items, ", ")
end

--- Toggle Browser debug logging and report its new state
-- @param core table core API
-- @param config table runtime configuration
function debug.toggle_browser_logging(core, config)
	config.debug_browser = not config.debug_browser
	core.log("Browser debug logging: %s", config.debug_browser and "ON" or "OFF")
end

function debug.log_lua_info(core, open_log_view)
	-- Shared implementation for 'debug:lua-info' command
	local lua_version = tostring(_VERSION or "unknown")
	local jit_tbl = rawget(_G, "jit")
	local jit_version = (type(jit_tbl) == "table" and jit_tbl.version) or nil
	local has_jit = jit_version ~= nil
	local ok_load_shift, chunk_shift = pcall(load, "return 1>>1")
	local has_shift_syntax = ok_load_shift and type(chunk_shift) == "function"
	local bitops_ok = load("return (8 >> 1), (7 & 3)") ~= nil
	local has_pack = type(string.pack) == "function" and type(string.unpack) == "function"
	local utf8_tbl = rawget(_G, "utf8")
	local has_utf8_lib = type(utf8_tbl) == "table"

	local lines = {
		"Lua runtime info:",
		string.format(" _VERSION: %s", lua_version),
		string.format(" JIT: %s", has_jit and tostring(jit_version) or "no"),
		string.format(' shift syntax via pcall(load("return 1>>1")): %s', has_shift_syntax and "yes" or "no"),
		string.format(" 5.3 bitwise syntax (>>, &): %s", bitops_ok and "yes" or "no"),
		string.format(" string.pack/unpack: %s", has_pack and "yes" or "no"),
		string.format(" utf8 library: %s", has_utf8_lib and "yes" or "no"),
	}
	core.log("%s", table.concat(lines, "\n"))
	open_log_view()
end

function debug.log_fallback_stats(core, style, open_log_view)
	-- Shared implementation for 'debug:fallback-stats' command
	local lines = { "Font fallback stats" }
	local function log_stats(label, font)
		if not font then
			lines[#lines + 1] = string.format("  %s: nil", label)
			return
		end
		local s = renderer.font.get_fallback_stats(font)
		lines[#lines + 1] = string.format(
			" %s: group %s, total %d, loaded %d, pending %d, attempts %d, successes %d",
			label,
			tostring(s.is_group == true),
			tonumber(s.total) or 0,
			tonumber(s.loaded) or 0,
			tonumber(s.pending) or 0,
			tonumber(s.attempts) or 0,
			tonumber(s.successes) or 0
		)
	end
	log_stats("code_font", style.code_font)
	log_stats("font", style.font)
	log_stats("big_font", style.big_font)
	core.log("%s", table.concat(lines, "\n"))
	open_log_view()
end

function debug.record_syntax_render_perf(key, delta)
	local current = syntax_render_perf_stats[key]
	if current == nil then
		return
	end

	syntax_render_perf_stats[key] = current + (delta or 1)
end

function debug.dump_syntax_render_perf(core, config, open_log_view)
	local lines = {
		string.format(
			"Syntax/render perf counters (enabled=%s)",
			tostring(config.debug_syntax_render_perf == true)
		),
		string.format(
			" highlighter worker work cycles: %d",
			tonumber(syntax_render_perf_stats.highlighter_worker_work_cycles) or 0
		),
		string.format(
			" highlighter get_line provisional: %d",
			tonumber(syntax_render_perf_stats.highlighter_get_line_provisional) or 0
		),
		string.format(
			" highlighter get_line exact: %d",
			tonumber(syntax_render_perf_stats.highlighter_get_line_exact) or 0
		),
		string.format(
			" doc view tokens emitted: %d",
			tonumber(syntax_render_perf_stats.editor_tokens_emitted) or 0
		),
		string.format(
			" doc view text draw calls: %d",
			tonumber(syntax_render_perf_stats.editor_text_draw_calls) or 0
		),
	}

	local renderer_stats = renderer.get_perf_stats and renderer.get_perf_stats(true) or nil
	local font_stats = renderer.font.get_perf_stats and renderer.font.get_perf_stats(true) or nil

	if type(renderer_stats) == "table" then
		lines[#lines + 1] = string.format(
			" render width cache hits: %d",
			tonumber(renderer_stats.width_cache_hits) or 0
		)
		lines[#lines + 1] = string.format(
			" render width cache misses: %d",
			tonumber(renderer_stats.width_cache_misses) or 0
		)
		lines[#lines + 1] = string.format(
			" render glyph run cache hits: %d",
			tonumber(renderer_stats.glyph_run_cache_hits) or 0
		)
		lines[#lines + 1] = string.format(
			" render glyph run cache misses: %d",
			tonumber(renderer_stats.glyph_run_cache_misses) or 0
		)
	end

	if type(font_stats) == "table" then
		lines[#lines + 1] = string.format(
			" font resolve_for_codepoint calls: %d",
			tonumber(font_stats.resolve_for_codepoint_calls) or 0
		)
		lines[#lines + 1] = string.format(
			" font fallback cache hits: %d",
			tonumber(font_stats.fallback_resolution_cache_hits) or 0
		)
		lines[#lines + 1] = string.format(
			" font fallback cache misses: %d",
			tonumber(font_stats.fallback_resolution_cache_misses) or 0
		)
		lines[#lines + 1] = string.format(
			" font glyph metric cache hits: %d",
			tonumber(font_stats.glyph_metric_cache_hits) or 0
		)
		lines[#lines + 1] = string.format(
			" font glyph metric loads: %d",
			tonumber(font_stats.glyph_metric_loads) or 0
		)
		lines[#lines + 1] = string.format(
			" font glyph bitmap requests: %d",
			tonumber(font_stats.glyph_bitmap_requests) or 0
		)
		lines[#lines + 1] = string.format(
			" font glyph bitmap cache hits: %d",
			tonumber(font_stats.glyph_bitmap_cache_hits) or 0
		)
		lines[#lines + 1] = string.format(
			" font glyph bitmap loads: %d",
			tonumber(font_stats.glyph_bitmap_loads) or 0
		)
		lines[#lines + 1] = string.format(
			" font metric FT_Load_Glyph calls: %d",
			tonumber(font_stats.metric_load_glyph_calls) or 0
		)
		lines[#lines + 1] = string.format(
			" font bitmap FT_Load_Glyph calls: %d",
			tonumber(font_stats.bitmap_load_glyph_calls) or 0
		)
	end

	reset_syntax_render_perf_stats()
	core.log("%s", table.concat(lines, "\n"))
	open_log_view()
end

-- Delegate test execution into dedicated debug test unit module
debug.run_tests = tests.run_tests

return debug
