--ui/widgets.lua, reusable option-descriptor helpers for DF:BuildMenu (frontend).
--these build the plain descriptor tables BuildMenu consumes; no backend logic here.

local _ = nil
local addonName, private = ...

local detailsFramework = DetailsFramework

private.UI = private.UI or {}

---@class FPSPP_Widgets
local Widgets = {}
private.UI.Widgets = Widgets

--returns the five standard DF option templates in BuildMenu's expected order:
--text, dropdown, switch, slider, button.
function Widgets.GetOptionTemplates()
	return
		detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE"),
		detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"),
		detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_TEMPLATE"),
		detailsFramework:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE"),
		detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE")
end

--builds a BuildMenu "label" descriptor used as a section header.
---@param text string
---@return table
function Widgets.SectionHeader(text)
	return {
		type = "label",
		get = function() return text end,
		text_template = detailsFramework:GetTemplate("font", "ORANGE_FONT_TEMPLATE"),
	}
end

--builds a BuildMenu "toggle" descriptor bound to a dotted profile path; flipping it
--writes the profile and re-applies optimizations.
---@param path string
---@param name string
---@param description string
---@return table
function Widgets.ProfileToggle(path, name, description)
	return {
		type = "toggle",
		name = name,
		desc = description,
		boxfirst = true,
		get = function() return private.Core.Profile:GetBool(path) end,
		--toggle set signature is (widget, fixedParam, value); value is a boolean.
		set = function(_, _, value)
			private.Core.Profile:SetBool(path, value)
			private.Core.Optimizer:ApplyProfile()
		end,
	}
end

--builds a BuildMenu "toggle" descriptor for a frame-hide group (manual_hide.*): writes the
--profile and re-applies. the group hides only while the context gate is active (core/context.lua).
---@param groupKey string
---@param name string
---@param description string
---@return table
function Widgets.ManualToggle(groupKey, name, description)
	local path = "manual_hide." .. groupKey
	return {
		type = "toggle",
		name = name,
		desc = description,
		boxfirst = true,
		get = function() return private.Core.Profile:GetBool(path) end,
		--toggle set signature is (widget, fixedParam, value); value is a boolean.
		set = function(_, _, value)
			private.Core.Profile:SetBool(path, value)
			private.Core.Optimizer:ApplyProfile()
		end,
	}
end

--builds a BuildMenu "toggle" descriptor driven by custom get/set callbacks, for settings that are
--not a simple profile path (e.g. account-wide options stored in the saved-variables root).
---@param name string
---@param description string
---@param getValue function
---@param setValue function
---@return table
function Widgets.Toggle(name, description, getValue, setValue)
	return {
		type = "toggle",
		name = name,
		desc = description,
		boxfirst = true,
		get = function() return getValue() end,
		--toggle set signature is (widget, fixedParam, value); value is a boolean.
		set = function(_, _, value) setValue(value) end,
	}
end

--builds a "Live FPS Test" toggle that immediately hides/shows a frame group, bypassing the context
--gate (unlike ManualToggle, which writes the profile and waits on the gate). refreshes the live
--window afterward so the checkbox reflects the actual applied state (a protected frame blocked in
--combat stays as-is).
---@param groupKey string
---@param name string
---@param description string
---@return table
function Widgets.LiveHideToggle(groupKey, name, description)
	return {
		type = "toggle",
		name = name,
		desc = description,
		boxfirst = true,
		get = function() return private.Core.ManualHide:IsGroupHidden(groupKey) end,
		--toggle set signature is (widget, fixedParam, value); value is a boolean.
		set = function(_, _, value)
			private.Core.ManualHide:SetGroupHidden(groupKey, value)
			private.UI.Options:RefreshLiveTestIfOpen()
		end,
	}
end

