local _, GF = ...

-- Editor for one GF list-column definition. The model remains ListColumns;
-- this controller owns only validation, the satellite view, and repainting the
-- header that opened it.
local ColumnSizingDialog = {}
GF.ColumnSizingDialog = ColumnSizingDialog
GF.ColumnLayoutDialog = ColumnSizingDialog -- saved callers from earlier GF builds

local DIMENSIONS = {
	frameWidth = 320,
	frameHeight = 220,
	fieldWidth = 120,
	fieldHeight = 22,
}

local unpackArgs = unpack or table.unpack

local function model()
	return GF.ListColumns
end

local function localeText(key, fallback)
	local locale = GF.L or {}
	return locale[key] or fallback
end

local function trim(value)
	if type(value) ~= "string" then
		return ""
	end
	return (value:gsub("^%s+", ""):gsub("%s+$", ""))
end

local function writeProgrammaticText(editBox, value)
	editBox._gfProgrammaticColumnText = true
	editBox:SetText(value or "")
	editBox._gfProgrammaticColumnText = nil
end

local function retainDecimalInput(value)
	local filtered = {}
	local decimalSeen = false
	for character in tostring(value or ""):gmatch(".") do
		if character:match("%d") then
			filtered[#filtered + 1] = character
		elseif character == "." and not decimalSeen then
			filtered[#filtered + 1] = character
			decimalSeen = true
		end
	end
	return table.concat(filtered)
end

local FIELD_RULES = {
	minimum = {
		parse = function(value)
			local owner = model()
			return owner and owner:ParseMinWidthText(value) or nil
		end,
		format = function(value)
			local owner = model()
			return owner and owner:FormatMinWidth(value) or tostring(value or "")
		end,
		numeric = true,
	},
	weight = {
		parse = function(value)
			local owner = model()
			return owner and owner:ParseWeightText(value) or nil
		end,
		format = function(value)
			local owner = model()
			return owner and owner:FormatWeight(value) or tostring(value or "")
		end,
		filter = retainDecimalInput,
	},
}

local function canonicalizeField(editBox, rule)
	local parsed = rule.parse(trim(editBox:GetText()))
	if parsed == nil then
		writeProgrammaticText(editBox, editBox._gfLastValidColumnText or "")
		return nil
	end
	local canonical = rule.format(parsed)
	editBox._gfLastValidColumnText = canonical
	writeProgrammaticText(editBox, canonical)
	return parsed
end

local function installFieldBehavior(editBox, rule)
	if rule.numeric then
		editBox:SetNumeric(true)
	end
	if rule.filter then
		editBox:SetScript("OnTextChanged", function(field)
			if field._gfProgrammaticColumnText then
				return
			end
			local current = field:GetText() or ""
			local filtered = rule.filter(current)
			if current ~= filtered then
				writeProgrammaticText(field, filtered)
			end
		end)
	end
	editBox:SetScript("OnEditFocusLost", function(field)
		canonicalizeField(field, rule)
	end)
	editBox:SetScript("OnEnterPressed", function(field)
		field:ClearFocus()
	end)
end

local function createField(parent, label, rule)
	local editBox = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
	editBox:SetSize(DIMENSIONS.fieldWidth, DIMENSIONS.fieldHeight)
	editBox:SetPoint("TOPLEFT", label, "BOTTOMLEFT", 8, -4)
	editBox:SetAutoFocus(false)
	if GF.UI.TrackEditBox then
		GF.UI.TrackEditBox(editBox, "GameFontHighlightSmall")
	end
	installFieldBehavior(editBox, rule)
	GF.UI.StyleFilterNumberBox(editBox, {
		width = DIMENSIONS.fieldWidth,
		height = DIMENSIONS.fieldHeight,
	})
	return editBox
end

local function createActionButton(frame, label, anchor, handler)
	local button = GF.UI.CreatePanelButton(
		frame,
		label,
		GF.PANEL_BUTTON_TWO_CHAR_W
	)
	if anchor then
		button:SetPoint(unpackArgs(anchor))
	end
	button:SetScript("OnClick", handler)
	return button
end

local function ensureView(controller)
	if controller.frame then
		return controller.frame
	end
	local frame = GF.UI.CreateSatelliteSettingsFrame({
		name = "GroupFinderAddonColumnLayoutDialog",
		width = DIMENSIONS.frameWidth,
		height = DIMENSIONS.frameHeight,
		title = localeText("COL_LAYOUT_DIALOG_TITLE", "Column layout"),
		levelOffset = 5,
		onClose = function()
			controller:Dismiss()
		end,
	})

	local form = CreateFrame("Frame", nil, frame)
	form:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -36)
	form:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, 52)
	frame.form = form

	frame.helpIcon = GF.UI.CreateHelpIcon(
		form,
		localeText("COL_LAYOUT_HELP_HINT", ""),
		40
	)
	frame.helpIcon:SetPoint("TOPRIGHT", form, "TOPRIGHT", 5, 5)
	frame.helpIcon:SetFrameLevel(form:GetFrameLevel() + 10)

	frame.minLabel = GF.UI.CreateFontString(form, "OVERLAY", "GameFontNormal")
	frame.minLabel:SetPoint("TOPLEFT", form, "TOPLEFT")
	frame.minLabel:SetJustifyH("LEFT")
	frame.minInput = createField(form, frame.minLabel, FIELD_RULES.minimum)

	frame.weightLabel = GF.UI.CreateFontString(form, "OVERLAY", "GameFontNormal")
	frame.weightLabel:SetPoint("TOPLEFT", frame.minInput, "BOTTOMLEFT", -8, -14)
	frame.weightLabel:SetJustifyH("LEFT")
	frame.weightInput = createField(form, frame.weightLabel, FIELD_RULES.weight)

	frame.confirmButton = createActionButton(
		frame,
		localeText("COL_LAYOUT_OK", "OK"),
		{ "BOTTOMRIGHT", frame, "BOTTOMRIGHT", -112, 14 },
		function()
			controller:Commit()
		end
	)
	frame.resetButton = createActionButton(
		frame,
		localeText("COL_LAYOUT_RESET", "Reset"),
		{ "RIGHT", frame.confirmButton, "LEFT", -8, 0 },
		function()
			controller:RestoreDefault()
		end
	)
	frame.cancelButton = createActionButton(
		frame,
		localeText("COL_LAYOUT_CANCEL", "Cancel"),
		{ "LEFT", frame.confirmButton, "RIGHT", 8, 0 },
		function()
			controller:Dismiss()
		end
	)

	controller.frame = frame
	return frame
