local addonName, addonTable = ...
addonTable = addonTable or {}

---@class DetentionRolls : AceAddon-3.0, AceConsole-3.0, AceEvent-3.0, AceTimer-3.0
local DR = LibStub("AceAddon-3.0"):NewAddon(addonTable, addonName, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0")
_G.DetentionRolls = DR

DR.Modules = DR.Modules or {}
DR.Runtime = DR.Runtime or {
	versions = {},
	initComplete = false,
}

local SERVICE_PATHS = {
	"Services.Identity",
	"Services.Audit",
	"Services.SessionState",
	"Services.Protocol",
	"Services.Comms",
	"Services.DeferredTrade",
	"Services.Ledger",
	"Services.SessionManager",
}

local MODULE_PATHS = {
	"Modules.VersionCheck",
	"Modules.RaidActivation",
	"Modules.RaidLeaderFrame",
	"Modules.RaiderFrame",
	"Modules.RosterFrame",
	"Modules.ProxyRoll",
	"Modules.RollOffModal",
	"Modules.DeferredTradeFrame",
	"Modules.TradeWatcher",
	"Modules.LootWatcher",
	"Modules.GroupLoot",
	"Modules.Toolbar",
	"Modules.TestMode",
}

function DR:RegisterModule(path, module)
	if type(path) ~= "string" or path == "" then
		error("DetentionRolls:RegisterModule requires a non-empty string path")
	end
	self.Modules[path] = module
	return module
end

function DR:Require(path)
	local module = self.Modules[path]
	if not module then
		error("DetentionRolls missing module: " .. tostring(path))
	end
	return module
end

function DR:CallLifecycle(pathList, methodName)
	for _, path in ipairs(pathList) do
		local module = self.Modules[path]
		if module and type(module[methodName]) == "function" then
			local ok, err = pcall(module[methodName], module)
			if not ok and self.logService then
				self.logService:Get("Core"):e("Lifecycle error", path, methodName, err)
			end
		end
	end
end

function DR:OnProfileUpdated()
	if self.logService then
		self.logService:Get("Core"):i("Profile updated")
	end
end

function DR:OnInitialize()
	self.version = (C_AddOns and C_AddOns.GetAddOnMetadata and C_AddOns.GetAddOnMetadata(addonName, "Version")) or "dev"

	self.constants = self:Require("Core.Constants")
	self.defaults = self:Require("Core.Defaults")
	self.db = LibStub("AceDB-3.0"):New("DetentionRollsDB", self.defaults, true)

	self.logService = self:Require("Services.Log")
	self.logService:SetDb(self.db)
	self.Log = self.logService:Get("Core")
	self.Log:i("OnInitialize start", self.version)

	self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileUpdated")
	self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileUpdated")
	self.db.RegisterCallback(self, "OnProfileReset", "OnProfileUpdated")

	self:Require("Core.Compat"):Run(self)
	self:CallLifecycle(SERVICE_PATHS, "OnInitialize")
	self:CallLifecycle(MODULE_PATHS, "OnInitialize")

	local config = self:Require("Modules.Config")
	if type(config.RegisterOptions) == "function" then
		config:RegisterOptions()
	end

	self:RegisterChatCommand("droll", "ChatCommand")
	self:RegisterChatCommand("detentionrolls", "ChatCommand")

	self.Runtime.initComplete = true
	self.Log:i("OnInitialize complete")
end

function DR:OnEnable()
	self.Log:i("OnEnable start")
	self:CallLifecycle(SERVICE_PATHS, "OnEnable")
	self:CallLifecycle(MODULE_PATHS, "OnEnable")
	self.Log:i("OnEnable complete")
end

function DR:OnDisable()
	self.Log:i("OnDisable start")
	self:CallLifecycle(MODULE_PATHS, "OnDisable")
	self:CallLifecycle(SERVICE_PATHS, "OnDisable")
	self.Log:i("OnDisable complete")
end

--- Minimap addon-compartment entry (uses the TOC IconTexture).
--- Left-click: loot rolls window. Right-click: options.
function _G.DetentionRolls_OnAddonCompartmentClick(_, buttonName)
	if buttonName == "RightButton" then
		DR:Require("Modules.Config"):OpenOptions()
	else
		DR:Require("Modules.RaiderFrame"):Toggle()
	end
end

local function splitInput(input)
	local cmd, rest = input:match("^(%S+)%s*(.-)$")
	if not cmd then
		return "", ""
	end
	return string.lower(cmd), rest or ""
end

function DR:ChatCommand(input)
	local cmd, rest = splitInput(input)

	-- Bare /droll opens the options panel.
	if cmd == "" or cmd == "config" then
		local config = self:Require("Modules.Config")
		if config and config.OpenOptions then
			config:OpenOptions()
		end
		return
	end

	if cmd == "help" then
		self:Print("DetentionRolls commands:")
		self:Print("  /droll - open options")
		self:Print("  /droll loot - open the loot rolls window")
		self:Print("  /droll raid [on|off] - designate this raid for DetentionRolls")
		self:Print("  /droll bar - toggle the status toolbar")
		self:Print("  /droll roster - player roster (roll power, addon status)")
		self:Print("  /droll test [off|fake N|resolve] - test mode (works solo)")
		self:Print("  /droll ping - check who else runs the addon")
		self:Print("  /droll weight - your current roll power")
		self:Print("  /droll status | ui | export")
		self:Print("  /drollrl (leader), /drollme (raider), /drollroster, /drolltrade")
		return
	end

	if cmd == "loot" then
		self:Require("Modules.RaiderFrame"):Toggle()
		return
	end

	if cmd == "bar" then
		self:Require("Modules.Toolbar"):Toggle()
		return
	end

	if cmd == "raid" then
		local activation = self:Require("Modules.RaidActivation")
		local sub = string.lower(rest or "")
		if sub == "on" then
			if not UnitIsGroupLeader("player") and IsInGroup() then
				self:Print("Only the group leader can activate raid mode")
			else
				activation:SetActive(true, "manual", true)
			end
		elseif sub == "off" then
			activation:SetActive(false, "manual", true)
		else
			self:Print("Raid mode: " .. (activation:IsActive() and "|cff6bcc7aACTIVE|r" or "|cffd08484inactive|r") .. " - /droll raid on|off")
		end
		return
	end

	if cmd == "test" then
		self:Require("Modules.TestMode"):HandleCommand(rest)
		return
	end

	if cmd == "ping" then
		self:Require("Services.Comms"):StartPing()
		return
	end

	if cmd == "weight" then
		local guid = UnitGUID("player")
		if not guid then
			self:Print("Could not resolve your GUID")
			return
		end
		local weight = self:Require("Services.Ledger"):GetWeight(guid)
		local formatted = self:Require("Domain.RollEngine"):FormatWeight(weight)
		self:Print(string.format("Your roll power: %s%% (rolls count for baseRoll x %s%%)", formatted, formatted))
		return
	end

	if cmd == "roster" then
		self:Require("Modules.RosterFrame"):Toggle()
		return
	end

	if cmd == "status" then
		local versionCheck = self:Require("Modules.VersionCheck")
		local ok, outdated = versionCheck:CanStartSession()
		if ok then
			self:Print("Session gate: PASS")
		else
			self:Print("Session gate: BLOCKED. Outdated players: " .. table.concat(outdated, ", "))
		end
		return
	end

	if cmd == "ui" then
		self:Require("Modules.RaidLeaderFrame"):Toggle()
		self:Require("Modules.RaiderFrame"):Toggle()
		return
	end

	if cmd == "export" then
		local export = self:Require("Services.Audit"):ExportAsLua(100)
		self:Print("Audit export (last 100 events):")
		self:Print(export)
		return
	end

	self:Print("Unknown command. Use /droll help")
end