--builds a "Live FPS Test" toggle that immediately reduces/restores one managed graphics CVar,
--bypassing the context gate (unlike the profile graphics_cvars toggles, which wait on the gate).
---@param fieldName string
---@param name string
---@param description string
---@return table
function Widgets.LiveCVarToggle(fieldName, name, description)
	return {
		type = "toggle",
		name = name,
		desc = description,
		boxfirst = true,
		get = function() return private.Core.Optimizer:IsLiveCVarActive(fieldName) end,
		--toggle set signature is (widget, fixedParam, value); value is a boolean.
		set = function(_, _, value)
			private.Core.Optimizer:SetLiveCVar(fieldName, value)
		end,
	}
end

--builds a BuildMenu "range" (slider) descriptor bound to a numeric CVar: reads the CVar's current
--value and, on drag, sets it live through the reversible CVars engine (so Restore All / disable puts
--it back to its original). used for graphics CVars that take a value rather than on/off.
--optionally drives a second, derived CVar from the same slider value: pass companionCvar plus
--mapCompanion(value) -> companion value (e.g. farclip -> graphicsViewDistance). both are reversible.
--NOTE: an unknown CVar name makes GetCVar/SetCVar no-op (see core/cvars.lua), so a wrong name is
--harmless but does nothing -- verify the name in-client.
---@param cvar string
---@param name string
---@param description string
---@param minValue number
---@param maxValue number
---@param step number
---@param companionCvar string? a second CVar set alongside the first
---@param mapCompanion function? maps the slider value to the companion CVar's value
---@return table
function Widgets.CVarSlider(cvar, name, description, minValue, maxValue, step, companionCvar, mapCompanion)
	return {
		type = "range",
		name = name,
		desc = description,
		min = minValue,
		max = maxValue,
		step = step,
		--hidden from the menu entirely: these CVars are driven by the "View Distance" toggle now, so the
		--slider is not shown. BuildMenu skips any entry with `hidden = true` (it takes up no space).
		hidden = true,
		get = function() return tonumber(GetCVar(cvar)) or minValue end,
		--range set signature is (widget, fixedParam, value); value is the new slider number.
		set = function(_, _, value)
			private.Core.CVars:Set(cvar, value)
			--optional derived CVar (e.g. the view-distance quality scale) set from the same value.
			if companionCvar and mapCompanion then
				private.Core.CVars:Set(companionCvar, mapCompanion(value))
			end
		end,
	}
end

--builds a BuildMenu "select" (dropdown) descriptor listing every saved profile; choosing one switches
--to it. it is disabled while auto-switch-by-role is on (the spec role decides the profile then) -- the
--disableif is re-checked on every options refresh, so toggling auto-switch enables/disables it live.
---@param name string
---@param description string
---@return table
function Widgets.ProfileDropdown(name, description)
	return {
		type = "select",
		name = name,
		desc = description,
		get = function() return private.Core.Profile:GetCurrentName() end,
		values = function()
			local options = {}
			for index, profileName in ipairs(private.Core.Profile:GetNames()) do
				--onclick receives (dropdown, fixedParam, value); value is the option's value.
				options[#options + 1] = {
					value = profileName,
					label = profileName,
					onclick = function(_, _, selectedProfile) private.Core.Profile:Switch(selectedProfile) end,
				}
			end

			return options
		end,
		disableif = function() return private.Core.Profile:IsAutoSwitchByRole() end,
	}
end

--builds a BuildMenu "blank" spacer descriptor.
---@return table
function Widgets.Blank()
	return {type = "blank"}
end

--builds a BuildMenu "breakline" descriptor: starts a new column, so everything after it renders in
--the next column of the panel instead of continuing down.
---@return table
function Widgets.Breakline()
	return {type = "breakline"}
end

--builds a BuildMenu "execute" (button) descriptor; BuildMenu calls onClick on click.
---@param name string
---@param description string
---@param onClick function
---@return table
function Widgets.Button(name, description, onClick)
	return {
		type = "execute",
		name = name,
		desc = description,
		func = onClick,
	}
end
