-- Portal Pro :: UI/Tab_Settings.lua

local ADDON_NAME, PP = ...
local UI, T, U = PP.UI, PP.Theme, PP.U
local L = PP.L

--------------------------------------------------------------------------------
-- Layout helper: a cursor that walks down the page
--------------------------------------------------------------------------------

local Cursor = {}
Cursor.__index = Cursor

local function newCursor(parent)
	return setmetatable({ parent = parent, y = 0 }, Cursor)
end

function Cursor:Section(title)
	self.y = self.y - (self.y == 0 and 0 or T.space.xl)
	local label = UI:Label(self.parent, title)
	label:SetPoint("TOPLEFT", 0, self.y)
	self.y = self.y - 16

	local rule = UI:Divider(self.parent)
	rule:SetPoint("TOPLEFT", 0, self.y)
	rule:SetPoint("TOPRIGHT", 0, self.y)
	self.y = self.y - T.space.md
	return label
end

function Cursor:Place(widget, height, indent)
	widget:SetPoint("TOPLEFT", indent or 0, self.y)
	self.y = self.y - (height or widget:GetHeight()) - T.space.sm
	return widget
end

--- Place two widgets on the same row.
function Cursor:Row(a, b, height)
	a:SetPoint("TOPLEFT", 0, self.y)
	if b then b:SetPoint("TOPLEFT", 260, self.y) end
	self.y = self.y - (height or a:GetHeight()) - T.space.sm
end

function Cursor:Gap(amount)
	self.y = self.y - (amount or T.space.md)
end

function Cursor:Height()
	return -self.y + T.space.xl
end

--------------------------------------------------------------------------------

