local addonName, ns = ...
local Fatebound = ns

-- ============================================
-- PLAYER STATS
-- ============================================
local function GenerateUniqueKey(name)
    return (name or "fatebound") .. "_" .. tostring(time()) .. "_" .. tostring(math.random(1000,9999))
end

-- Helper function to format numbers
function Fatebound.FormatNumber(n)
    if n >= 1e6 then
        return string.format("%.1fm", n / 1e6)
    elseif n >= 1e3 then
        return string.format("%sk", math.floor(n / 1e3))
    else
        return tostring(n)
    end
end

function Fatebound.FormatWithCommas(n)
    if not n then return "0" end
    local formatted = tostring(n)
    while true do
        formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
        if k == 0 then break end
    end
    return formatted
end

-- ============================================
-- MINOR STATS & CORRUPTION CALCULATION
-- ============================================
function Fatebound:RecalculateMinorStats()
    for _, stat in ipairs(Fatebound.STAT_LIST) do
        FateboundDB.stats[stat] = 0
    end

    local perkTrees = {"YshaarjPerks","NzothPerks","CthunPerks","YoggPerks","GhuunPerks"}

    if (FateboundDB.favor or 0) > 0 then
        for _, treeKey in ipairs(perkTrees) do
            local treeDB = FateboundDB[treeKey]
            if treeDB then
                for i, slotItem in pairs(treeDB) do
                    if type(i) ~= "string" and slotItem and slotItem.stats then
                        for _, stat in ipairs(Fatebound.STAT_LIST) do
                            FateboundDB.stats[stat] = FateboundDB.stats[stat] + (slotItem.stats[stat] or 0)
                        end
                    end
                end
                local eliteItem = treeDB["elite"]
                if eliteItem and eliteItem.stats then
                    for _, stat in ipairs(Fatebound.STAT_LIST) do
                        FateboundDB.stats[stat] = FateboundDB.stats[stat] + (eliteItem.stats[stat] or 0)
                    end
                end
            end
        end
    end

    -- Update corruption regardless of favor
    self:CalculateCorruption()
end

