-- ------------------------------------------------------------------------------ --
--                                TradeSkillMaster                                --
--                          https://tradeskillmaster.com                          --
--    All Rights Reserved - Detailed license information included with addon.     --
-- ------------------------------------------------------------------------------ --

local _, TSM = ...
local Auctioning = TSM.MainUI.Operations:NewPackage("Auctioning")
local L = TSM.Include("Locale").GetTable()
local String = TSM.Include("Util.String")
local Money = TSM.Include("Util.Money")
local Vararg = TSM.Include("Util.Vararg")
local CustomPrice = TSM.Include("Service.CustomPrice")
local UIElements = TSM.Include("UI.UIElements")
local private = {
	currentOperationName = nil,
	currentTab = nil,
}
local IGNORE_DURATION_OPTIONS = {
	L["None"],
	AUCTION_TIME_LEFT1.." ("..AUCTION_TIME_LEFT1_DETAIL..")",
	AUCTION_TIME_LEFT2.." ("..AUCTION_TIME_LEFT2_DETAIL..")",
	AUCTION_TIME_LEFT3.." ("..AUCTION_TIME_LEFT3_DETAIL..")",
}
local BELOW_MIN_ITEMS = { L["Don't Post Items"], L["Post at Minimum Price"], L["Post at Maximum Price"], L["Post at Normal Price"], L["Ignore Auctions Below Min"] }
local BELOW_MIN_KEYS = { "none", "minPrice", "maxPrice", "normalPrice", "ignore" }
local ABOVE_MAX_ITEMS = { L["Don't Post Items"], L["Post at Minimum Price"], L["Post at Maximum Price"], L["Post at Normal Price"] }
local ABOVE_MAX_KEYS = { "none", "minPrice", "maxPrice", "normalPrice" }
local BAD_PRICE_SOURCES = { auctioningopmin = true, auctioningopmax = true, auctioningopnormal = true }


-- ============================================================================
-- Module Functions
-- ============================================================================

function Auctioning.OnInitialize()
	TSM.MainUI.Operations.RegisterModule("Auctioning", private.GetAuctioningOperationSettings)
end



-- ============================================================================
-- Auctioning Operation Settings UI
-- ============================================================================

function private.GetAuctioningOperationSettings(operationName)
	TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning")
	private.currentOperationName = operationName
	private.currentTab = private.currentTab or L["Details"]
	return UIElements.New("TabGroup", "tabs")
		:SetMargin(0, 0, 8, 0)
		:SetNavCallback(private.GetAuctioningSettings)
		:AddPath(L["Details"])
		:AddPath(L["Posting"])
		:AddPath(L["Canceling"])
		:SetPath(private.currentTab)
end

function private.GetDetailsSettings()
	TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning", "details")
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	return UIElements.New("ScrollFrame", "settings")
		:SetPadding(8, 8, 8, 0)
		:SetBackgroundColor("PRIMARY_BG")
		:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "generalOptions", L["General Options"], L["Adjust some general settings."])
			:AddChild(UIElements.New("Frame", "content")
				:SetLayout("VERTICAL")
				:AddChildrenWithFunction(private.AddMaxStackSizeSetting)
				:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("ignoreLowDuration", L["Ignore auctions by duration"])
					:SetLayout("VERTICAL")
					:SetHeight(48)
					:SetMargin(0, 0, 0, 16)
					:AddChild(UIElements.New("SelectionDropdown", "dropdown")
						:SetHeight(24)
						:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "ignoreLowDuration"))
						:SetItems(IGNORE_DURATION_OPTIONS)
						:SetSelectedItemByKey(operation.ignoreLowDuration + 1, true)
						:SetScript("OnSelectionChanged", private.IgnoreLowDurationOnSelectionChanged)
					)
				)
				:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("blacklist", L["Blacklisted players"])
					:SetLayout("VERTICAL")
					:SetHeight(48)
					:AddChild(UIElements.New("Input", "input")
						:SetHeight(24)
						:SetBackgroundColor("ACTIVE_BG")
						:SetHintText(L["Enter player name"])
						:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "blacklist"))
						:SetScript("OnEnterPressed", private.BlacklistInputOnEnterPressed)
					)
				)
				:AddChildrenWithFunction(private.AddBlacklistPlayers)
			)
		)
		:AddChild(TSM.MainUI.Operations.GetOperationManagementElements("Auctioning", private.currentOperationName))
end

function private.AddMaxStackSizeSetting(frame)
	if TSM.IsWowClassic() then
		frame:AddChild(private.CreateToggleLine("matchStackSize", L["Match stack size"]))
	end
end