local function build(parent)
	local frame = CreateFrame("Frame", nil, parent)

	local scroll = CreateFrame("ScrollFrame", nil, frame)
	scroll:SetPoint("TOPLEFT", T.space.lg, -T.space.lg)
	scroll:SetPoint("BOTTOMRIGHT", -T.space.lg, T.space.lg)

	local content = CreateFrame("Frame", nil, scroll)
	content:SetSize(1, 1)
	scroll:SetScrollChild(content)
	scroll:EnableMouseWheel(true)
	scroll:SetScript("OnMouseWheel", function(self, delta)
		local maxScroll = math.max(0, content:GetHeight() - self:GetHeight())
		self:SetVerticalScroll(U.Clamp(self:GetVerticalScroll() - delta * 40, 0, maxScroll))
	end)
	scroll:SetScript("OnSizeChanged", function(self, w) content:SetWidth(w) end)

	local c = newCursor(content)
	frame.widgets = {}
	local W = frame.widgets

	----------------------------------------------------------------------
	c:Section(L["Detection"])

	W.enabled = UI:Switch(content, "Scan chat for portal requests", PP.profile.detection.enabled,
		function(v) PP.profile.detection.enabled = v end)
	W.autoWhisper = UI:Switch(content, "Auto-whisper matched requests", PP.profile.detection.autoWhisper,
		function(v) PP.profile.detection.autoWhisper = v end)
	c:Row(W.enabled, W.autoWhisper, 22)

	W.autoInvite = UI:Switch(content, "Auto-invite matched requests", PP.profile.detection.autoInvite,
		function(v) PP.profile.detection.autoInvite = v end)
	W.ignoreCompetitors = UI:Switch(content, "Ignore other mages' adverts", PP.profile.detection.ignoreCompetitors,
		function(v) PP.profile.detection.ignoreCompetitors = v end)
	c:Row(W.autoInvite, W.ignoreCompetitors, 22)

	UI:Tooltip(W.autoWhisper, "Auto-whisper", {
		"Whispers your greeting the moment a request matches.",
		" ",
		"Off by default on purpose: aggressive auto-messaging gets you",
		"squelched by the server spam filter and is a ToS risk.",
	})

	W.requireDest = UI:Switch(content, "Only keep leads with a known destination", PP.profile.detection.requireDest,
		function(v) PP.profile.detection.requireDest = v end)
	W.sound = UI:Switch(content, "Play a sound on new leads", PP.profile.detection.sound,
		function(v) PP.profile.detection.sound = v end)
	c:Row(W.requireDest, W.sound, 22)

	W.toast = UI:Switch(content, "Show the pop-up lead alert", PP.profile.detection.toast,
		function(v) PP.profile.detection.toast = v end)
	W.flash = UI:Switch(content, "Flash the taskbar icon", PP.profile.detection.flash,
		function(v) PP.profile.detection.flash = v end)
	c:Row(W.toast, W.flash, 22)

	UI:Tooltip(W.toast, "Lead alert", {
		"Never appears while you're in combat.",
		" ",
		"Leads that arrive mid-fight are held and shown once you drop out",
		"of combat, newest first, with a count of any others still waiting.",
		"Expired leads are discarded rather than replayed.",
		" ",
		"The alert sound still plays immediately - that part is useful",
		"while you're fighting.",
	})

	c:Gap()

	-- Channels
	local channelLabel = UI:Text(content, "Channels to watch", T.fontSize.base, "", "textSecondary")
	c:Place(channelLabel, 18)

	local channelSpecs = {
		{ key = "TRADE", label = "Trade" }, { key = "GENERAL", label = "General" },
		{ key = "LFG", label = "LFG" }, { key = "SAY", label = "Say" },
		{ key = "YELL", label = "Yell" }, { key = "WHISPER", label = "Whisper" },
		{ key = "GUILD", label = "Guild" }, { key = "PARTY", label = "Party" },
	}
	W.channels = {}
	local col, rowY = 0, c.y
	for i, spec in ipairs(channelSpecs) do
		local sw = UI:Switch(content, spec.label, PP.profile.detection.channels[spec.key],
			function(v) PP.profile.detection.channels[spec.key] = v end)
		sw:SetPoint("TOPLEFT", col * 130, rowY)
		W.channels[spec.key] = sw
		col = col + 1
		if col == 4 then
			col = 0
			rowY = rowY - 26
		end
	end
	c.y = rowY - 30

	W.cooldown = UI:Slider(content, "Per-player cooldown", 30, 900, 30, PP.profile.detection.playerCooldown,
		function(v) PP.profile.detection.playerCooldown = math.floor(v) end,
		function(v) return U.Duration(v) end)
	W.cooldown:SetWidth(230)
	W.expiry = UI:Slider(content, "Lead expiry", 60, 900, 30, PP.profile.detection.leadExpiry,
		function(v) PP.profile.detection.leadExpiry = math.floor(v) end,
		function(v) return U.Duration(v) end)
	W.expiry:SetWidth(230)
	c:Row(W.cooldown, W.expiry, 44)

	----------------------------------------------------------------------
	c:Section(L["Pricing"])

	W.defaultPrice = UI:Input(content, "e.g. 10g or 5g50s", U.MoneyPlain(PP.profile.prices.default),
		function(value)
			local copper = U.ParseMoney(value)
			if copper then
				PP.profile.prices.default = copper
				PP:Fire("PRICES_CHANGED")
			end
		end)
	W.defaultPrice:SetWidth(140)
	local defaultLabel = UI:Text(content, "Default price", T.fontSize.base, "", "textSecondary")
	defaultLabel:SetPoint("LEFT", W.defaultPrice, "RIGHT", T.space.sm, 0)
	c:Place(W.defaultPrice, T.size.inputHeight)

	W.prices = {}
	for _, dest in ipairs(PP.Dest:Available()) do
		local explicit = PP.profile.prices[dest.key]
		local input = UI:Input(content, "default", explicit and U.MoneyPlain(explicit) or "",
			function(value)
				PP.Dest:SetPrice(dest.key, U.ParseMoney(value))
			end)
		input:SetWidth(140)

		local icon = content:CreateTexture(nil, "ARTWORK")
		icon:SetSize(16, 16)
		icon:SetTexture(PP.Dest:Icon(dest))
		icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
		icon:SetPoint("LEFT", input, "RIGHT", T.space.sm, 0)

		local label = UI:Text(content, dest.name, T.fontSize.base, "", "textSecondary")
		label:SetPoint("LEFT", icon, "RIGHT", T.space.xs, 0)

		W.prices[dest.key] = input
		c:Place(input, T.size.inputHeight)
	end

	----------------------------------------------------------------------
	c:Section("Tracking")

	local trackHint = UI:Text(content,
		"Which casts count as business. A portal for your dungeon group isn't a sale, and\n" ..
		"charging its rune against your margin makes the ledger lie.",
		T.fontSize.sm, "", "textMuted")
	trackHint:SetWordWrap(true)
	trackHint:SetJustifyH("LEFT")
	c:Place(trackHint, 30)

	W.ignoreInInstances = UI:Switch(content, "Don't log portals cast in dungeons or raids",
		PP.profile.tracking.ignoreInInstances,
		function(v) PP.profile.tracking.ignoreInInstances = v end)
	c:Place(W.ignoreInInstances, 22)

	W.trackTeleports = UI:Switch(content, "Count my own teleports as a business cost",
		PP.profile.tracking.trackTeleports,
		function(v) PP.profile.tracking.trackTeleports = v end)
	c:Place(W.trackTeleports, 22)

	UI:Tooltip(W.trackTeleports, "Teleports", {
		"Off by default. Teleport moves only you, so it can never be a sale -",
		"charging its rune against your margin makes the ledger lie.",
		" ",
		"Turn this on if you'd rather account for travel to your selling spot",
		"as a cost of doing business.",
	})

	W.freeMode = UI:Switch(content, "Free-cast mode (stop logging now)", PP.CastTracker.freeMode,
		function(v) PP.CastTracker:SetFreeMode(v) end)
	c:Place(W.freeMode, 22)

	UI:Tooltip(W.freeMode, "Free-cast mode", {
		"Temporarily stops logging every portal you cast - for when you're",
		"porting guildmates around for free.",
		" ",
		"Session only. It clears on logout, deliberately: a forgotten flag",
		"that survived a relog would quietly stop recording income.",
		" ",
		"Also on /pp free.",
	})

	local exclLabel = UI:Text(content, "Never track these destinations", T.fontSize.base, "", "textSecondary")
	c:Place(exclLabel, 18)

	W.excluded = {}
	local exCol, exRowY = 0, c.y
	for _, dest in ipairs(PP.Dest:Available()) do
		local sw = UI:Switch(content, dest.name, PP.profile.tracking.excludedDests[dest.key],
			function(v) PP.profile.tracking.excludedDests[dest.key] = v or nil end)
		sw:SetPoint("TOPLEFT", exCol * 170, exRowY)
		W.excluded[dest.key] = sw
		exCol = exCol + 1
		if exCol == 3 then
			exCol = 0
			exRowY = exRowY - 26
		end
	end
	c.y = exRowY - 30

	----------------------------------------------------------------------
	c:Section(L["Reagents"])

	W.portalCost = UI:Input(content, "20s", U.MoneyPlain(PP.profile.reagents.portalCost),
		function(value)
			local copper = U.ParseMoney(value)
			if copper then PP.profile.reagents.portalCost = copper end
		end)
	W.portalCost:SetWidth(120)
	local pcLabel = UI:Text(content, "Rune of Portals cost (per cast)", T.fontSize.base, "", "textSecondary")
	pcLabel:SetPoint("LEFT", W.portalCost, "RIGHT", T.space.sm, 0)
	c:Place(W.portalCost, T.size.inputHeight)

	W.teleportCost = UI:Input(content, "10s", U.MoneyPlain(PP.profile.reagents.teleportCost),
		function(value)
			local copper = U.ParseMoney(value)
			if copper then PP.profile.reagents.teleportCost = copper end
		end)
	W.teleportCost:SetWidth(120)
	local tcLabel = UI:Text(content, "Rune of Teleportation cost (per cast)", T.fontSize.base, "", "textSecondary")
	tcLabel:SetPoint("LEFT", W.teleportCost, "RIGHT", T.space.sm, 0)
	c:Place(W.teleportCost, T.size.inputHeight)

	W.lowWarning = UI:Slider(content, "Warn when runes drop below", 0, 20, 1, PP.profile.reagents.lowWarning,
		function(v) PP.profile.reagents.lowWarning = math.floor(v) end,
		function(v) return tostring(math.floor(v)) end)
	W.lowWarning:SetWidth(230)
	c:Place(W.lowWarning, 44)

	----------------------------------------------------------------------
	c:Section(L["Whispers"])

	local hint = UI:Text(content, "Variables: {name} {dest} {short} {city} {price} {me} {count}",
		T.fontSize.sm, "", "textMuted")
	c:Place(hint, 16)

	W.whispers = {}
	for _, spec in ipairs(PP.Whispers.TEMPLATE_ORDER) do
		local label = UI:Text(content, spec.label, T.fontSize.base, "", "textSecondary")
		c:Place(label, 16)

		local input = UI:Input(content, "", PP.profile.whispers[spec.key], function(value)
			PP.profile.whispers[spec.key] = value
		end)
		input:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, c.y)
		W.whispers[spec.key] = input
		c:Place(input, T.size.inputHeight)
	end

	local greetPointer = UI:Text(content,
		"Alternative greetings, and which ad uses which, are at the bottom of this page.",
		T.fontSize.sm, "", "textMuted")
	greetPointer:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, c.y - 6)
	c:Place(greetPointer, 22)

	----------------------------------------------------------------------
	c:Section(L["Group"])

	W.announce = UI:Switch(content, "Announce the portal to party", PP.profile.group.announceToParty,
		function(v) PP.profile.group.announceToParty = v end)
	W.autoThanks = UI:Switch(content, "Thank customers after payment", PP.profile.group.autoThanks,
		function(v) PP.profile.group.autoThanks = v end)
	c:Row(W.announce, W.autoThanks, 22)

	UI:Tooltip(W.announce, "Party announce", {
		"PARTY chat is not hardware-gated, so this one genuinely fires",
		"by itself the moment the portal lands.",
	})

	W.autoUninvite = UI:Switch(content, "Clear the group after each portal", PP.profile.group.autoUninvite,
		function(v) PP.profile.group.autoUninvite = v end)
	c:Place(W.autoUninvite, 22)

	W.uninviteDelay = UI:Slider(content, "Wait before clearing", 5, 60, 5, PP.profile.group.uninviteDelay,
		function(v) PP.profile.group.uninviteDelay = math.floor(v) end,
		function(v) return U.Duration(v) end)
	W.uninviteDelay:SetWidth(230)
	c:Place(W.uninviteDelay, 44)

	----------------------------------------------------------------------
	c:Section("Broadcasting")

	W.armEnabled = UI:Switch(content, "Enable arm mode", PP.profile.ads.armEnabled,
		function(v)
			PP.profile.ads.armEnabled = v
			if not v then PP.Broadcaster:Disarm() end
		end)
	c:Place(W.armEnabled, 22)

	local armHelp = UI:Text(content,
		"Blizzard requires a real click or keypress to post in Trade/General/Say/Yell.\n" ..
		"Arm mode stages the ad and releases it on your next keypress, so you don't have\n" ..
		"to aim for the button. Every send still happens inside a genuine input.",
		T.fontSize.sm, "", "textMuted")
	armHelp:SetWordWrap(true)
	armHelp:SetJustifyH("LEFT")
	c:Place(armHelp, 48)

	----------------------------------------------------------------------
	c:Section(L["Interface"])

	W.scale = UI:Slider(content, "Window scale", 0.6, 1.5, 0.05, PP.profile.ui.scale,
		function(v)
			PP.profile.ui.scale = U.Round(v, 2)
			UI:ApplyScale()
		end,
		function(v) return ("%d%%"):format(v * 100) end)
	W.scale:SetWidth(230)

	W.locked = UI:Switch(content, "Lock position and size", PP.profile.ui.locked,
		function(v) PP.profile.ui.locked = v end)
	c:Row(W.scale, W.locked, 44)

	W.hideMinimap = UI:Switch(content, "Hide the minimap button", PP.profile.ui.minimap.hide,
		function(v)
			PP.profile.ui.minimap.hide = v
			PP.MinimapButton:Update()
		end)
	c:Place(W.hideMinimap, 22)

	-- Font picker. Options that aren't installed are labelled as such rather than being
	-- hidden, so "why is Inter not doing anything" answers itself.
	local function fontItems()
		local items = {}
		for _, entry in ipairs(T:AvailableFonts()) do
			local label = entry.label
			if entry.key == "custom" then
				label = "Custom path..."
			elseif not entry.available then
				label = entry.label .. "  (not installed)"
			end
			items[#items + 1] = { value = entry.key, label = label }
		end
		table.insert(items, 1, { value = "auto", label = "Automatic (best available)" })
		return items
	end

	W.font = UI:Dropdown(content, fontItems(), PP.profile.ui.font or "auto", function(value)
		PP.profile.ui.font = value
		T:InvalidateFont()
		frame.__def.refresh(frame)
		PP:Print("Font changed - /reload to apply it everywhere.")
	end, 240)
	local fontLabel = UI:Text(content, "Interface font", T.fontSize.base, "", "textSecondary")
	fontLabel:SetPoint("LEFT", W.font, "RIGHT", T.space.sm, 0)
	c:Place(W.font, T.size.inputHeight)

	W.fontPath = UI:Input(content, "Interface\\AddOns\\PortalPro\\Media\\fonts\\MyFont.ttf",
		PP.profile.ui.fontPath or "", function(value)
			PP.profile.ui.fontPath = U.Trim(value)
			T:InvalidateFont()
			frame.__def.refresh(frame)
			PP:Print("Font changed - /reload to apply it everywhere.")
		end)
	W.fontPath:SetWidth(340)
	c:Place(W.fontPath, T.size.inputHeight)

	W.fontStatus = UI:Text(content, "", T.fontSize.sm, "", "textMuted")
	W.fontStatus:SetWordWrap(true)
	W.fontStatus:SetJustifyH("LEFT")
	c:Place(W.fontStatus, 30)

	----------------------------------------------------------------------
	c:Section("Profile")

	local profileInfo = UI:Text(content, "", T.fontSize.sm, "", "textMuted")
	frame.profileInfo = profileInfo
	c:Place(profileInfo, 16)

	local resetBtn = UI:Button(content, "Reset settings", function()
		PP.DB:ResetProfile()
		PP:Print("Profile reset to defaults. /reload to rebuild the interface.")
	end, "danger", 120, 24)
	local wipeBtn = UI:Button(content, "Wipe ledger", function()
		wipe(PP.global.ledger)
		wipe(PP.global.customers)
		PP:Fire("LEDGER_CHANGED")
		PP:Print("Ledger cleared.")
	end, "danger", 120, 24)
	c:Row(resetBtn, wipeBtn, 24)

	-- Version and update status live on the About tab, which is where people look for them.

	----------------------------------------------------------------------
	c:Section("Greeting library")

	-- Deliberately last on the page. This section grows and shrinks as you add greetings, and
	-- the layout cursor above is resolved once at build time - anything placed after a
	-- runtime-sized block would be overlapped by it. Nothing follows, so it can grow freely
	-- and refresh just extends the scroll height.

	local greetHint = UI:Text(content,
		"Alternatives to the greeting above, each assignable to an ad on the Ads tab. An ad "
		.. "advertising tips wants a different opening line from one quoting a fixed price, "
		.. "and the reply should follow the ad that earned it.",
		T.fontSize.sm, "", "textMuted")
	greetHint:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, c.y)
	greetHint:SetWordWrap(true)
	greetHint:SetJustifyH("LEFT")
	c:Place(greetHint, 42)

	local addGreeting = UI:Button(content, "Add greeting", function()
		local entry = PP.Whispers:AddGreeting()
		PP:Print(("Added \"%s\" - assign it to an ad on the Ads tab."):format(entry.name))
	end, "secondary", 130, 24)
	W.addGreeting = addGreeting
	c:Place(addGreeting, 24)

	-- One row per entry, built on demand. A handful of greetings never needs a virtualised
	-- list, and per-row inputs let you edit the name and the text in place.
	W.greetingRows = {}
	W.greetingHost = CreateFrame("Frame", nil, content)
	W.greetingHost:SetPoint("TOPLEFT", 0, c.y)
	W.greetingHost:SetPoint("TOPRIGHT", 0, c.y)
	W.greetingHost:SetHeight(1)

	-- What the page measures without any greeting rows. refresh() adds the rows' height.
	W.baseContentHeight = c:Height()

	content:SetHeight(W.baseContentHeight)
	frame.content = content
	return frame
