-- Portal Pro :: Modules/MoneyWatch.lua
-- Single arbiter for PLAYER_MONEY.
--
-- PLAYER_MONEY fires for vendor sales, loot, repairs, mail and trades alike — the event
-- itself says nothing about *why* your gold changed. So callers register an expectation
-- ("a trade with Bob is completing, the next money change within 5s is his") and the first
-- matching expectation claims the delta. Anything unclaimed is broadcast as ambient.
--
-- Routing every attribution through one place is what stops TradeWatch and MailWatch from
-- both booking the same 10g.

local ADDON_NAME, PP = ...
local U = PP.U

local MW = PP:NewModule("MoneyWatch")
PP.MoneyWatch = MW

local expectations = {}
local lastMoney = 0

function MW:OnEnable()
	lastMoney = GetMoney() or 0
	PP:RegisterEvent("PLAYER_MONEY", function()
		MW:OnMoneyChanged()
	end)
end

--- Claim the next money change.
--- @param source string "trade" | "mail" | ...
--- @param partner string|nil counterparty name
--- @param timeout number seconds the claim stays valid
--- @param handler fun(delta:number, partner:string|nil, source:string)
--- @return table claim the caller may mutate `partner` and `expires` while it's live
function MW:Expect(source, partner, timeout, handler)
	local claim = {
		source  = source,
		partner = partner,
		expires = GetTime() + (timeout or 5),
		handler = handler,
	}
	expectations[#expectations + 1] = claim
	PP:Debug("money expectation registered:", source, partner or "?")
	return claim
end

--- Drop any outstanding claim from a source (e.g. the trade was cancelled).
function MW:Cancel(source)
	for i = #expectations, 1, -1 do
		if expectations[i].source == source then
			table.remove(expectations, i)
		end
	end
end

local function prune()
	local now = GetTime()
	for i = #expectations, 1, -1 do
		if expectations[i].expires < now then
			PP:Debug("money expectation expired:", expectations[i].source)
			table.remove(expectations, i)
		end
	end
end

function MW:OnMoneyChanged()
	local current = GetMoney() or 0
	local delta = current - lastMoney
	lastMoney = current

	if delta == 0 then return end
	prune()

	local claim = table.remove(expectations, 1)
	if claim then
		local ok, err = pcall(claim.handler, delta, claim.partner, claim.source)
		if not ok then
			PP:Error("money handler failed: " .. tostring(err))
		end
		return
	end

	PP:Fire("MONEY_AMBIENT", delta, current)
end

function MW:Current()
	return lastMoney
end