function private.GetPostingSettings()
	TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning", "posting")
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	return UIElements.New("ScrollFrame", "settings")
		:SetPadding(8, 8, 8, 0)
		:SetBackgroundColor("PRIMARY_BG")
		:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "postingSettings", L["Posting Options"], L["Adjust the settings below to set how groups attached to this operation will be auctioned."])
			:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("duration", L["Auction duration"])
				:SetLayout("VERTICAL")
				:SetHeight(48)
				:SetMargin(0, 0, 0, 12)
				:AddChild(UIElements.New("SelectionDropdown", "dropdown")
					:SetHeight(24)
					:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "duration"))
					:SetItems(TSM.CONST.AUCTION_DURATIONS)
					:SetSelectedItemByKey(operation.duration)
					:SetScript("OnSelectionChanged", private.SetAuctioningDuration)
				)

			)
			:AddChild(private.CreateInputLine("postCap", L["Post cap"], false, true))
			:AddChildrenWithFunction(private.AddStackSizeSettings)
			:AddChild(private.CreateInputLine("keepQuantity", L["Amount kept in bags"], false, true))
			:AddChild(private.CreateInputLine("maxExpires", L["Don't post after this many expires"]))
		)
		:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "priceSettings", L["Posting Price"], L["Adjust the settings below to set how groups attached to this operation will be priced."])
			:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("bidPercent", L["Set bid as percentage of buyout"])
				:SetLayout("VERTICAL")
				:SetHeight(48)
				:SetMargin(0, 0, 0, 12)
				:AddChild(UIElements.New("Frame", "content")
					:SetLayout("HORIZONTAL")
					:SetHeight(24)
					:AddChild(UIElements.New("Input", "input")
						:SetMargin(0, 8, 0, 0)
						:SetBackgroundColor("ACTIVE_BG")
						:SetValidateFunc(private.BidPercentValidateFunc)
						:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "bidPercent"))
						:SetValue((operation.bidPercent * 100).."%")
						:SetScript("OnValueChanged", private.BidPercentOnValueChanged)
					)
					:AddChild(UIElements.New("Text", "label")
						:SetWidth("AUTO")
						:SetFont("BODY_BODY3")
						:SetFormattedText(L["Enter a value from %d - %d%%"], 0, 100)
						:SetTextColor(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "bidPercent") and "TEXT_DISABLED" or "TEXT")
					)
				)
			)
			:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("undercut", L["Undercut amount"], 66, private.CheckUndercut)
				:SetMargin(0, 0, 0, 12)
			)
			:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("minPrice", L["Minimum price"], 126, BAD_PRICE_SOURCES))
			:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("priceReset", L["When below minimum:"])
				:SetMargin(0, 0, 12, 12)
				:AddChild(UIElements.New("SelectionDropdown", "dropdown")
					:SetWidth(240)
					:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "priceReset"))
					:SetItems(BELOW_MIN_ITEMS, BELOW_MIN_KEYS)
					:SetSettingInfo(operation, "priceReset")
				)
			)
			:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("maxPrice", L["Maximum price"], 126, BAD_PRICE_SOURCES))
			:AddChild(TSM.MainUI.Operations.CreateLinkedSettingLine("aboveMax", L["When above maximum:"])
				:SetMargin(0, 0, 12, 12)
				:AddChild(UIElements.New("SelectionDropdown", "dropdown")
					:SetWidth(240)
					:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, "aboveMax"))
					:SetItems(ABOVE_MAX_ITEMS, ABOVE_MAX_KEYS)
					:SetSettingInfo(operation, "aboveMax")
				)
			)
			:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("normalPrice", L["Normal price"], 126, BAD_PRICE_SOURCES))
		)
end

function private.AddStackSizeSettings(frame)
	if not TSM.IsWowClassic() then
		return
	end
	frame:AddChild(private.CreateInputLine("stackSize", L["Stack size"], false, true))
	frame:AddChild(private.CreateToggleLine("stackSizeIsCap", L["Allow partial stack"]))
end

function private.GetCancelingSettings()
	TSM.UI.AnalyticsRecordPathChange("main", "operations", "auctioning", "canceling")
	return UIElements.New("ScrollFrame", "settings")
		:SetPadding(8, 8, 8, 0)
		:AddChild(TSM.MainUI.Operations.CreateExpandableSection("Auctioning", "priceSettings", L["Canceling Options"], L["Adjust the settings below to set how groups attached to this operation will be cancelled."])
			:AddChild(private.CreateToggleLine("cancelUndercut", L["Cancel undercut auctions"]))
			:AddChild(private.CreateToggleLine("cancelRepost", L["Cancel to repost higher"]))
			:AddChild(TSM.MainUI.Operations.CreateLinkedPriceInput("cancelRepostThreshold", L["Repost threshold"], 66))
		)
end

function private.GetAuctioningSettings(self, button)
	private.currentTab = button
	if button == L["Details"] then
		return private.GetDetailsSettings()
	elseif button == L["Posting"] then
		return private.GetPostingSettings()
	elseif button == L["Canceling"] then
		return private.GetCancelingSettings()
	else
		error("Unknown button!")
	end
end