end

--------------------------------------------------------------------------------
-- Greeting library rows
--------------------------------------------------------------------------------

local GREETING_ROW_H = 52

local function buildGreetingRow(host)
	local row = CreateFrame("Frame", nil, host)
	row:SetHeight(GREETING_ROW_H)

	row.nameInput = UI:Input(row, "Name", "", function(value)
		if row.entry then
			row.entry.name = (value ~= "") and value or row.entry.name
			-- The ad tab lists these by name, so a rename has to reach it.
			PP:Fire("GREETINGS_CHANGED")
		end
	end)
	row.nameInput:SetPoint("TOPLEFT")
	row.nameInput:SetWidth(140)

	row.delete = UI:Button(row, "Remove", function(self)
		if not row.entry then return end
		-- Destructive, so arm-then-confirm rather than a modal.
		if not self.armed then
			self.armed = true
			self:SetTextValue("Sure?")
			C_Timer.After(3, function()
				if self.armed then
					self.armed = false
					self:SetTextValue("Remove")
				end
			end)
			return
		end
		self.armed = false
		self:SetTextValue("Remove")
		PP.Whispers:DeleteGreeting(row.entry.id)
	end, "danger", 80, T.size.inputHeight)
	row.delete:SetPoint("TOPRIGHT")

	row.usedBy = UI:Text(row, "", T.fontSize.xs, "", "textMuted", "RIGHT")
	row.usedBy:SetPoint("RIGHT", row.delete, "LEFT", -T.space.sm, 0)
	row.usedBy:SetPoint("TOP", row.nameInput, "TOP", 0, -4)
	row.usedBy:SetWidth(160)

	row.textInput = UI:Input(row, "What to say when someone answers that ad", "",
		function(value)
			if row.entry then row.entry.text = value end
		end)
	row.textInput:SetPoint("TOPLEFT", row.nameInput, "BOTTOMLEFT", 0, -T.space.xs)
	row.textInput:SetPoint("RIGHT", row, "RIGHT")

	return row
