-- Portal Pro :: Core/Init.lua
-- Namespace, module lifecycle, internal event bus.
--
-- Modules never call each other directly. They publish on the bus and subscribe to what
-- they care about, so TradeWatch has no idea the Ledger exists.

local ADDON_NAME, PP = ...

PP.name = ADDON_NAME
PP.modules = {}
PP.moduleOrder = {}

local PREFIX = "|cff4C9AFFPortal Pro|r "

function PP:Print(...)
	print(PREFIX .. strjoin(" ", tostringall(...)))
end

function PP:Warn(...)
	print(PREFIX .. "|cffFBBF24" .. strjoin(" ", tostringall(...)) .. "|r")
end

function PP:Error(...)
	print(PREFIX .. "|cffF87171" .. strjoin(" ", tostringall(...)) .. "|r")
end

function PP:Debug(...)
	if PP.db and PP.db.global and PP.db.global.debug then
		print(PREFIX .. "|cff6B7280[dbg]|r " .. strjoin(" ", tostringall(...)))
	end
end

--------------------------------------------------------------------------------
-- Internal event bus
--------------------------------------------------------------------------------

local listeners = {}

--- Subscribe to an internal message. Returns the handler so it can be unsubscribed.
function PP:On(msg, fn)
	local bucket = listeners[msg]
	if not bucket then
		bucket = {}
		listeners[msg] = bucket
	end
	bucket[#bucket + 1] = fn
	return fn
end

function PP:Off(msg, fn)
	local bucket = listeners[msg]
	if not bucket then return end
	for i = #bucket, 1, -1 do
		if bucket[i] == fn then
			table.remove(bucket, i)
		end
	end
end

--- Publish an internal message. A throwing subscriber never takes down the publisher.
function PP:Fire(msg, ...)
	local bucket = listeners[msg]
	if not bucket then return end
	for i = 1, #bucket do
		local ok, err = pcall(bucket[i], ...)
		if not ok then
			PP:Error(("handler for %s failed: %s"):format(tostring(msg), tostring(err)))
		end
	end
end

--------------------------------------------------------------------------------
-- Modules
--------------------------------------------------------------------------------

--- Create a module. Load order in the TOC determines OnInitialize/OnEnable order.
function PP:NewModule(name)
	local m = PP.modules[name]
	if m then return m end
	m = { moduleName = name }
	PP.modules[name] = m
	PP.moduleOrder[#PP.moduleOrder + 1] = m
	return m
end

function PP:GetModule(name)
	return PP.modules[name]
end

--------------------------------------------------------------------------------
-- Bootstrap
--------------------------------------------------------------------------------

local function runLifecycle(phase)
	for i = 1, #PP.moduleOrder do
		local m = PP.moduleOrder[i]
		local fn = m[phase]
		if fn then
			local ok, err = pcall(fn, m)
			if not ok then
				PP:Error(("%s:%s() failed: %s"):format(m.moduleName, phase, tostring(err)))
			end
		end
	end
end

local boot = CreateFrame("Frame")
boot:RegisterEvent("ADDON_LOADED")
boot:RegisterEvent("PLAYER_LOGIN")
boot:SetScript("OnEvent", function(self, event, arg1)
	if event == "ADDON_LOADED" then
		if arg1 ~= ADDON_NAME then return end
		PP.DB:Initialize()
		runLifecycle("OnInitialize")
		PP.initialized = true
	elseif event == "PLAYER_LOGIN" then
		PP.playerName = UnitName("player")
		PP.playerClass = select(2, UnitClass("player"))
		PP.playerRealm = GetRealmName()
		runLifecycle("OnEnable")
		PP.ready = true
		PP:Fire("READY")

		if PP.db.global.firstRun then
			PP.db.global.firstRun = false
			PP:Print("First run - type |cff4C9AFF/pp|r to open, |cff4C9AFF/pp help|r for commands.")
			if PP.playerClass ~= "MAGE" then
				PP:Warn("You're not a mage. Portal Pro will run, but most of it won't be much use.")
			end
		end
	end
end)
