local _, ns = ...
local SlickUI = ns.SlickUI

local VendorAutomation = {
	Selling = false,
}
SlickUI.VendorAutomation = VendorAutomation

VendorAutomation.RepairFundLabels = {
	personal = "Personal Funds",
	guild = "Guild Funds Only",
	guildFirst = "Guild First, Then Personal",
	personalFirst = "Personal First, Then Guild",
}

local function SafeNumber(value)
	if canaccessvalue and not canaccessvalue(value) then return nil end
	if issecretvalue and issecretvalue(value) then return nil end
	return type(value) == "number" and value or nil
end

local function SafeBoolean(value)
	if canaccessvalue and not canaccessvalue(value) then return false end
	if issecretvalue and issecretvalue(value) then return false end
	return value == true
end

local function Settings()
	return SlickUI.DB.vendorAutomation
end

local function MoneyText(value)
	return GetCoinTextureString(math.max(0, math.floor(SafeNumber(value) or 0)))
end

local function MerchantIsOpen()
	return MerchantFrame and MerchantFrame:IsShown() == true
end

local function PoorQuality()
	return Enum and Enum.ItemQuality and Enum.ItemQuality.Poor or 0
end

local function GetSellPrice(itemID)
	if not C_Item or not C_Item.GetItemInfo then return nil end
	return SafeNumber(select(11, C_Item.GetItemInfo(itemID)))
end

function VendorAutomation:BuildGreyQueue()
	local queue = {}
	local lastBag = NUM_TOTAL_EQUIPPED_BAG_SLOTS or NUM_BAG_SLOTS or 4
	local poorQuality = PoorQuality()

	for bag = 0, lastBag do
		local slots = SafeNumber(C_Container.GetContainerNumSlots(bag)) or 0
		for slot = 1, slots do
			local info = C_Container.GetContainerItemInfo(bag, slot)
			if info and not info.isLocked and info.quality == poorQuality and not info.hasNoValue and info.itemID then
				local count = SafeNumber(info.stackCount) or 1
				local sellPrice = GetSellPrice(info.itemID)
				if sellPrice and sellPrice > 0 then
					queue[#queue + 1] = {
						bag = bag,
						slot = slot,
						itemID = info.itemID,
						link = info.hyperlink or (C_Item.GetItemLinkByID and C_Item.GetItemLinkByID(info.itemID)) or ("Item " .. info.itemID),
						count = count,
						value = sellPrice * count,
					}
				end
			end
		end
	end
	return queue
end

function VendorAutomation:ReportSales(sales, totalCount, totalValue)
	if totalCount <= 0 then return end
	for _, sale in ipairs(sales) do
		SlickUI:Print("Sold " .. sale.link .. " x" .. sale.count .. " for " .. MoneyText(sale.value) .. ".")
	end
	SlickUI:Print("Grey-item sales: " .. totalCount .. " item" .. (totalCount == 1 and "" or "s") .. " sold for " .. MoneyText(totalValue) .. " total.")
end