end

--- @return number height
local function refreshGreetings(W)
	local library = PP.Whispers:Library()

	-- How many ads point at each entry, so removing one isn't a guess.
	local users = {}
	for _, ad in ipairs(PP.profile.ads.list) do
		if ad.greetingId then users[ad.greetingId] = (users[ad.greetingId] or 0) + 1 end
	end

	for i, entry in ipairs(library) do
		local row = W.greetingRows[i]
		if not row then
			row = buildGreetingRow(W.greetingHost)
			W.greetingRows[i] = row
		end

		row.entry = entry
		row:ClearAllPoints()
		row:SetPoint("TOPLEFT", 0, -(i - 1) * (GREETING_ROW_H + T.space.sm))
		row:SetPoint("TOPRIGHT", 0, -(i - 1) * (GREETING_ROW_H + T.space.sm))
		row:Show()

		if not row.nameInput.edit:HasFocus() then row.nameInput:SetValue(entry.name) end
		if not row.textInput.edit:HasFocus() then row.textInput:SetValue(entry.text) end

		local n = users[entry.id] or 0
		row.usedBy:SetText(n == 0 and "|cff6E7681not assigned|r"
			or ("|cff6E7681used by %d ad%s|r"):format(n, n == 1 and "" or "s"))

		if row.armed and row.entry ~= entry then
			row.armed = false
			row.delete:SetTextValue("Remove")
		end
	end

	for i = #library + 1, #W.greetingRows do
		W.greetingRows[i]:Hide()
	end

	local height = math.max(1, #library * (GREETING_ROW_H + T.space.sm))
	W.greetingHost:SetHeight(height)
	return height
end

local function refresh(frame)
	local W = frame.widgets
	if not W then return end

	-- The library sits at the end of the page and changes height, so the scroll extent has to
	-- follow it or the last rows can't be reached.
	local greetingsHeight = refreshGreetings(W)
	if frame.content then
		frame.content:SetHeight((W.baseContentHeight or 0) + greetingsHeight + T.space.lg)
	end

	frame.profileInfo:SetText(("Profile: %s · %d ledger entries · %d customers · %d untracked casts"):format(
		PP.profileName or "?", #PP.Ledger:All(), U.Count(PP.global.customers),
		PP.global.counters.personalCasts or 0))

	W.freeMode:SetValue(PP.CastTracker.freeMode, true)

	-- Anything reachable from somewhere other than this screen has to be re-read here.
	-- These widgets are built once, so without this they keep showing whatever the value
	-- was the first time Settings was opened — and a switch displaying the opposite of
	-- reality is worse than no switch at all.
	W.enabled:SetValue(PP.profile.detection.enabled, true)

	-- Prices can also be set with /pp price.
	if not W.defaultPrice.edit:HasFocus() then
		W.defaultPrice:SetValue(U.MoneyPlain(PP.profile.prices.default))
	end
	for key, input in pairs(W.prices) do
		if not input.edit:HasFocus() then
			local explicit = PP.profile.prices[key]
			input:SetValue(explicit and U.MoneyPlain(explicit) or "")
		end
	end

	-- Font state: the custom path field only matters for the custom option, and the
	-- status line reports what's actually in use, which may not be what was picked.
	local choice = PP.profile.ui.font or "auto"
	W.fontPath:SetShown(choice == "custom")

	local resolved = T:ResolveFont()
	local pretty = resolved:match("([^\\]+)$") or resolved

	-- Did the user's pick actually load, or did the fallback chain step in?
	local pickedPath
	if choice == "custom" then
		pickedPath = PP.profile.ui.fontPath
	else
		for _, entry in ipairs(T.fonts) do
			if entry.key == choice then
				pickedPath = entry.path
				break
			end
		end
	end

	local pickHonoured = (choice == "auto") or T:FontWorks(pickedPath)

	if pickHonoured then
		W.fontStatus:SetText("In use: " .. pretty ..
			"   ·   WoW needs a static .ttf; variable-axis and .otf files render nothing.")
		W.fontStatus:SetTextColor(T:C("textMuted"))
	else
		W.fontStatus:SetText(("That font isn't installed - see Media/fonts/README.md. Using %s instead.")
			:format(pretty))
		W.fontStatus:SetTextColor(T:C("warning"))
	end
end

UI:RegisterTab({
	key = "settings",
	label = L["Settings"],
	icon = "Interface\\ICONS\\Trade_Engineering",
	order = 6,
	build = build,
	refresh = refresh,
})