function private.AddBlacklistPlayers(frame)
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	if operation.blacklist == "" then return end
	local containerFrame = UIElements.New("Frame", "blacklistFrame")
		:SetLayout("FLOW")
	for index, player in Vararg.Iterator(strsplit(",", operation.blacklist)) do
		containerFrame:AddChild(UIElements.New("Frame", "blacklist" .. index)
			:SetLayout("HORIZONTAL")
			:SetSize(100, 20)
			:SetMargin(0, 12, 0, 0)
			:AddChild(UIElements.New("Text", "text")
				:SetWidth("AUTO")
				:SetMargin(0, 2, 0, 0)
				:SetFont("BODY_BODY2_MEDIUM")
				:SetText(player)
			)
			:AddChild(UIElements.New("Button", "removeBtn")
				:SetBackgroundAndSize("iconPack.14x14/Close/Circle")
				:SetContext(player)
				:SetScript("OnClick", private.RemoveBlacklistOnClick)
			)
			:AddChild(UIElements.New("Spacer", "spacer"))
		)
	end
	frame:AddChild(containerFrame)
end

function private.CreateInputLine(key, label, disabled, margin)
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	local hasRelationship = TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, key)
	return TSM.MainUI.Operations.CreateLinkedSettingLine(key, label, disabled)
		:SetLayout("VERTICAL")
		:SetHeight(48)
		:SetMargin(0, 0, 0, margin and 12 or 4)
		:AddChild(UIElements.New("Frame", "content")
			:SetLayout("HORIZONTAL")
			:SetHeight(24)
			:AddChild(UIElements.New("Input", "input")
				:SetMargin(0, 8, 0, 0)
				:SetBackgroundColor("ACTIVE_BG")
				:SetContext(key)
				:SetDisabled(hasRelationship or disabled)
				:SetValidateFunc("CUSTOM_PRICE")
				:SetSettingInfo(operation, key)
			)
			:AddChild(UIElements.New("Text", "label")
				:SetWidth("AUTO")
				:SetFont("BODY_BODY3")
				:SetTextColor((hasRelationship or disabled) and "TEXT_DISABLED" or "TEXT")
				:SetFormattedText(L["Supported value range: %d - %d"], TSM.Operations.Auctioning.GetMinMaxValues(key))
			)
		)
end

function private.CreateToggleLine(key, label)
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	return TSM.MainUI.Operations.CreateLinkedSettingLine(key, label)
		:SetLayout("VERTICAL")
		:SetHeight(48)
		:SetMargin(0, 0, 0, 12)
		:AddChild(UIElements.New("ToggleOnOff", "toggle")
			:SetHeight(18)
			:SetDisabled(TSM.Operations.HasRelationship("Auctioning", private.currentOperationName, key))
			:SetSettingInfo(operation, key)
		)
end

function private.CheckUndercut(_, value)
	if not TSM.IsWowClassic() and Money.FromString(Money.ToString(value) or value) == 0 then
		return true
	elseif not TSM.IsWowClassic() and (Money.FromString(Money.ToString(value) or value) or math.huge) < COPPER_PER_SILVER then
		return false, L["Invalid undercut. To post below the cheapest auction without a significant undercut, set your undercut to 0c."]
	else
		local isValid, err = CustomPrice.Validate(value)
		if isValid then
			return true
		end
		return false, L["Invalid custom price."].." "..err
	end
end



-- ============================================================================
-- Local Script Handlers
-- ============================================================================

function private.IgnoreLowDurationOnSelectionChanged(self)
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	operation.ignoreLowDuration = self:GetSelectedItemKey() - 1
end

function private.BlacklistInputOnEnterPressed(input)
	local newPlayer = input:GetValue()
	if newPlayer == "" or strfind(newPlayer, ",") or newPlayer ~= String.Escape(newPlayer) then
		-- this is an invalid player name
		return
	end
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	local found = false
	for _, player in Vararg.Iterator(strsplit(",", operation.blacklist)) do
		if newPlayer == player then
			-- this player is already added
			input:SetValue("")
			found = true
		end
	end
	if found then
		return
	end
	operation.blacklist = (operation.blacklist == "") and newPlayer or (operation.blacklist..","..newPlayer)
	input:GetElement("__parent.__parent.__parent.__parent.__parent.__parent"):ReloadContent()
end

function private.RemoveBlacklistOnClick(button)
	local player = button:GetContext()
	-- FIXME: This sort of logic should go within some Auctioning-specific operation setting wrapper code
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	if operation.blacklist == player then
		operation.blacklist = ""
	else
		-- handle cases where this entry is at the start, in the middle, and at the end
		operation.blacklist = gsub(operation.blacklist, "^"..player..",", "")
		operation.blacklist = gsub(operation.blacklist, ","..player..",", ",")
		operation.blacklist = gsub(operation.blacklist, ","..player.."$", "")
	end
	button:GetElement("__parent.__parent.__parent.__parent.__parent.__parent.__parent"):ReloadContent()
end

function private.SetAuctioningDuration(dropdown)
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	operation.duration = dropdown:GetSelectedItemKey()
end

function private.BidPercentValidateFunc(_, value)
	value = strmatch(value, "^([0-9]+) *%%?$")
	value = tonumber(value)
	if not value or value < 0 or value > 100 then
		return false, L["Bid percent must be between 0 and 100."]
	end
	return true
end

function private.BidPercentOnValueChanged(input)
	local operation = TSM.Operations.GetSettings("Auctioning", private.currentOperationName)
	local value = strmatch(input:GetValue(), "^([0-9]+) *%%?$")
	value = tonumber(value) / 100
	operation.bidPercent = value
end