end

local function localizeView(frame)
	frame.minLabel:SetText(localeText("COL_LAYOUT_MIN_WIDTH", "Min width"))
	frame.weightLabel:SetText(localeText("COL_LAYOUT_WEIGHT", "Weight"))
	frame.confirmButton:SetText(localeText("COL_LAYOUT_OK", "OK"))
	frame.resetButton:SetText(localeText("COL_LAYOUT_RESET", "Reset"))
	frame.cancelButton:SetText(localeText("COL_LAYOUT_CANCEL", "Cancel"))
	frame.helpIcon._gfTooltip = localeText("COL_LAYOUT_HELP_HINT", "")
end

local function putFieldValue(editBox, rule, value)
	local text = rule.format(value)
	editBox._gfLastValidColumnText = text
	writeProgrammaticText(editBox, text)
end

function ColumnSizingDialog:Dismiss()
	if self.frame then
		self.frame:Hide()
	end
	self.session = nil
end

function ColumnSizingDialog:ShowFor(profile, columnID, headerBar)
	local owner = model()
	local definition = owner and owner:GetColumnDef(profile, columnID)
	if not definition then
		return false
	end
	local frame = ensureView(self)
	localizeView(frame)
	self.session = {
		profile = profile,
		columnID = columnID,
		headerBar = headerBar,
	}
	putFieldValue(frame.minInput, FIELD_RULES.minimum, definition.minWidth)
	putFieldValue(frame.weightInput, FIELD_RULES.weight, definition.weight)
	GF.UI.PresentSatelliteFrame(frame, {
		title = localeText("COL_LAYOUT_DIALOG_TITLE", "Column layout"),
		onShown = function()
			frame.minInput:SetFocus()
			frame.minInput:HighlightText()
		end,
	})
	return true
end

function ColumnSizingDialog:RepaintOwner(session)
	local bar = session and session.headerBar
	if not bar then
		return
	end
	local headerView = GF.ColumnHeaderBar
	if headerView and type(headerView.Layout) == "function" then
		headerView:Layout(bar)
	end
	if type(bar._onLayoutChange) == "function" then
		bar._onLayoutChange()
	end
end

function ColumnSizingDialog:RestoreDefault()
	local session = self.session
	local owner = model()
	if not (session and owner) then
		return false
	end
	owner:ResetColumnLayout(session.profile, session.columnID)
	self:RepaintOwner(session)
	self:Dismiss()
	return true
end

function ColumnSizingDialog:Commit()
	local session = self.session
	local owner = model()
	local frame = self.frame
	if not (session and owner and frame) then
		return false
	end
	local minWidth = FIELD_RULES.minimum.parse(trim(frame.minInput:GetText()))
	local weight = FIELD_RULES.weight.parse(trim(frame.weightInput:GetText()))
	if minWidth == nil or weight == nil
		or not owner:SetColumnLayout(
			session.profile,
			session.columnID,
			minWidth,
			weight
		)
	then
		if GF.ShowWarningMessage then
			GF.ShowWarningMessage(localeText(
				"COL_LAYOUT_INVALID",
				"Invalid column layout values."
			))
		end
		return false
	end
	self:RepaintOwner(session)
	self:Dismiss()
	return true
end

-- Compatibility aliases are intentionally thin; GF code uses the domain names
-- above while older saved callbacks may still invoke these methods.
ColumnSizingDialog.Hide = ColumnSizingDialog.Dismiss
ColumnSizingDialog.Open = ColumnSizingDialog.ShowFor
ColumnSizingDialog.ApplyChange = ColumnSizingDialog.RepaintOwner
ColumnSizingDialog.OnReset = ColumnSizingDialog.RestoreDefault
ColumnSizingDialog.OnConfirm = ColumnSizingDialog.Commit