function Fatebound:CalculateCorruption()
    if (FateboundDB.favor or 0) == 0 then
        FateboundDB.stats.Corruption = 0
        return
    end

    local totalMinor = 0
    for _, stat in ipairs(Fatebound.STAT_LIST) do
        totalMinor = totalMinor + (FateboundDB.stats[stat] or 0)
    end

    local minorCorruption = (totalMinor / (#Fatebound.STAT_LIST * Fatebound.STAT_CAP)) * 25
    local totalItemCorruption = 0

    -- Only sum item corruption if favor > 0 (we know favor > 0 here)
    local perkTrees = {"YshaarjPerks","NzothPerks","CthunPerks","YoggPerks","GhuunPerks"}
    for _, treeKey in ipairs(perkTrees) do
        local treeDB = FateboundDB[treeKey]
        if treeDB then
            for i, item in pairs(treeDB) do
                if type(i) ~= "string" and item and item.stats and item.stats.Corruption then
                    totalItemCorruption = totalItemCorruption + item.stats.Corruption
                end
            end

            local eliteItem = treeDB["elite"]
            if eliteItem and eliteItem.stats and eliteItem.stats.Corruption then
                totalItemCorruption = totalItemCorruption + eliteItem.stats.Corruption
            end
        end
    end

    FateboundDB.stats.Corruption = math.min(100, minorCorruption + totalItemCorruption)
end

-- ============================================
-- SOUL ESSENCE
-- ============================================
function Fatebound:GainSoulEssence(amount)
    local gained = amount or math.random(64, 128)
    FateboundDB.SoulEssence = (FateboundDB.SoulEssence or 0) + gained
    Fatebound:RefreshAll()
end

-- ============================================
-- SOUL ESSENCE & RANDOM ITEM DROPS
-- ============================================
local ITEM_DROP_CHANCE = 0.005

local accumulatedSoulEssence = 0
local combatUnits = {}        -- tracks engaged units (nameplates or creature GUIDs)
local combatEngaged = false   -- whether the player participated in combat

local frame = CreateFrame("Frame")

-- Check if a GUID belongs to a creature
local function IsCreatureGUID(guid)
    return type(guid) == "string" and guid:match("^Creature%-")
end

-- Check if player is in an instance
local function IsInstance()
    local _, instanceType = IsInInstance()
    return instanceType == "party"
        or instanceType == "raid"
        or instanceType == "scenario"
end

-- ============================================
-- COMBAT TRACKING (INSTANCES)
-- ============================================
-- Track a unit by unit token
local function TrackUnit(unit)
    if UnitExists(unit)
       and UnitCanAttack("player", unit)
       and not UnitIsDeadOrGhost(unit) then
        combatUnits[unit] = true
        combatEngaged = true
    end
end

-- Track all visible hostile nameplates
local function TrackAllNameplates()
    for _, plate in ipairs(C_NamePlate.GetNamePlates()) do
        local unit = plate.UnitFrame and plate.UnitFrame.unit
        if unit then
            TrackUnit(unit)
        end
    end
end

-- ============================================
-- REWARD HANDLER
-- ============================================
local function GrantCombatRewards()
    -- Cancel rewards if player died or is resting
    if UnitIsDeadOrGhost("player") or IsResting() then
        accumulatedSoulEssence = 0
        combatUnits = {}
        combatEngaged = false
        return
    end

	-- ============================================
	-- Soul Essence: per unit
	-- ============================================
	for unit in pairs(combatUnits) do
		local gained = math.random(64, 128)
		accumulatedSoulEssence = accumulatedSoulEssence + gained
	end

	-- ============================================
	-- Item Drop: once per combat
	-- ============================================
	local dominance = FateboundDB.stats.Dominance or 0
	local finalDropChance = math.min(1, ITEM_DROP_CHANCE + dominance * 0.0005)

	if math.random() <= finalDropChance then
		local keys = {}
		for k in pairs(Fatebound.FakeItems) do
			table.insert(keys, k)
		end

		if #keys > 0 then
			local data = Fatebound.FakeItems[keys[math.random(#keys)]]
			if data then
				local fakeItem = {
					key = GenerateUniqueKey(data.name),
					name = "|cff7955c8" .. data.name .. "|r",
					icon = data.icon,
					stats = Fatebound:AssignRandomStats(2, data.elite),
					rank = 1,
					upgradeCount = 0,
					lore = data.lore,
					tree = data.tree,
					isElite = data.elite == true,
				}

				local added =
					Fatebound.Inventory
					and Fatebound.Inventory.AddItem
					and Fatebound.Inventory:AddItem(fakeItem)

				if added then
					if not FateboundDB.disableItemLootPrint then
						print("|cff00aa00You receive loot:|r |cff7955c8[" .. fakeItem.name .. "]|r")
					end
				else
					local fallbackSoul = math.random(256, 512)
					accumulatedSoulEssence = accumulatedSoulEssence + fallbackSoul

					if not FateboundDB.disableSoulPrint then
						print("|cffff0000Inventory Full!|r Converted item into " .. fallbackSoul .. " Soul Essence.")
					end
				end
			end
		end
	end

    -- Grant accumulated Soul Essence
    if accumulatedSoulEssence > 0 then
        Fatebound:GainSoulEssence(accumulatedSoulEssence)
        if not FateboundDB.disableSoulPrint then
            print("|cff6f6fff+" .. accumulatedSoulEssence .. " Soul Essence|r")
        end
    end

    -- Reset combat state
    accumulatedSoulEssence = 0
    combatUnits = {}
    combatEngaged = false
end

-- ============================================
-- INSTANCE NAMEPLATE TRACKING
-- ============================================
C_Timer.NewTicker(0.25, function()
    if IsInstance() then
        TrackAllNameplates()
    end
end)

-- ============================================
-- COMBAT END HANDLER
-- ============================================
local combatFrame = CreateFrame("Frame")
combatFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
combatFrame:SetScript("OnEvent", function()
    if combatEngaged then
        GrantCombatRewards()
    end
end)

-- ============================================
-- OUTDOOR KILL TRACKING
-- ============================================
frame:RegisterEvent("PARTY_KILL")
frame:SetScript("OnEvent", function(_, _, killerGUID, victimGUID)
    if IsInstance() then return end
    if killerGUID ~= UnitGUID("player") then return end
    if not IsCreatureGUID(victimGUID) then return end

    combatEngaged = true
    combatUnits[victimGUID] = true
end)

-- Configuration
local BG_SOUL_GAIN_MIN = 128
local BG_SOUL_GAIN_MAX = 256
local BGCombatUnits = {} -- track BG player kills

-- Frame for combat log
local BGFrame = CreateFrame("Frame")

-- Helper: Check if a GUID is a player
local function IsPlayerGUID(guid)
    return type(guid) == "string" and guid:match("^Player%-")
end

-- Helper: Check if GUID is secret (combat log "secret value")
local function IsSecret(value)
    if not issecretvalue then return false end
    return issecretvalue(value)
end

-- Helper: Did the player make the kill?
local function KillerIsSelf(killerGUID)
    if not killerGUID or IsSecret(killerGUID) then return false end
    return killerGUID == UnitGUID("player")
end

-- Helper: Is the victim a player?
local function TargetIsPlayer(victimGUID)
    if not victimGUID then return false end
    return IsSecret(victimGUID) or IsPlayerGUID(victimGUID)
end

-- Track player kills in BG via combat log
local function TrackBGKills()
    local _, instanceType = IsInInstance()
    if instanceType ~= "pvp" then return end

    BGFrame:SetScript("OnEvent", function()
        local _, subEvent, _, killerGUID, _, _, _, victimGUID, _, destFlags = CombatLogGetCurrentEventInfo()
        if subEvent ~= "PARTY_KILL" then return end

        if not KillerIsSelf(killerGUID) then return end
        if not TargetIsPlayer(victimGUID) then return end

        -- Only track valid player kills
        BGCombatUnits[victimGUID] = true
    end)

    BGFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end

-- Register safely (combat-safe)
local function SafeRegister()
    if not BGFrame.isRegistered then
        TrackBGKills()
        BGFrame.isRegistered = true
    end
end

-- Try registration immediately
SafeRegister()

-- Retry after leaving combat
local safeFrame = CreateFrame("Frame")
safeFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
safeFrame:SetScript("OnEvent", function()
    SafeRegister()
end)

-- Grant Soul Essence after combat ends
local BGCombatEndFrame = CreateFrame("Frame")
BGCombatEndFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
BGCombatEndFrame:SetScript("OnEvent", function()
    local _, instanceType = IsInInstance()
    if instanceType ~= "pvp" then return end

    for guid in pairs(BGCombatUnits) do
        local gained = math.random(BG_SOUL_GAIN_MIN, BG_SOUL_GAIN_MAX)
        Fatebound:GainSoulEssence(gained)
        if not FateboundDB.disableSoulPrint then
            print("|cff6f6fff+"..gained.." Soul Essence (BG)|r")
        end
    end

    -- Reset table after rewarding
    BGCombatUnits = {}
end)

-- ============================================
-- SOUL ESSENCE DEATH PENALTY
-- ============================================
local deathFrame = CreateFrame("Frame")
deathFrame:RegisterEvent("PLAYER_DEAD")

deathFrame:SetScript("OnEvent", function()
    -- Do not lose Soul Essence in battlegrounds
    local _, instanceType = IsInInstance()
    if instanceType == "pvp" then return end

    local current = FateboundDB.SoulEssence or 0
    if current <= 0 then return end

    -- Calculate 10% loss
    local loss = math.floor(current * 0.10)
    if loss <= 0 then return end

    FateboundDB.SoulEssence = math.max(0, current - loss)

    -- Clear any pending combat rewards to prevent exploitation
    accumulatedSoulEssence = 0
    combatUnits = {}
    combatEngaged = false

    if not FateboundDB.disableSoulPrint then
        print("|cffbb2f2a-" .. loss .. " Soul Essence|r")
    end

    Fatebound:RefreshAll()
end)

-- ============================================
-- STAT MODIFICATION
-- ============================================
function Fatebound:AddStat(stat, amount)
    if not FateboundDB.stats[stat] then return end
    FateboundDB.stats[stat] = math.min(Fatebound.STAT_CAP, math.max(0, FateboundDB.stats[stat] + amount))
    self:CalculateCorruption()
    Fatebound:RefreshAll()
end

function Fatebound:SetStat(stat, value)
    if not FateboundDB.stats[stat] then return end
    FateboundDB.stats[stat] = math.min(Fatebound.STAT_CAP, math.max(0, value))
    self:CalculateCorruption()
    Fatebound:RefreshAll()
end

-- ============================================
-- RANDOM STATS ASSIGNMENT
-- ============================================
function Fatebound:AssignRandomStats(count, isElite)
    local stats = {}
    local pool = {unpack(Fatebound.STAT_LIST)}

    if isElite then
        for i = 1, 2 do
            local idx = math.random(1, #pool)
            local stat = table.remove(pool, idx)
            stats[stat] = math.random(48, 96)
        end
        -- Use decimal for Corruption
        stats.Corruption = math.random() * (12 - 6) + 6  -- random float between 6 and 12
        stats.Corruption = math.floor(stats.Corruption * 10) / 10  -- round to 1 decimal place
    else
        count = count or math.random(1, 2)
        for i = 1, count do
            local idx = math.random(1, #pool)
            local stat = table.remove(pool, idx)
            stats[stat] = math.random(12, 24)
        end
    end

    return stats
end

-- ============================================
-- ITEM UPGRADE
-- ============================================
function Fatebound:UpgradeItem(item)
    if not item then return false end

    -- Check max rank
    if item.rank >= 3 then
        UIErrorsFrame:AddMessage("This item is at the maximum rank.", 1, 0, 0)
        return false
    end

    -- Calculate cost
    local costPerRank = { [1] = 4200, [2] = 10400 }
    local cost = costPerRank[item.rank] or 4200

    if (FateboundDB.SoulEssence or 0) < cost then
        UIErrorsFrame:AddMessage("Not enough Soul Essence (" .. cost .. " required).", 1, 0, 0)
        return false
    end

    -- Chance calculation
-- Chance calculation
	local baseChance = (item.rank == 1) and 20 or 10
	local convergence = FateboundDB.stats.Convergence or 0

	-- Apply convergence bonus without exceeding rank cap
	local cap = (item.rank == 1) and 80 or 60
	local finalChance = math.min(cap, baseChance + convergence * 0.08)

	local roll = math.random(1, 100)

    -- Failed upgrade
    if roll > finalChance then
        FateboundDB.SoulEssence = FateboundDB.SoulEssence - cost

        for i, it in ipairs(Fatebound.Items) do
            if it.key == item.key then
                table.remove(Fatebound.Items, i)
                break
            end
        end

        Fatebound:RefreshAll()
        return "destroyed"
    end

    -- SUCCESS: deduct cost
    FateboundDB.SoulEssence = FateboundDB.SoulEssence - cost

	if item.isElite then
		-- Increase Corruption by 2–6%, now as decimal
		local corruptionIncrease = (math.random() * (6 - 2) + 2)  -- random float 2–6
		item.stats.Corruption = math.min(100, (item.stats.Corruption or 0) + corruptionIncrease)
		item.stats.Corruption = math.floor(item.stats.Corruption * 10) / 10  -- round to 1 decimal

        if item.rank == 1 then
            -- Upgrade existing 2 normal stats by 18–36
            for stat, value in pairs(item.stats) do
                if stat ~= "Corruption" then
                    item.stats[stat] = value + math.random(18, 36)
                end
            end
        elseif item.rank == 2 then
            -- Add 3rd normal stat
            local availableStats = {}
            for _, stat in ipairs(Fatebound.STAT_LIST) do
                if not item.stats[stat] then
                    table.insert(availableStats, stat)
                end
            end
            if #availableStats > 0 then
                local newStat = availableStats[math.random(1, #availableStats)]
                item.stats[newStat] = math.random(48, 96)
            end
        end
    else
        -- Non-elite items
        if item.rank == 1 then
            for stat, value in pairs(item.stats) do
                item.stats[stat] = value + math.random(18, 36)
            end
        elseif item.rank == 2 then
            local availableStats = {}
            for _, stat in ipairs(Fatebound.STAT_LIST) do
                if not item.stats[stat] then
                    table.insert(availableStats, stat)
                end
            end
            if #availableStats > 0 then
                local newStat = availableStats[math.random(1, #availableStats)]
                item.stats[newStat] = math.random(48, 96)
            end
        end
    end

    item.rank = item.rank + 1
    Fatebound:RefreshAll()
    return true
end

-- ============================================
-- ITEM DATABASE
-- ============================================
Fatebound.Items = Fatebound.Items or {}
Fatebound.NextItemKey = Fatebound.NextItemKey or 1

function Fatebound:CreateFakeItemFromBlizzard(itemID)
    if not itemID then return end
    local data = Fatebound.AllowedExtractItems[itemID]
    if not data then return end

    FateboundDB.siphonCooldowns = FateboundDB.siphonCooldowns or {}
    local now = time()
    local expires = FateboundDB.siphonCooldowns[itemID]

	local cooldown = 24 * 60 * 60  -- 24 hours

	-- Check cooldown
	if expires and now < expires then
		local remaining = expires - now
		local hours = math.floor(remaining / 3600)
		local minutes = math.floor((remaining % 3600) / 60)
		UIErrorsFrame:AddMessage(
			"|cffFF5555You cannot siphon this item again for " .. hours .. "h " .. minutes .. "m.|r", 
			1, 0, 0
		)
		return
	end

    -- Create the item
    local fakeItem = {
        key = GenerateUniqueKey(data.name),
        name = "|cff7955c8" .. data.name .. "|r",
        icon = data.icon,
        stats = Fatebound:AssignRandomStats(2, data.elite),
        rank = 1,
        upgradeCount = 0,
        lore = data.lore,
        tree = data.tree,
        isElite = data.elite == true,
    }

    table.insert(Fatebound.Items, fakeItem)

    -- Apply cooldown as absolute timestamp
    FateboundDB.siphonCooldowns[itemID] = now + cooldown

	Fatebound:UpdateCooldownList()
    Fatebound:RefreshAll()
    return fakeItem
end

-- ==============================
-- Madness Random Events System
-- ==============================
Fatebound.MadnessEvents = Fatebound.MadnessEvents or {}

-- Spell / World Model IDs
local MADNESS_MODEL_ID   = 3051326
local MADNESS_MODEL_ID_2 = 3197070
local MADNESS_MODEL_ID_3 = 669475
local GOOD_CLICKABLE_ID = 2109030
local BAD_CLICKABLE_ID  = 2109043

-- ===================================
-- Madness Event Cooldown
-- ===================================
local MADNESS_EVENT_COOLDOWN = 10 -- seconds
Fatebound.LastMadnessEventTime = Fatebound.LastMadnessEventTime or 0

local function CanTriggerMadnessEvent()
    return (GetTime() - Fatebound.LastMadnessEventTime) >= MADNESS_EVENT_COOLDOWN
end

local function MarkMadnessEventTriggered()
    Fatebound.LastMadnessEventTime = GetTime()
end

-- ===================================
-- EVENT 1: Pulse
-- ===================================
local function MadnessEvent_PulseScreenWithModel()
    if not CanTriggerMadnessEvent() then return end

    if (Fatebound.MadnessPulseFrame and Fatebound.MadnessPulseFrame:IsShown()) or
       (Fatebound.MadnessModelFrame and Fatebound.MadnessModelFrame:IsShown()) then
        return
    end

    MarkMadnessEventTriggered()

    if not Fatebound.MadnessPulseFrame then
        local f = CreateFrame("Frame", nil, UIParent)
        f:SetAllPoints(UIParent)
        f:SetFrameStrata("FULLSCREEN")
        f:SetFrameLevel(0)

        local tex = f:CreateTexture(nil, "OVERLAY")
        tex:SetTexture("interface/scenarios/battleforazeroththreatsmap")
        tex:SetTexCoord(0.00048828125, 0.68115234375, 0.0009765625, 0.9072265625)
        tex:SetAllPoints()
        tex:SetVertexColor(1, 1, 1, 0)
        f.tex = tex

        f:SetScript("OnUpdate", function(self, elapsed)
            self.time = (self.time or 0) + elapsed
            tex:SetAlpha(math.sin(self.time * 3) * 0.5 + 0.5)
        end)

        f:Hide()
        Fatebound.MadnessPulseFrame = f
    end

    Fatebound.MadnessPulseFrame:Show()

    if not Fatebound.MadnessModelFrame then
        local container = CreateFrame("Frame", nil, UIParent)
        container:SetAllPoints(UIParent)
        container:SetFrameStrata("MEDIUM")
        container:SetFrameLevel(50)

        local model = CreateFrame("PlayerModel", nil, container)
        model:SetAllPoints(container)
        model:SetCamera(0)
        Fatebound.MadnessModelFrame = container
        Fatebound.MadnessModel = model
    end

    local models = {
        {id = MADNESS_MODEL_ID,   sound = "Interface\\AddOns\\Fatebound\\Media\\sounds\\FX_NZoth_Evil_Laugh_Stereo_01.ogg"},
        {id = MADNESS_MODEL_ID_2, sound = "Interface\\AddOns\\Fatebound\\Media\\sounds\\YoggSaronTentcles_Attack4.ogg"},
        {id = MADNESS_MODEL_ID_3, sound = "Interface\\AddOns\\Fatebound\\Media\\sounds\\YoggSaron_Birth.ogg"}
    }

    local choice = models[math.random(#models)]
    Fatebound.MadnessModel:SetModel(choice.id)
    Fatebound.MadnessModelFrame:Show()
    PlaySoundFile(choice.sound, "Master")

    C_Timer.After(5, function()
        if Fatebound.MadnessPulseFrame then Fatebound.MadnessPulseFrame:Hide() end
        if Fatebound.MadnessModelFrame then Fatebound.MadnessModelFrame:Hide() end
    end)
end

-- ===================================
-- EVENT 2: Clickable Models
-- ===================================
local function MadnessEvent_ClickableModels()
    if not CanTriggerMadnessEvent() then return end

    if (Fatebound.MadnessPulseFrame and Fatebound.MadnessPulseFrame:IsShown()) or
       (Fatebound.MadnessModelsFrame and Fatebound.MadnessModelsFrame:IsShown()) then
        return
    end

    MarkMadnessEventTriggered()

    local frame = CreateFrame("Frame", nil, UIParent)
    frame:SetAllPoints(UIParent)
    frame:SetFrameStrata("FULLSCREEN")
    frame:SetFrameLevel(50)
    Fatebound.MadnessModelsFrame = frame

    frame.models = {}
    frame.goodCount = 0
    frame.maxGood = 30

    local UIW, UIH = UIParent:GetWidth(), UIParent:GetHeight()

    local function RandomPoint(model)
        model:SetPoint(
            "CENTER",
            UIParent,
            "CENTER",
            math.random(-UIW/2 + 128, UIW/2 - 128),
            math.random(-UIH/2 + 128, UIH/2 - 128)
        )
    end

    local function SpawnGood()
        if frame.goodCount >= frame.maxGood then return end

        local m = CreateFrame("PlayerModel", nil, frame)
        m:SetSize(256, 256)
        RandomPoint(m)
        m:SetFrameStrata("HIGH")
        m:SetFrameLevel(51)
        m:SetCamera(0)
        m:SetModel(GOOD_CLICKABLE_ID)
        m:RefreshCamera()
        m:Show()

        frame.goodCount = frame.goodCount + 1
        table.insert(frame.models, m)

        m:SetScript("OnMouseDown", function(self)
            PlaySoundFile("Interface\\AddOns\\Fatebound\\Media\\sounds\\GO_YoggSaronTentacle_Birth_01.ogg", "Master")
            self:Hide()

            for i, v in ipairs(frame.models) do
                if v == self then table.remove(frame.models, i) break end
            end

            frame.goodCount = frame.goodCount - 1
            if frame.goodCount <= 0 then frame:Hide() end
        end)
    end

    local function SpawnBad()
        local m = CreateFrame("PlayerModel", nil, frame)
        m:SetSize(256, 256)
        RandomPoint(m)
        m:SetFrameStrata("HIGH")
        m:SetFrameLevel(51)
        m:SetCamera(0)
        m:SetModel(BAD_CLICKABLE_ID)
        m:RefreshCamera()
        m:Show()

        table.insert(frame.models, m)

        m:SetScript("OnMouseDown", function(self)
            PlaySoundFile(CLICK_SOUND, "Master")
            self:Hide()

            for i, v in ipairs(frame.models) do
                if v == self then table.remove(frame.models, i) break end
            end

            SpawnGood()
            SpawnBad()
        end)
    end

    for i = 1, 5 do SpawnGood() end
    for i = 1, 2 do SpawnBad() end

    frame:Show()
end

-- ===================================
-- EVENT 3: Old God Messages
-- ===================================
local function MadnessEvent_OldGodMessage()
    if not CanTriggerMadnessEvent() then return end
    MarkMadnessEventTriggered()

    if not Fatebound.OldGodMessages or #Fatebound.OldGodMessages == 0 then return end

    local entry = Fatebound.OldGodMessages[math.random(#Fatebound.OldGodMessages)]
    
    ns.ShowCenterMessage(entry.text, FateboundDB.path, 3, 2, 28)

    if entry.sound then
        PlaySoundFile(entry.sound, "Master")
    end
end

-- ===================================
-- Register events
-- ===================================
Fatebound.MadnessEvents[1] = MadnessEvent_PulseScreenWithModel
Fatebound.MadnessEvents[2] = MadnessEvent_ClickableModels
Fatebound.MadnessEvents[3] = MadnessEvent_OldGodMessage

-- Weights for events
local MadnessEventWeights = {
    [1] = 1, -- Event 1
    [2] = 1, -- Event 2
    [3] = 5, -- Event 3 is 5x more likely
}

-- ===================================
-- Trigger random Madness event
-- ===================================
function Fatebound:TriggerMadnessEvent()
    if not FateboundDB.stats.Madness or FateboundDB.stats.Madness <= 0 then return end
    if #Fatebound.MadnessEvents == 0 then return end
    if not CanTriggerMadnessEvent() then return end

    local chance = math.min(FateboundDB.stats.Madness / 1000, 1) * 0.25
    if math.random() <= chance then
        local totalWeight = 0
        for _, w in pairs(MadnessEventWeights) do totalWeight = totalWeight + w end
        local r = math.random() * totalWeight
        local cumulative = 0
        for i, w in pairs(MadnessEventWeights) do
            cumulative = cumulative + w
            if r <= cumulative then
                Fatebound.MadnessEvents[i]()
                break
            end
        end
    end
end

-- ===================================
-- Passive Madness Event Ticker
-- ===================================
if not Fatebound.MadnessTicker then
    Fatebound.MadnessTicker = C_Timer.NewTicker(15, function()
        if FateboundDB.stats.Madness and FateboundDB.stats.Madness > 0 then
            if (not Fatebound.MadnessPulseFrame or not Fatebound.MadnessPulseFrame:IsShown()) and
               (not Fatebound.MadnessModelsFrame or not Fatebound.MadnessModelsFrame:IsShown()) then
                Fatebound:TriggerMadnessEvent()
            end
        end
    end)
end

-- ============================================
-- SLASH COMMAND
-- ============================================
SLASH_SOULADD1 = "/souladd"
SlashCmdList["SOULADD"] = function(amount)
    amount = tonumber(amount)
    if not amount then
        print("|cffff4444Usage: /souladd <number>|r")
        return
    end

    FateboundDB.SoulEssence = (FateboundDB.SoulEssence or 0) + amount
    Fatebound:RefreshAll()
end

SLASH_FAKEITEM1 = "/fakeitem"
SlashCmdList["FAKEITEM"] = function()
    local keys = {}
    for k in pairs(Fatebound.FakeItems) do
        table.insert(keys, k)
    end

    if #keys == 0 then
        print("|cffff5555No fake items defined in Fatebound.FakeItems!|r")
        return
    end

    local randomKey = keys[math.random(#keys)]
    local data = Fatebound.FakeItems[randomKey]
    if not data then
        print("|cffff5555Invalid fake item data!|r")
        return
    end

    local fakeItem = {
        key = GenerateUniqueKey(data.name),
        name = "|cff7955c8" .. data.name .. "|r",
        icon = data.icon,
        stats = Fatebound:AssignRandomStats(2, data.elite),
        rank = 1,
        upgradeCount = 0,
        lore = data.lore,
        tree = data.tree,
        isElite = data.elite == true,
    }

    if Fatebound.Inventory and Fatebound.Inventory.AddItem then
        local added = Fatebound.Inventory:AddItem(fakeItem)
        if added then
            print("|cff00aa00You receive loot:|r |cff7955c8[" .. data.name .. "]|r")
        else
            print("|cffff0000Inventory Full! Cannot add item.|r")
        end
    else
        print("|cffff0000Inventory system not available!|r")
    end

    Fatebound:RefreshAll()
end