function VendorAutomation:SellGreys(callback)
	if self.Selling then return end
	local queue = self:BuildGreyQueue()
	if #queue == 0 then
		if callback then callback() end
		return
	end

	self.Selling = true
	local index, totalCount, totalValue = 1, 0, 0
	local sales = {}
	local function Finish(interrupted)
		VendorAutomation.Selling = false
		VendorAutomation:ReportSales(sales, totalCount, totalValue)
		if interrupted then SlickUI:Print("Grey-item selling stopped because the merchant window closed.") end
		if callback then C_Timer.After(0.20, callback) end
	end
	local function SellNext()
		if not MerchantIsOpen() then Finish(true) return end
		local sale = queue[index]
		if not sale then Finish(false) return end
		local info = C_Container.GetContainerItemInfo(sale.bag, sale.slot)
		if info and info.itemID == sale.itemID and info.quality == PoorQuality() then
			local succeeded = pcall(C_Container.UseContainerItem, sale.bag, sale.slot)
			if succeeded then
				totalCount = totalCount + sale.count
				totalValue = totalValue + sale.value
				sales[#sales + 1] = sale
			end
		end
		index = index + 1
		C_Timer.After(0.08, SellNext)
	end
	SellNext()
end

function VendorAutomation:CanUseGuildFunds(cost)
	if type(CanGuildBankRepair) ~= "function" or not SafeBoolean(CanGuildBankRepair()) then return false end
	if type(GetGuildBankWithdrawMoney) ~= "function" then return false end
	local allowance = SafeNumber(GetGuildBankWithdrawMoney())
	return allowance == -1 or (allowance and allowance >= cost) or false
end

function VendorAutomation:CanUsePersonalFunds(cost)
	local money = SafeNumber(GetMoney())
	return money and money >= cost or false
end

function VendorAutomation:Repair()
	if not Settings().autoRepair or not MerchantIsOpen() then return end
	if type(CanMerchantRepair) ~= "function" or not SafeBoolean(CanMerchantRepair()) then return end
	local cost = SafeNumber(GetRepairAllCost())
	if not cost or cost <= 0 then return end

	local mode = Settings().repairFunds
	local guildAvailable = self:CanUseGuildFunds(cost)
	local personalAvailable = self:CanUsePersonalFunds(cost)
	local useGuild
	if mode == "guild" then
		useGuild = guildAvailable and true or nil
	elseif mode == "guildFirst" then
		useGuild = guildAvailable and true or (personalAvailable and false or nil)
	elseif mode == "personalFirst" then
		useGuild = personalAvailable and false or (guildAvailable and true or nil)
	else
		useGuild = personalAvailable and false or nil
	end

	if useGuild == nil then
		if mode == "guild" then
			SlickUI:Print("Automatic repair skipped: guild repair funds or permission are unavailable. Repair cost: " .. MoneyText(cost) .. ".")
		else
			SlickUI:Print("Automatic repair skipped: the selected funding sources cannot cover " .. MoneyText(cost) .. ".")
		end
		return
	end

	if useGuild then
		RepairAllItems(true)
		-- The guild API exposes the player's withdrawal allowance, but that value
		-- can exceed the guild bank's actual available repair funds. Verify the
		-- result after Blizzard processes the guild attempt and use personal money
		-- for any remaining bill when the selected mode permits that fallback.
		C_Timer.After(0.20, function()
			if not MerchantIsOpen() then return end
			local remaining = SafeNumber(GetRepairAllCost())
			if not remaining or remaining <= 0 then
				SlickUI:Print("Repaired all items for " .. MoneyText(cost) .. " using guild funds.")
				return
			end
			if mode == "guildFirst" and VendorAutomation:CanUsePersonalFunds(remaining) then
				RepairAllItems(false)
				SlickUI:Print("Guild funds could not cover the full repair. Paid the remaining " .. MoneyText(remaining) .. " from personal funds.")
			elseif mode == "guildFirst" then
				SlickUI:Print("Guild funds could not cover the full repair, and personal funds cannot cover the remaining " .. MoneyText(remaining) .. ".")
			else
				SlickUI:Print("Guild funds could not cover the full repair. Remaining cost: " .. MoneyText(remaining) .. ".")
			end
		end)
	else
		RepairAllItems(false)
		SlickUI:Print("Repaired all items for " .. MoneyText(cost) .. " using personal funds.")
	end
end

function VendorAutomation:ProcessMerchant()
	if Settings().sellGreys then
		self:SellGreys(function() VendorAutomation:Repair() end)
	else
		self:Repair()
	end
end

function VendorAutomation:Initialize()
	local frame = CreateFrame("Frame")
	frame:RegisterEvent("MERCHANT_SHOW")
	frame:SetScript("OnEvent", function()
		C_Timer.After(0.10, function() VendorAutomation:ProcessMerchant() end)
	end)
	self.EventFrame = frame
end

SlickUI:RegisterModule("VendorAutomation", VendorAutomation)
