-- WarlockDemonology.lua
-- july 2025 by Smufrik

-- MoP: Use UnitClass instead of UnitClassBase

local _, playerClass = UnitClass('player')
if playerClass ~= 'WARLOCK' then 
    -- Not a warlock, exiting WarlockDemonology.lua
    return 
end
-- Warlock detected, continuing WarlockDemonology.lua loading

local addon, ns = ...
local Hekili = _G[ addon ]
local class, state = Hekili.Class, Hekili.State

local floor = math.floor
local strformat = string.format

local spec = Hekili:NewSpecialization(266, true)

spec.name = "Demonology"
spec.role = "DAMAGER"
spec.primaryStat = 7 -- Intellect

-- Use MoP power type numbers instead of Enum
-- Mana = 0, SoulShards = 7 in MoP Classic
spec:RegisterResource( 0, { -- Mana = 0 in MoP
    -- Life Tap mana restoration (Warlock signature mechanic)
    life_tap = {
        last = function ()
            return state.last_cast_time.life_tap or 0
        end,
        interval = 1.5, -- Life Tap GCD
        value = function()
            -- Life Tap converts health to mana (30% max mana in MoP)
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "life_tap" ) and state.mana.max * 0.30 or 0
        end,
    },
    
    -- Harvest Life mana return (Demonology talent)
    harvest_life = {
        aura = "harvest_life",
        last = function ()
            local app = state.buff.harvest_life.applied
            local t = state.query_time
            return app + floor( ( t - app ) / 1 ) * 1
        end,
        interval = 1,
        value = function()
            -- Harvest Life channels mana return over time
            return state.buff.harvest_life.up and state.mana.max * 0.03 or 0 -- 3% per second
        end,
    },
    
    -- Dark Pact mana return (from demon health)
    dark_pact = {
        last = function ()
            return state.last_cast_time.dark_pact or 0
        end,
        interval = 1.5, -- Dark Pact GCD
        value = function()
            -- Dark Pact converts demon health to mana
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "dark_pact" ) and state.mana.max * 0.25 or 0
        end,
    },
}, {
    -- Base mana regeneration for Demonology
    base_regen = function ()
        local base = state.mana.max * 0.01 -- 1% base (Warlocks have low base regen)
        local spirit_bonus = (state.stat.spirit or 0) * 0.4 -- Spirit is less effective for Warlocks
        
        -- Fel Armor bonus to mana regeneration
        if state.buff.fel_armor.up then
            base = base * 1.20 -- 20% bonus
        end
        
        return (base + spirit_bonus) / 5 -- Convert to per-second
    end,
    
    -- Improved Life Tap talent bonus
    improved_life_tap = function ()
        return state.talent.improved_life_tap.enabled and 0.2 or 0 -- 20% spell power as mana efficiency
    end,
} )

-- Soul Shards resource registration
spec:RegisterResource( 7, { -- Soul Shards = 7 in MoP
    -- Soul Burn shard conversion
    soul_burn = {
        last = function ()
            return state.last_cast_time.soul_burn or 0
        end,
        interval = 1,
        value = function()
            -- Soul Burn converts shard for enhanced abilities
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "soul_burn" ) and -1 or 0 -- Consumes 1 shard
        end,
    },
    
}, {
    -- Base soul shard mechanics
    base_generation = function ()
        -- Soul shards don't regenerate naturally, must be generated by abilities
        return 0
    end,
    
    -- Soul Link talent affects shard efficiency
    soul_link_efficiency = function ()
        return state.talent.soul_link.enabled and 0.05 or 0 -- 5% efficiency bonus
    end,
} )

-- Demonic Fury resource system (Demonology signature mechanic)
spec:RegisterResource( 15, { -- Demonic Fury = 15 in MoP
    max = 1000, -- Explicit cap for Demonic Fury in MoP.
    modmax = 1000, -- Initialize modmax equal to max so cost scaling works before any adjustments.
    -- Demonic Fury generation from Shadow Bolt
    shadow_bolt_generation = {
        last = function ()
            return state.last_cast_time.shadow_bolt or 0
        end,
        interval = 1,
        value = function()
            -- Shadow Bolt generates 30 Demonic Fury
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "shadow_bolt" ) and 30 or 0
        end,
    },
    
    -- Demonic Fury generation from Soul Fire (higher than Shadow Bolt)
    soul_fire_generation = {
        last = function ()
            return state.last_cast_time.soul_fire or 0
        end,
        interval = 1,
        value = function()
            -- Soul Fire generates 50 Demonic Fury
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "soul_fire" ) and 50 or 0
        end,
    },
    
    -- Demonic Fury generation from Hand of Gul'dan
    hand_of_guldan_generation = {
        last = function ()
            return state.last_cast_time.hand_of_guldan or 0
        end,
        interval = 1,
        value = function()
            -- Hand of Gul'dan generates 30 Demonic Fury
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "hand_of_guldan" ) and 30 or 0
        end,
    },
    
    -- Demonic Fury drain while Metamorphosis is active (continuous)
    metamorphosis_drain = {
        last = function ()
            -- use last tick each second while buff up
            return state.buff.metamorphosis.up and (state.query_time - (state.query_time % 1)) or 0
        end,
        interval = 1,
        value = function()
            -- Drain 6 fury per second while in Metamorphosis
            return state.buff.metamorphosis.up and -6 or 0
        end,
    },
    
    -- Demonic Fury consumption for Hand of Gul'dan (Metamorphosis) reduced
    meta_hand_consumption = {
        last = function ()
            return state.last_cast_time.meta_hand_of_guldan or 0
        end,
        interval = 1,
        value = function()
            -- Hand of Gul'dan in Metamorphosis consumes 50 Demonic Fury
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "meta_hand_of_guldan" ) and -50 or 0
        end,
    },
    
    -- Demonic Fury consumption for Touch of Chaos (reduced)
    touch_of_chaos_consumption = {
        last = function ()
            return state.last_cast_time.touch_of_chaos or 0
        end,
        interval = 1,
        value = function()
            -- Touch of Chaos consumes 30 Demonic Fury
            local last = state.prev and state.prev[1] and state.prev[1].actName
            return ( last == "touch_of_chaos" ) and -30 or 0
        end,
    },
}, {
    -- Demonic Fury generation modifiers
    demonic_calling_bonus = function ()
        return state.talent.demonic_calling.enabled and 0.15 or 0 -- 15% bonus from Demonic Calling
    end,
    
    demonic_embrace_bonus = function ()
        return state.talent.demonic_embrace.enabled and 0.1 or 0 -- 10% bonus from Demonic Embrace
    end,
    
    demonic_presence_bonus = function ()
        return state.buff.demonic_presence.up and 0.2 or 0 -- 20% bonus from Demonic Presence
    end,
} )


-- Pet management system for Demonology Warlock
local function summon_demon(demon_type)
    -- Track which demon is active
    if demon_type == "imp" then
        applyBuff("grimoire_of_sacrifice_imp")
    elseif demon_type == "voidwalker" then
        applyBuff("grimoire_of_sacrifice_voidwalker")
    elseif demon_type == "succubus" then
        applyBuff("grimoire_of_sacrifice_succubus")
    elseif demon_type == "felhunter" then
        applyBuff("grimoire_of_sacrifice_felhunter")
    elseif demon_type == "felguard" then
        applyBuff("grimoire_of_sacrifice_felguard")
    end
end

-- Pet stat tracking for Demonology
local function update_pet_stats()
    local pet_health = UnitHealth("pet") or 0
    local pet_max_health = UnitHealthMax("pet") or 1
    local pet_health_pct = pet_health / pet_max_health
    
    -- Update pet health state for Dark Pact calculations
    if state then
        state.pet_health_pct = pet_health_pct
    end
end

-- Add reset_precast hook for state management and form checking
spec:RegisterHook( "reset_precast", function()
    -- Set safe default values to avoid errors
    local metamorphosis_up = false
    local current_mana = -1
    local current_shards = -1
    
    -- Safely access resource values using the correct state access pattern
    if state.mana then
        current_mana = state.mana.current or -1
    end
    if state.soul_shards then
        current_shards = state.soul_shards.current or -1
    end
    
    -- Fallback to direct API calls if state resources are not available
    if current_mana == -1 then
        current_mana = UnitPower("player", 0) or 0 -- Mana = power type 0
    end
    if current_shards == -1 then
        current_shards = UnitPower("player", 7) or 0 -- Soul Shards = power type 7 in MoP
    end
    
    -- Check for Metamorphosis buff
    local meta_name = GetSpellInfo(103958) -- Metamorphosis spell ID in MoP
    if meta_name and UnitBuff("player", meta_name) then
        applyBuff("metamorphosis")
        metamorphosis_up = true
    else
        removeBuff("metamorphosis")
    end
    
    -- Check which demon is active
    local active_pet = UnitCreatureFamily("pet")
    if active_pet then
        if active_pet == "Imp" then
            applyBuff("grimoire_of_sacrifice_imp")
        elseif active_pet == "Voidwalker" then
            applyBuff("grimoire_of_sacrifice_voidwalker")
        elseif active_pet == "Succubus" then
            applyBuff("grimoire_of_sacrifice_succubus")
        elseif active_pet == "Felhunter" then
            applyBuff("grimoire_of_sacrifice_felhunter")
        elseif active_pet == "Felguard" then
            applyBuff("grimoire_of_sacrifice_felguard")
        end
    end
end )

-- Additional debugging hook for when recommendations are generated
spec:RegisterHook( "runHandler", function( ability )
    -- Only log critical issues
    if not ability then
        -- Nil ability passed to runHandler
        return
    end
end )

-- Debug hook to check state at the beginning of each update cycle
spec:RegisterHook( "reset", function()
    -- Minimal essential verification
    if not state or not state.spec or state.spec.id ~= 266 then
        return
    end
    
    -- Basic state verification - level check
    if level and level < 10 then
        return
    end
end )

-- Talents - MoP compatible talent structure
-- Using tier/column system instead of retail IDs
spec:RegisterTalents( {
    -- Tier 1 (Level 15) - Movement/Utility
    dark_regeneration    = { 1, 1, 108359 }, -- Tier 1, Column 1, SpellID
    soul_leech           = { 1, 2, 108370 }, -- Tier 1, Column 2, SpellID
    harvest_life         = { 1, 3, 108371 }, -- Tier 1, Column 3, SpellID

    -- Tier 2 (Level 30) - Crowd Control
    howl_of_terror       = { 2, 1, 5484 },   -- Tier 2, Column 1, SpellID
    mortal_coil          = { 2, 2, 6789 },   -- Tier 2, Column 2, SpellID
    shadowfury           = { 2, 3, 30283 },  -- Tier 2, Column 3, SpellID

    -- Tier 3 (Level 45) - Defensive
    soul_link            = { 3, 1, 108415 }, -- Tier 3, Column 1, SpellID
    sacrificial_pact     = { 3, 2, 108416 }, -- Tier 3, Column 2, SpellID
    dark_bargain         = { 3, 3, 110913 }, -- Tier 3, Column 3, SpellID

    -- Tier 4 (Level 60) - Utility
    blood_fear           = { 4, 1, 111397 }, -- Tier 4, Column 1, SpellID
    burning_rush         = { 4, 2, 111400 }, -- Tier 4, Column 2, SpellID
    unbound_will         = { 4, 3, 108482 }, -- Tier 4, Column 3, SpellID

    -- Tier 5 (Level 75) - Demons
    grimoire_of_supremacy = { 5, 1, 108499 }, -- Tier 5, Column 1, SpellID
    grimoire_of_service   = { 5, 2, 108501 }, -- Tier 5, Column 2, SpellID
    grimoire_of_sacrifice = { 5, 3, 108503 }, -- Tier 5, Column 3, SpellID

    -- Tier 6 (Level 90) - DPS Enhancement
    archimondes_vengeance = { 6, 1, 130736 }, -- Tier 6, Column 1, SpellID
    kiljadens_cunning     = { 6, 2, 137587 }, -- Tier 6, Column 2, SpellID
    mannoroth_fury        = { 6, 3, 137288 }, -- Tier 6, Column 3, SpellID
} )

-- Glyphs (Enhanced System - authentic MoP 5.4.8 glyph system)
spec:RegisterGlyphs( {
    -- Major glyphs - Demonology Combat
    [58616] = "soul_swap",            -- Soul Swap now has a 50% chance to not trigger a cooldown
    [58617] = "haunt",                -- Haunt now has a 50% chance to not trigger a cooldown
    [58618] = "unstable_affliction",  -- Unstable Affliction now has a 50% chance to not trigger a cooldown
    [58619] = "corruption",           -- Corruption now has a 50% chance to not trigger a cooldown
    [58620] = "agony",                -- Agony now has a 50% chance to not trigger a cooldown
    [58621] = "siphon_life",          -- Siphon Life now has a 50% chance to not trigger a cooldown
    [58622] = "drain_life",           -- Drain Life now has a 50% chance to not trigger a cooldown
    [58623] = "drain_soul",           -- Drain Soul now has a 50% chance to not trigger a cooldown
    [58624] = "fear",                 -- Fear now has a 50% chance to not trigger a cooldown
    [58625] = "howl_of_terror",       -- Howl of Terror now has a 50% chance to not trigger a cooldown
    [58626] = "death_coil",           -- Death Coil now has a 50% chance to not trigger a cooldown
    [58627] = "shadow_bolt",          -- Shadow Bolt now has a 50% chance to not trigger a cooldown
    [58628] = "incinerate",           -- Incinerate now has a 50% chance to not trigger a cooldown
    [58629] = "immolate",             -- Immolate now has a 50% chance to not trigger a cooldown
    [58630] = "conflagrate",          -- Conflagrate now has a 50% chance to not trigger a cooldown
    [58631] = "chaos_bolt",           -- Chaos Bolt now has a 50% chance to not trigger a cooldown
    [58632] = "shadowburn",           -- Shadowburn now has a 50% chance to not trigger a cooldown
    [58633] = "fel_flame",            -- Fel Flame now has a 50% chance to not trigger a cooldown
    [58634] = "soul_fire",            -- Soul Fire now has a 50% chance to not trigger a cooldown
    [58635] = "metamorphosis",        -- Metamorphosis now has a 50% chance to not trigger a cooldown
    [58636] = "hand_of_guldan",       -- Hand of Gul'dan now has a 50% chance to not trigger a cooldown
    [58637] = "demonbolt",            -- Demonbolt now has a 50% chance to not trigger a cooldown
    [58638] = "doom",                 -- Doom now has a 50% chance to not trigger a cooldown
    [58639] = "shadowflame",          -- Shadowflame now has a 50% chance to not trigger a cooldown
    [58640] = "dancing_rune_weapon",  -- Dancing Rune Weapon lasts 5 sec longer but generates 20% less runic power
    [58641] = "vampiric_aura",        -- Your Vampiric Aura affects 2 additional party members but the healing is reduced by 25%
    [58642] = "unholy_frenzy",        -- Your Unholy Frenzy grants an additional 10% attack speed but lasts 50% shorter
    [58643] = "corpse_explosion",     -- Your corpses explode when they expire, dealing damage to nearby enemies
    [58644] = "disease",              -- Your diseases last 50% longer but deal 25% less damage
    [58645] = "resilient_grip",       -- Your Death Grip removes one movement impairing effect from yourself
    [58646] = "shifting_presences",   -- Reduces the rune cost to change presences by 1, but you cannot change presences while in combat
    
    -- Minor glyphs - Cosmetic and convenience
    [58647] = "corpse_walker",        -- Your undead minions appear to be spectral
    [58648] = "the_geist",            -- Your ghoul appears as a geist
    [58649] = "deaths_embrace",       -- Your death grip has enhanced visual effects
    [58650] = "bone_spikes",          -- Your abilities create bone spike visual effects
    [58651] = "unholy_vigor",         -- Your character emanates an unholy aura
    [58652] = "the_bloodied",         -- Your weapons appear to be constantly dripping blood
    [58653] = "runic_mastery",        -- Your runes glow with enhanced energy when available
    [58654] = "the_forsaken",         -- Your character appears more skeletal and undead
    [58655] = "shadow_walk",          -- Your movement leaves shadowy footprints
    [58656] = "deaths_door",          -- Your abilities create portal-like visual effects
} )

-- Ticks gained on refresh (MoP version for Warlock Demonology).
local tick_calculator = setfenv( function( t, action, pmult )
    local state = _G["Hekili"] and _G["Hekili"].State or {}
    local remaining_ticks = 0
    local potential_ticks = 0
    local remains = t.remains
    local tick_time = t.tick_time
    local ttd = min( state.fight_remains or 300, state.target and state.target.time_to_die or 300 )

    local aura = action
    -- Handle specific warlock spell mappings
    if action == "hand_of_guldan" then aura = "shadowflame" end
    if action == "metamorphosis_doom" then aura = "doom" end

    local class = _G["Hekili"] and _G["Hekili"].Class or {}
    local duration = class.auras and class.auras[ aura ] and class.auras[ aura ].duration or 0
    local app_duration = min( ttd, duration )
    local app_ticks = app_duration / tick_time

    remaining_ticks = min( remains, ttd ) / tick_time
    duration = max( 0, min( remains + duration, 1.3 * duration, ttd ) )
    potential_ticks = min( duration, ttd ) / tick_time

    -- Metamorphosis adjustments - certain spells have different durations in meta form
    if buff.metamorphosis and buff.metamorphosis.up then
        if action == "corruption" then aura = "doom" end
    end

    return max( 0, potential_ticks - remaining_ticks )
end, {} )

-- Auras
spec:RegisterAuras( {
    -- Core Metamorphosis and resources
    metamorphosis = {
        id = 103958,
        duration = 30,
        max_stack = 1,
    },
    dark_soul_knowledge = {
        id = 113861,
        duration = 20,
        max_stack = 1,
    },
    dark_soul = { -- generic alias so APL conditions using buff.dark_soul work across specs
        id = 113861, -- same as dark_soul_knowledge for Demonology
        duration = 20,
        max_stack = 1,
        generate = function( t )
            local a = state and state.buff.dark_soul_knowledge
            if a and a.up then
                t.count = 1; t.expires = a.expires; t.applied = a.applied; t.caster = 'player'; return
            end
            t.count = 0; t.expires = 0; t.applied = 0; t.caster = 'nobody'
        end,
    },
    molten_core = {
        id = 122355,
        duration = 14,
        max_stack = 5,
    },
    decimation = {
        id = 108869,
        duration = 10,
        max_stack = 1,
    },
    demonic_fury = {
        id = 109145,
        duration = 3600,
        max_stack = 1000,
    },
    
    -- DoTs and debuffs
    corruption = {
        id = 146739,
        duration = 14,
        tick_time = 2,
        max_stack = 1,
    },
    -- Doom already defined; add placeholder aura for shadowflame provided below.
    doom = {
        id = 603,
        duration = 60,
        tick_time = 15,
        max_stack = 1,
    },
    immolate = {
        id = 348,
        duration = 15,
        tick_time = 3,
        max_stack = 1,
    },
    shadowflame = {
        id = 47960,
        duration = 8,
        tick_time = 2,
        max_stack = 1,
    },
    curse_of_elements = {
        id = 1490,
        duration = 300,
        max_stack = 1,
    },
    curse_of_enfeeblement = {
        id = 109466,
        duration = 30,
        max_stack = 1,
    },
    curse_of_exhaustion = {
        id = 18223,
        duration = 30,
        max_stack = 1,
    },
    
    -- Crowd control
    fear = {
        id = 5782,
        duration = 20,
        mechanic = "fear",
        max_stack = 1,
    },
    howl_of_terror = {
        id = 5484,
        duration = 8,
        mechanic = "fear",
        max_stack = 1,
    },
    mortal_coil = {
        id = 6789,
        duration = 3,
        mechanic = "horror",
        max_stack = 1,
    },
    banish = {
        id = 710,
        duration = 30,
        mechanic = "banish",
        max_stack = 1,
    },
    shadowfury = {
        id = 30283,
        duration = 3,
        mechanic = "stun",
        max_stack = 1,
    },
    
    -- Grimoire talents
    grimoire_of_sacrifice = {
        id = 108503,
        duration = 3600,
        max_stack = 1,
    },
    grimoire_of_sacrifice_imp = {
        id = 0, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1,
    },
    grimoire_of_sacrifice_voidwalker = {
        id = 0, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1,
    },
    grimoire_of_sacrifice_succubus = {
        id = 0, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1,
    },
    grimoire_of_sacrifice_felhunter = {
        id = 0, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1,
    },
    grimoire_of_sacrifice_felguard = {
        id = 0, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1,
    },
    
    -- Self buffs and defensives
    unending_resolve = {
        id = 104773,
        duration = 8,
        max_stack = 1,
    },
    soul_link = {
        id = 108446,
        duration = 3600,
        max_stack = 1,
    },
    dark_bargain = {
        id = 110913,
        duration = 8,
        max_stack = 1,
    },
    burning_rush = {
        id = 111400,
        duration = 3600,
        max_stack = 1,
    },
    twilight_ward = {
        id = 6229,
        duration = 30,
        max_stack = 1,
    },
    blood_horror = {
        id = 111397,
        duration = 60,
        max_stack = 1,
    },
    
    -- Procs and effects
    demonic_rebirth = {
        id = 88448,
        duration = 15,
        max_stack = 1,
    },
    backdraft = {
        id = 117828,
        duration = 15,
        max_stack = 3,
    },
    hand_of_guldan = {
        id = 86040,
        duration = 0,
        max_stack = 1,
    },
    
    -- Pet abilities
    commanding_presence = {
        id = 0, -- Custom ID for tracking 
        duration = 3600,
        max_stack = 1,
    },
    demonic_synergy = {
        id = 0, -- Custom ID for tracking
        duration = 10,
        max_stack = 1,
    },
    fel_intelligence = {
        id = 54424,
        duration = 3600,
        max_stack = 1,
    },
    
    -- Utilities
    life_tap = {
        id = 1454,
        duration = 40,
        max_stack = 1,
    },
    drain_life = {
        id = 689,
        duration = 3,
        tick_time = 1,
        max_stack = 1,
    },
    drain_soul = {
        id = 1120,
        duration = 6,
        tick_time = 1,
        max_stack = 1,
    },
    soulburn = {
        id = 74434,
        duration = 30,
        max_stack = 1,
    },
    health_funnel = {
        id = 755,
        duration = 6,
        tick_time = 1,
        max_stack = 1,
    },
    
    -- Metamorphosis abilities
    carrion_swarm = {
        id = 103967,
        duration = 0,
        max_stack = 1,
    },
    chaos_wave = {
        id = 124916,
        duration = 0,
        max_stack = 1,
    },
    touch_of_chaos = {
        id = 103964, 
        duration = 0,
        max_stack = 1,
    },
    void_ray = {
        id = 115422,
        duration = 0,
        max_stack = 1,
    },
    
    -- MoP Tier Sets
    -- Tier 14 (MoP - Heart of Fear)
    tier14_2pc = {
        id = 123136, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1
    },
    tier14_4pc = {
        id = 123141, -- Custom ID for tracking
        duration = 30,
        max_stack = 1
    },

    -- Tier 15 (MoP - Throne of Thunder)
    tier15_2pc = {
        id = 138129, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1
    },
    tier15_4pc = {
        id = 138134, -- Custom ID for tracking
        duration = 15,
        max_stack = 1
    },

    -- Tier 16 (MoP - Siege of Orgrimmar)
    tier16_2pc = {
        id = 145091, -- Custom ID for tracking
        duration = 3600,
        max_stack = 1
    },
    tier16_4pc = {
        id = 145164, -- Custom ID for tracking
        duration = 30,
        max_stack = 1
    },
} )

-- Provide numeric state expressions for demonic_fury to support bare-token usage in APLs.
spec:RegisterStateExpr( "demonic_fury", function ()
    -- Prefer explicit resource table if initialized, else fall back to aura stack, else 0.
    if state.demonic_fury and state.demonic_fury.current ~= nil then
        return state.demonic_fury.current
    end
    return ( state.buff.demonic_fury and state.buff.demonic_fury.stack ) or 0
end )





-- Tweaking for new Demonology APL.
local doom_applied = false

spec:RegisterEvent( "PLAYER_REGEN_ENABLED", function ()
    doom_applied = false
end )

-- Event handler to ensure Demonology spec is enabled  
spec:RegisterEvent( "PLAYER_ENTERING_WORLD", function ()
    if state.spec.id == 266 then
        -- Ensure the spec is enabled in the profile
        if Hekili.DB and Hekili.DB.profile and Hekili.DB.profile.specs then
            if not Hekili.DB.profile.specs[266] then
                Hekili.DB.profile.specs[266] = {}
            end
            Hekili.DB.profile.specs[266].enabled = true
            
            -- Set default package if none exists
            if not Hekili.DB.profile.specs[266].package then
                Hekili.DB.profile.specs[266].package = "Demonology"
            end
        end
    end
end )

--[[spec:RegisterStateExpr( "opener_done", function ()
    return doom_applied
end )--]]

-- Metamorphosis and pet management for MoP

spec:RegisterStateFunction( "enter_metamorphosis", function ()
    applyBuff( "metamorphosis" )
    -- In MoP, Metamorphosis transforms some spells
    if buff.metamorphosis.up then
        -- Corruption becomes Doom in Metamorphosis
        if debuff.corruption.up then
            removeBuff( "corruption" )
            applyDebuff( "target", "doom" )
        end
    end
end )

spec:RegisterStateFunction( "exit_metamorphosis", function ()
    removeBuff( "metamorphosis" )
    -- Transform back doom to corruption if needed
    if debuff.doom.up and not action.doom.lastCast then
        removeBuff( "doom" )
        applyDebuff( "target", "corruption" )
    end
end )

-- Function to summon and manage demons
spec:RegisterStateFunction( "summon_demon", function( demon_type )
    -- Remove existing demon buffs
    removeBuff( "grimoire_of_sacrifice_imp" )
    removeBuff( "grimoire_of_sacrifice_voidwalker" )
    removeBuff( "grimoire_of_sacrifice_succubus" )
    removeBuff( "grimoire_of_sacrifice_felhunter" )
    removeBuff( "grimoire_of_sacrifice_felguard" )
    
    -- Apply new demon buff
    if demon_type then
        applyBuff( "grimoire_of_sacrifice_" .. demon_type )
    end
end )

local soul_shard_generators = {
    shadow_bolt       = true,
    soul_fire         = true,
    fel_flame         = true,
    drain_soul        = true
}

local soul_shard_spenders = {
    summon_imp        = true,
    summon_voidwalker = true,
    summon_succubus   = true,
    summon_felhunter  = true,
    summon_felguard   = true,
    soul_burn         = true,
    hand_of_guldan    = true
}

spec:RegisterHook( "runHandler", function( ability )
    local a = class.abilities[ ability ]

    if not a then return end
    
    -- Handle Soul Shard generation
    if soul_shard_generators[ ability ] then
        if not buff.metamorphosis.up then
            -- Chance to generate soul shard based on MoP mechanics
            if math.random() < 0.15 then -- 15% base chance
                gain( 1, "soul_shards" )
            end
        end
    end
    
    -- Handle Demonic Fury generation in Metamorphosis
    if buff.metamorphosis.up and a.startsCombat then
        if ability == "touch_of_chaos" then
            gain( 40, "demonic_fury" )
        elseif ability == "carrion_swarm" then
            gain( 25, "demonic_fury" )
        elseif ability == "chaos_wave" then
            gain( 350, "demonic_fury" )
        end
    end
end )

spec:RegisterHook( "gain", function( amt, resource, overflow )
    if overflow == nil then overflow = true end
    if amt > 0 and resource == "soul_shards" then
        -- Soul shard generation can trigger various effects
    end
end )

spec:RegisterStateTable( "warlock", setmetatable( {},{
    __index = function( t, k )
    if k == "no_cds" then return not ( state.toggle and state.toggle.cooldowns )
        elseif k == "metamorphosis_active" then return buff.metamorphosis.up
        elseif k == "in_metamorphosis" then return buff.metamorphosis.up
        elseif k == "pet_active" then 
            return buff.grimoire_of_sacrifice_imp.up or 
                   buff.grimoire_of_sacrifice_voidwalker.up or
                   buff.grimoire_of_sacrifice_succubus.up or
                   buff.grimoire_of_sacrifice_felhunter.up or
                   buff.grimoire_of_sacrifice_felguard.up
        elseif debuff[ k ] ~= nil then return debuff[ k ]
        end
    end
} ) )

-- MoP: Check for active DoTs
spec:RegisterStateExpr( "dots_ticking", function ()
    return debuff.corruption.up or debuff.doom.up or debuff.immolate.up or debuff.shadowflame.up
end )

-- Essential state expressions for APL functionality
spec:RegisterStateExpr( "time_to_die", function ()
    return target.time_to_die or 300
end )

spec:RegisterStateExpr( "spell_targets", function ()
    return active_enemies or 1
end )

spec:RegisterStateExpr( "mana_deficit", function ()
    return mana.max - mana.current
end )

spec:RegisterStateExpr( "mana_time_to_max", function ()
    return mana.deficit / mana.regen
end )

spec:RegisterStateExpr( "soul_shards_deficit", function ()
    return state.soul_shards and ( state.soul_shards.max - state.soul_shards.current ) or 0
end )

spec:RegisterStateExpr( "time_to_pool", function ()
    local mana_deficit = mana.max - mana.current
    if mana_deficit <= 0 then return 0 end
    return mana_deficit / mana.regen
end )

-- State expression to check if we can make recommendations
spec:RegisterStateExpr( "can_recommend", function ()
    return state.spec and state.spec.id == 266 and level >= 10
end )

-- Essential state expressions for APL functionality
spec:RegisterStateExpr( "current_mana", function ()
    return mana.current or 0
end )

spec:RegisterStateExpr( "current_soul_shards", function ()
    return state.soul_shards and state.soul_shards.current or 0
end )

spec:RegisterStateExpr( "max_mana", function ()
    return mana.max or 100000
end )

spec:RegisterStateExpr( "mana_regen", function ()
    return mana.regen or 500
end )

spec:RegisterStateExpr( "in_combat", function ()
    return state.combat and state.combat > 0 or 0
end )

spec:RegisterStateExpr( "player_level", function ()
    return level or 85
end )

-- Additional essential state expressions for APL compatibility
spec:RegisterStateExpr( "metamorphosis_form", function ()
    return buff.metamorphosis.up
end )

spec:RegisterStateExpr( "health_pct", function ()
    return health.percent or 100
end )

spec:RegisterStateExpr( "target_health_pct", function ()
    return target.health.percent or 100
end )

-- Demonic Fury tracking for Metamorphosis
spec:RegisterStateExpr( "demonic_fury_current", function ()
    return state.demonic_fury and state.demonic_fury.current or ( buff.demonic_fury.stack or 0 )
end )

spec:RegisterStateExpr( "demonic_fury_deficit", function ()
    local cur = state.demonic_fury and state.demonic_fury.current or ( buff.demonic_fury.stack or 0 )
    local max = state.demonic_fury and state.demonic_fury.max or 1000
    return max - cur
end )

-- Pet type checking
spec:RegisterStateExpr( "imp_active", function ()
    return buff.grimoire_of_sacrifice_imp.up
end )

spec:RegisterStateExpr( "felguard_active", function ()
    return buff.grimoire_of_sacrifice_felguard.up
end )

spec:RegisterStateExpr( "voidwalker_active", function ()
    return buff.grimoire_of_sacrifice_voidwalker.up
end )

spec:RegisterStateExpr( "succubus_active", function ()
    return buff.grimoire_of_sacrifice_succubus.up
end )

spec:RegisterStateExpr( "felhunter_active", function ()
    return buff.grimoire_of_sacrifice_felhunter.up
end )

-- MoP Tier Sets for Warlock Demonology

-- Tier 14 (MoP - Heart of Fear)
spec:RegisterGear( "tier14", 84430, 84429, 84428, 84427, 84426 )
-- 2-piece: When your corruption deals damage, you have a 2% chance to gain 20% additional haste for 20 sec.
spec:RegisterAura( "t14_2pc_proc", {
    id = 123136,
    duration = 20,
    max_stack = 1
} )
-- 4-piece: Your Curse spells and Shadowflame have a 40% chance to reduce the cast time of your next Malefic Grasp by 100%.
spec:RegisterAura( "t14_4pc_proc", {
    id = 123141,
    duration = 30,
    max_stack = 1
} )

-- Tier 15 (MoP - Throne of Thunder)
spec:RegisterGear( "tier15", 95430, 95429, 95428, 95427, 95426 )
-- 2-piece: When you use Dark Soul, you also gain 15% spell haste for 20 sec.
spec:RegisterAura( "t15_2pc_proc", {
    id = 138129,
    duration = 20,
    max_stack = 1
} )
-- 4-piece: When your Corruption deals damage, you have a 30% chance to deal Shadowflame damage to all enemies within 8 yards of your target.
spec:RegisterAura( "t15_4pc_proc", {
    id = 138134,
    duration = 15,
    max_stack = 1
} )

-- Tier 16 (MoP - Siege of Orgrimmar)
spec:RegisterGear( "tier16", 99159, 99158, 99157, 99156, 99155 )
-- 2-piece: Metamorphosis now reduces damage taken by an additional 20%, and you regenerate Demonic Fury 20% faster.
spec:RegisterAura( "t16_2pc", {
    id = 145091,
    duration = 3600,
    max_stack = 1
} )
-- 4-piece: While in Metamorphosis, your Touch of Chaos and Soul Fire deal 25% additional damage as Chaos damage.
spec:RegisterAura( "t16_4pc_proc", {
    id = 145164,
    duration = 30,
    max_stack = 1
} )

-- MoP: Demonic Fury calculation for Metamorphosis
local function calculate_demonic_fury_generation( ability )
    local base_generation = 0
    
    if ability == "shadow_bolt" then
        base_generation = 40
    elseif ability == "soul_fire" then
        base_generation = 60
    elseif ability == "hand_of_guldan" then
        base_generation = 400
    elseif ability == "life_tap" then
        base_generation = 50
    end
    
    -- Apply modifiers for gear sets
    local modifier = 1
    if set_bonus.tier16_2pc > 0 then
        modifier = modifier * 1.2 -- 20% faster Demonic Fury regeneration
    end
    
    return base_generation * modifier
end

-- Soul Shard generation tracking for MoP
-- Deterministic Soul Shard generation model (remove RNG): generate 1 shard every N qualifying casts.
do
    local casts_since_shard = 0
    local SHARD_INTERVAL = 7  -- adjustable: average out former 15% proc (~1 per 6-7 casts).
    spec:RegisterStateFunction( "generate_soul_shard", function()
        if buff.metamorphosis.up then return end -- no shard generation in meta
        casts_since_shard = casts_since_shard + 1
        if casts_since_shard >= SHARD_INTERVAL then
            casts_since_shard = 0
            gain( 1, "soul_shards" )
        end
    end )
end

-- Abilities specific to MoP Demonology
spec:RegisterAbilities( {
    -- Shadow Bolt: Primary nuke, generates soul shards and demonic fury
    shadow_bolt = {
        id = 686,
        cast = 2.5,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 0.02,
        spendType = "mana",
        startsCombat = true,
        velocity = 20,
        handler = function()
            if state.soul_shards and state.soul_shards.generate then state.soul_shards.generate( 1 ) end
            if buff.metamorphosis.up then
                gain( calculate_demonic_fury_generation("shadow_bolt"), "demonic_fury" )
            end
        end,
    },

    -- Core DoTs / Metamorphosis
    corruption = {
        id = 146739,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        startsCombat = true,
        texture = 136118,
        handler = function()
            applyDebuff( "target", "corruption" )
        end,
    },
    doom = {
        id = 603,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        startsCombat = true,
        texture = 136122,
        handler = function()
            applyDebuff( "target", "doom" )
        end,
    },

    -- Curse of the Elements: Magic vulnerability debuff (utility/raid-assigned)
    curse_of_elements = {
        id = 1490,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        startsCombat = true,
        handler = function()
            applyDebuff( "target", "curse_of_elements" )
        end,
    },

    -- metamorphosis ability likely already defined later; only add cancel helper.
    cancel_metamorphosis = {
        id = 103958, -- reuse spell id for icon
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        texture = 136243,
        handler = function()
            state:exit_metamorphosis()
        end,
        usable = function() return buff.metamorphosis.up end,
    },

    -- hand_of_guldan already exists; do not redefine.

    -- touch_of_chaos defined elsewhere (with resource consumption); skip duplicate.

    -- hellfire / immolation_aura not previously defined: re-add minimal versions below if missing.
    -- (Removed duplicate placeholder ability entries; full detailed definitions appear later.)

    -- Soul Fire: High damage nuke, enhanced in metamorphosis
    soul_fire = {
        id = 6353,
        cast = function() 
            if buff.decimation.up then return 0 end
            return 4.0 * haste 
        end,
        cooldown = 0,
        gcd = "spell",
        school = "fire",
        spend = 0.08,
        spendType = "mana",
        startsCombat = true,
        velocity = 20,
        handler = function()
            if buff.decimation.up then
                removeBuff("decimation")
            end
            if state.soul_shards and state.soul_shards.generate then state.soul_shards.generate( 1 ) end
            if buff.metamorphosis.up then
                gain( calculate_demonic_fury_generation("soul_fire"), "demonic_fury" )
            end
        end,
    },

    -- Touch of Chaos: Metamorphosis replacement for Shadow Bolt
    touch_of_chaos = {
        id = 103964,
        cast = 2.5,
        cooldown = 0,
        gcd = "spell",
        school = "chaos",
        spend = 40,
        spendType = "demonic_fury",
        startsCombat = true,
        form = "metamorphosis",
        velocity = 20,
        handler = function()
            -- Touch of Chaos doesn't generate soul shards but uses demonic fury
        end,
    },

    -- Metamorphosis: Transform into demon form
    metamorphosis = {
        id = 103958,
        cast = 0,
        cooldown = 600,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        toggle = "cooldowns",
        handler = function()
            applyBuff("metamorphosis")
            applyBuff("demonic_fury")
            -- Enter Metamorphosis if enough fury; just apply buff via handler
            if not state.buff.metamorphosis.up then applyBuff( "metamorphosis" ) end
        end,
    },

    -- Hand of Gul'dan: Soul shard spender, applies shadowflame
    hand_of_guldan = {
        id = 71521,
        cast = 2.0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = true,
        velocity = 15,
        handler = function()
            applyDebuff("target", "shadowflame")
            if buff.metamorphosis.up then
                gain( calculate_demonic_fury_generation("hand_of_guldan"), "demonic_fury" )
            end
        end,
    },
    -- Alias to satisfy import naming with apostrophe; no separate spellID, just call main handler.
    ["hand_of_gul'dan"] = {
        id = 71521,
        alias = "hand_of_guldan"
    },

    -- Carrion Swarm: Metamorphosis AoE ability
    carrion_swarm = {
        id = 103967,
        cast = 0,
        cooldown = 8,
        gcd = "spell",
        school = "shadow",
        spend = 50,
        spendType = "demonic_fury",
        startsCombat = true,
        form = "metamorphosis",
        handler = function()
            -- AoE damage handled by game
        end,
    },

    -- Chaos Wave: High demonic fury cost AoE
    chaos_wave = {
        id = 124916,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        school = "chaos",
        spend = 350,
        spendType = "demonic_fury",
        startsCombat = true,
        form = "metamorphosis",
        handler = function()
            -- High AoE damage
        end,
    },

    -- Dark Soul: Knowledge - Major DPS cooldown
    dark_soul_knowledge = {
        id = 113861,
        cast = 0,
        cooldown = 120,
        gcd = "off",
        school = "shadow",
        startsCombat = false,
        toggle = "cooldowns",
        handler = function()
            applyBuff("dark_soul_knowledge")
            if set_bonus.tier15_2pc > 0 then
                applyBuff("t15_2pc_proc")
            end
        end,
    },
    -- Generic dark_soul alias used by imported APLs (maps to spec-specific buff/action).
    dark_soul = {
        id = 113861, -- reuse Knowledge ID
        alias = "dark_soul_knowledge"
    },

    -- Life Tap: Convert health to mana, generates demonic fury in meta
    life_tap = {
        id = 1454,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        startsCombat = false,
        handler = function()
            applyBuff("life_tap")
            if buff.metamorphosis.up then
                gain( calculate_demonic_fury_generation("life_tap"), "demonic_fury" )
            end
        end,
    },

    -- Grimoire of Service: Summon a second demon temporarily.
    grimoire_of_service = {
        id = 108501,
        cast = 0,
        cooldown = 120,
        gcd = "spell",
        school = "shadow",
        startsCombat = false,
        toggle = "cooldowns",
        handler = function()
            -- Simplified: no separate pet modeling, assume immediate damage gain.
        end,
    },

    -- Imp Swarm (via glyph): converts Wild Imps into a burst (CD ability).
    imp_swarm = {
        id = 104316,
        cast = 0,
        cooldown = 120,
        gcd = "spell",
        school = "shadow",
        startsCombat = true,
        toggle = "cooldowns",
        handler = function() end,
    },

    -- Summon abilities using soul shards
    summon_imp = {
        id = 688,
        cast = 6.0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        essential = true,
        handler = function()
            summon_demon("imp")
        end,
    },

    summon_felguard = {
        id = 30146,
        cast = 6.0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        essential = true,
        handler = function()
            summon_demon("felguard")
        end,
    },

    -- Summon Infernal (AoE guardian alternative to Doomguard)
    summon_infernal = {
        id = 1122,
        cast = 6.0,
        cooldown = 600,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        toggle = "cooldowns",
        handler = function()
            -- Placeholder aura to reflect active guardian if needed
        end,
    },

    felstorm = {
        id = 89751,
        cast = 0,
        cooldown = 45,
        gcd = "spell",
        startsCombat = true,
        handler = function() end,
    },
    -- Namespaced pet ability reference sometimes produced by imports (felguard:felstorm)
    ["felguard:felstorm"] = {
        id = 89751,
        alias = "felstorm"
    },

    summon_voidwalker = {
        id = 697,
        cast = 6.0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        essential = true,
        handler = function()
            summon_demon("voidwalker")
        end,
    },

    summon_succubus = {
        id = 712,
        cast = 6.0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        essential = true,
        handler = function()
            summon_demon("succubus")
        end,
    },

    summon_felhunter = {
        id = 691,
        cast = 6.0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        essential = true,
        handler = function()
            summon_demon("felhunter")
        end,
    },

    -- Fel Flame: Instant cast spell, weak but mobile
    fel_flame = {
        id = 77799,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        school = "fire",
        spend = 0.04,
        spendType = "mana",
        startsCombat = true,
        velocity = 25,
        handler = function()
            if state.soul_shards and state.soul_shards.generate then state.soul_shards.generate( 1 ) end
        end,
    },

    -- Hellfire: Channeled AoE (mana drain); simplified modeling.
    hellfire = {
        id = 1949,
        channeled = true,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        school = "fire",
        spend = 0.02,
        spendType = "mana",
        startsCombat = true,
        aoe = 8,
        handler = function()
            -- Periodic AoE handled externally; treat each tick as damage event.
        end,
    },

    -- Immolation Aura: Metamorphosis AoE Fury spender (high cost, short duration aura).
    immolation_aura = {
        id = 104025, -- MoP-era spellID (may differ; adjust if necessary)
        cast = 0,
        cooldown = 15,
        gcd = "spell",
        school = "fire",
        spend = 100,
        spendType = "demonic_fury",
        startsCombat = true,
        form = "metamorphosis",
        aoe = 8,
        handler = function()
            applyBuff( "immolation_aura" )
        end,
        usable = function() return buff.metamorphosis.up end,
    },

    -- Void Ray: Metamorphosis channeled ability
    void_ray = {
        id = 115422,
        cast = 0,
        channeled = true,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 100,
        spendType = "demonic_fury",
        startsCombat = true,
        form = "metamorphosis",
        handler = function()
            -- Channeled damage
        end,
    },

    -- Soulburn: Enhances next spell
    soulburn = {
        id = 74434,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        handler = function()
            applyBuff("soulburn")
        end,
    },

    -- Dark Intent precombat buff.
    dark_intent = {
        id = 109773,
        cast = 0,
        cooldown = 0,
        gcd = "spell",
        startsCombat = false,
        handler = function()
            applyBuff( "dark_intent" )
        end,
        usable = function()
            if buff.dark_intent.up then return false, "dark_intent active" end
            return true
        end,
    },

    -- Grimoire of Sacrifice (active cast) – applies base aura and pet-specific aura.
    grimoire_of_sacrifice = {
        id = 108503,
        cast = 0,
        cooldown = 30,
        gcd = "spell",
        school = "shadow",
        startsCombat = false,
        handler = function()
            -- Remove any previous pet sacrifice buffs then apply one depending on last active demon (simplified: imp).
            removeBuff( "grimoire_of_sacrifice_imp" )
            removeBuff( "grimoire_of_sacrifice_voidwalker" )
            removeBuff( "grimoire_of_sacrifice_succubus" )
            removeBuff( "grimoire_of_sacrifice_felhunter" )
            removeBuff( "grimoire_of_sacrifice_felguard" )
            applyBuff( "grimoire_of_sacrifice" )
            -- Default to imp variant unless we track last demon (future improvement: detect via state.last_pet).
            applyBuff( "grimoire_of_sacrifice_imp" )
        end,
    },

    -- Summon Doomguard: Long cooldown guardian.
    summon_doomguard = {
        id = 18540,
        cast = 6.0,
        cooldown = 600,
        gcd = "spell",
        school = "shadow",
        spend = 1,
        spendType = "soul_shards",
        startsCombat = false,
        toggle = "cooldowns",
        handler = function()
            applyBuff( "doomguard" ) -- placeholder aura to reflect active guardian.
        end,
    },

    -- Command Demon: Utility; model as off-GCD placeholder if needed by APL.
    command_demon = {
        id = 119898,
        cast = 0,
        cooldown = 24,
        gcd = "off",
        school = "shadow",
        startsCombat = false,
        handler = function() end,
    },

    -- Generic summon_pet used by some imports – alias to summon_felguard (dominant demon for demo) if none active.
    summon_pet = {
        id = 30146,
        alias = "summon_felguard",
    },

    -- Drain Soul: Channel to finish low health enemies
    drain_soul = {
        id = 1120,
        cast = 6,
        channeled = true,
        cooldown = 0,
        gcd = "spell",
        school = "shadow",
        spend = 0.06,
        spendType = "mana",
        startsCombat = true,
        handler = function()
            applyDebuff("target", "drain_soul")
            -- Guaranteed soul shard if target dies during channel
        end,
    },
} )

spec:RegisterRanges( "shadow_bolt", "corruption", "fear", "curse_of_elements" )

-- Add missing auras for new abilities if not previously declared.
if not spec.auras.immolation_aura then
    spec:RegisterAura( "immolation_aura", { id = 104025, duration = 10, max_stack = 1 } )
end
if not spec.auras.doomguard then
    spec:RegisterAura( "doomguard", { id = 18540, duration = 45, max_stack = 1 } )
end
if not spec.auras.dark_intent then
    spec:RegisterAura( "dark_intent", { id = 109773, duration = 3600, max_stack = 1 } )
end

spec:RegisterOptions( {
    enabled = true,

    aoe = 3,
    cycle = false,

    nameplates = true,
    nameplateRange = 40,
    rangeFilter = false,

    damage = true,
    damageDots = true,
    damageExpiration = 3,

    potion = "jade_serpent_potion",

    package = "Demonology"
} )

spec:RegisterSetting( "metamorphosis_save", false, {
    name = strformat( "Save %s for Burn Phase", Hekili:GetSpellLinkWithTexture( spec.abilities.metamorphosis.id ) ),
    desc = strformat( "If checked, %s will only be recommended during burn phases or when dark soul is active.",
        Hekili:GetSpellLinkWithTexture( spec.abilities.metamorphosis.id ) ),
    type = "toggle",
    width = "full",
} )

spec:RegisterSetting( "corruption_refresh", 30, {
    name = strformat( "%s Refresh Percentage", Hekili:GetSpellLinkWithTexture( spec.auras.corruption.id ) ),
    desc = strformat( "Percentage of remaining duration at which to refresh %s.",
        Hekili:GetSpellLinkWithTexture( spec.auras.corruption.id ) ),
    type = "range",
    min = 10,
    max = 90,
    step = 5,
    width = 1.5
} )

spec:RegisterVariable( "metamorphosis_save", function()
    return state.settings.metamorphosis_save ~= false
end )

spec:RegisterVariable( "corruption_refresh", function()
    return state.settings.corruption_refresh or 30
end )

spec:RegisterStateExpr( "should_refresh_corruption", function()
    local refresh_pct = state.settings.corruption_refresh or 30
    return debuff.corruption.remains_percent <= refresh_pct
end )


spec:RegisterStateExpr( "metamorphosis_active", function()
    return buff.metamorphosis.up and 1 or 0
end )

spec:RegisterStateExpr( "demonic_presence_bonus", function()
    return buff.demonic_presence.up and 0.2 or 0 -- 20% damage bonus
end )

spec:RegisterStateExpr( "dark_soul_bonus", function()
    return buff.dark_soul.up and 0.3 or 0 -- 30% damage bonus
end )

spec:RegisterStateExpr( "molten_core_bonus", function()
    return buff.molten_core.up and 0.15 or 0 -- 15% damage bonus
end )

spec:RegisterStateExpr( "soulburn_active", function()
    return buff.soulburn.up and 1 or 0
end )

spec:RegisterStateExpr( "spell_power", function()
    return GetSpellBonusDamage(6) -- Shadow school
end )

spec:RegisterStateExpr( "metamorphosis_ready", function()
    local fury = state.demonic_fury and state.demonic_fury.current or 0
    return fury >= 300 and 1 or 0
end )


-- TODO: Re-export / update pack after ability additions if needed.
spec:RegisterPack( "Demonology", 20251006, [[Hekili:fRv)UnUXr8NfJa4RhSpvszlzNaldKMKMeJExcGs79FICf5kjwtYvLFyxdyq0hI(e2NKo7Yp2D5UdfLRY1aKdYKZoZUZ8B(C5k3v)2QLHKc6Qpn1z6mxhN5tMo152P3UAzXl7PRwUNe8izl8Jusc8VFpnHLYIzBFH)QxIzKqolYzLzbWRxTCDzuCXpNUATn(EZ1aP7PbWtNpF1YDrHH0AsP5bRw(B7IYR85)pPYVrWv(SnWFhueXsR8JJYlGxVHLv5)t0hJIJMSAP4H8DbBpnLMb)6tIZfnLSoMgU6pvZ)SO9CMSA5ViiRY)pSTmkK(bsC02uA47xTSwkW(IKg6X24TTm(DHK0vfW(LZW23hsYE0doYXQYai6QEsfezbnlIWL)6YnBMeWYYkf7IjHSNtLCu(coFUgLpfKyAAXKTzrjSOmkFtMtZEkkGoPL(oEAHioZNPEs2qJ3wsYc)g4h5fSSKENO5QehLS3l)zIbr3OsuEzcGq8czSebN7r7TQ02xpRt6xRsAcTGKWY2VJLhL3JqxNdP357MEAC(JelUpuX(IZOjKOua6DxL)2Gq5FFrLVRbxla3fqjW30Ea12qKQ20ST0IjfrjuVcMxyeG5VVYh8pcEjigEM495CwSmH8p90F6sxROiDe)NyPF4JWMPY)h)UVVY)dv(FB4tK0aAi4lLXsQ8)m7ZG9fopFK9RD4DRB3GDCjduUOYFAL)RVw5R(i3k)ZR8bN84yV2Ju97bDhFjtMnOJgUpuJEAhLexSBY(GIggoREtiSvjS4cAQhOiOtYlGiiGQSEtzLKY9kaxWF2Bd4VmShycjLif(voIJ7zv(prGxdKpbS3EHzS95E5BAFzTGvHWAIooAd3GUVZ90QKvfs(MozOCc2ramU3A4a257AhyZkudfjX2Wj6MbpqyrSUzq9vRHYb(pb75BG6D7MyiTcG9dEmkD7ifTQ662bpKwKXGGpTqoptIk8ktlII5(WEzus4lhrKNdjE9iADrISzm7juTCrDkdnIk4ba2qkHfFOSHFeS9Iqc)q6o8qcDYJuwWci5fESID0mWnIfZdSMpCqJZQT5kGod9HoG6QJjl11JjlfUJLTaWCa78MWgybECgiRx)CNWsbTfxe2sEQSTAxWEwJAPBBkcIi5JicsDrz)DsiLNIhQRPWRzHd6ByJvTcEDmJf6TPm7LopclBpJu3Aykz2LFcORUeUFSMsqDgLi811qzyzyou6exhSuqTiZjDb21d05ITqHYr11TBzIujZgCDcTxCzEbOt1xqBAan1(ywig8C6uRHGmZNHzBwcuu5)NbsQ85HP3staa0W4e5gw5X6kOBhom(W1CHRLWsJEivipDdVPLOabOU(fqUOHvUnj2nxjKeBOy49dbEewdjIt1CmGFIbqvvzGdGRpzbLzz8Wf8tEZjRvnJMDURgklc1Io2zyjP)86iQ)pyu4AF8IhpqfyzLPE1)2J3nzDpLE1rx7QINlH(fgoggO1kaNj9tf1YKYCQhSH5wznqY31MKf6zfu9zIMv7D266jLFU4cPF5FhiXI26hoVIlE1EwyL98kUhi5ecpOzW2ru3GOgN9z0awYAYHRY5ZKSywWJFtLVCcgG2ekT59v()N)1)UYpJkgDrR)2ZSNlwhmz7wrlEWFLlQf6HL)YNQ8)1w5oIcNWvLQLGu7dO1lPuHJxxLT1H1uv77fQ2O0crSE86OAkPzpTpvZgvzQfkoowmoDBgEdZ9kiD8nQ2REBrN67PzbCLCuAauYEoTnGFDOLdqMS9KED9ZlhCQ9un2Sqd3NE9H2O7(b7(DOE2UwE2uiXYjeoGyfCJ3X7VZn8JxxUL21hr(nrZj1(fdv8owLbUoxl3UfSYGD89lCGy5gf13719X)eg9GXL(w2paHHMFr7gn)9d12ZGXc6mkIJXHJhyLjkJ3aWvkOf1MUXscQo)PG(JrZk4L3nqWJn9fzZHahDyA6UYXrPgf1mi4vboqTC4ZpbVo0M5ZcAXuwg0HSqyMJMnccTgt4)2JuMrS1h4yK2zJwG7aVZ2zBD7iJFdp4jiFlNmXq8DUfQUDj0zDkK)nNpTEWfd62MLv0mr(3vpa(3XtM(pkbXbrTZzCpr(udsif8haW0uaMoP6H)suk8kxiN8FnnVCpNrCcQ3Aa36bCFx3sUcDjsqMK6RrP2YeYLlBg6Ym8lLlAo6I6glHK4Bp(t(xJUenaICbUoORGNfsHqC7qlHcd(gwCm7zr7aCOl4e)mL3aju7kSg(OKk4K1UyEzWChYIw6szc0szQg1HHCIdjfK1qMQVP6brUFP1S5b8Dc8ZhSa9ulY(ubaTHMME8gnC4AxxHJb2P4QogaNTn)jfXzB(OJc4HbvXne9o6FXbIA742Njlmbbs2mb2tfAeBYRJbBAdmGdlhiahEWulb4WbNYX7mgKgk4CaqMfxRbayyqsCnQg))IdiL6VbqOkLiJGq76F(4WON0yGkTJogyMST03qmtRkHtDwd98Q46kD6ov10GAbWvs6TXmg3xZv8f3dqp874G7qdzNkJ8aHiFdEh4MtBgFCljsySHQkvRre5sUb)q00kXyQQ4)35TnkGSFyYh(5K2n9CDzYbb8bmY2efd9M(vv(N0ricI(RoM5iw9q9MlFsxm7lw8hRhr4L8rmUWYegVmAZcEoI7CTV8E4XdrUsKA7eiJnJ8EjCOrbmQlFUJzalWkcsLgPvNFIoZ(fpRUadNz1x2vxJ6d7FFVcvNXTwC3I5UV(QXeOUBXuhTZKsYC5thHT1YnLPWa5a3XOvyco(7QvviwGqnZHAH752gk4DlCDE9vjn4xtlqPkHyt97(fUZmOtD(S1eCMPcaJuBgYPtnux2V(unus7rYUb4CK7rf2f3E(z2)GIu4UwSmbs3IioZ60JSFYpxDcz3Vy6mh76IRC6r5mhhtTZOUoZH1w6xZ4qiaNZvVnryhD(zi3I5RVAJ19o5oM8t9V5oWhJQrEg7DnH1E3TfG2DUng23H4G6WpASeiFKRYaZ1ZQZ0f2cfDJs1Y70d2A)7C1YYSm6T6yOh67B1cVWdFlPXmkU8D9dM)M0r9QSWuhcCxrd19fPossBJjQ8TNEHBJP2Y1dRIy6Uv5o8L2OXFdPCWwVLmoOK2DtUiKkozh33TQuvQ6pyGrn9BVF(LA39Wc3lnUaVfUOSFGCGtvZmzpD40jZq5SwirB1rmRjCMX39698KN2Uanmr1E)oCj1EjqI4z2teHKyb)Kil9tKQY2xY6iTF2Vj07wCZHYzEOJmxFY)evphj1XXYFl4cSpTgmwyBgROeBuqWXknT6ZpGsRTa(3G3Pc36c1HDT83lW4d86ARLwmYfZNAwGdU4pkx)JgvED9UFG7L)EhRS)egvrHR6tUrwXHwqIbQ0Q721T3xt)AiDDUEeBJ2IhnUlC5Ajm6Wz6RjaxPDVQvSMyRH8U2Ppzk25(qfRv80A67Uz7(m00pvtNDLJttOB1mIMfN3NT9MLdA1KNp4DgBOrBg3do7o7O4N5maKbn(BnHxZTh)On6BDbkqIJDKCpxpr(UlFIexsnBBq8sUP3yXjeyN66DLOY76v7o5Ql4HzBDMAc3QVu1q)nRtFRyK(OlwqpPErTRlNHWYlOEHGztaHuNBLXg)ej8opzzN3gcB9Vdct9doGuwSJLTA5YKYnzrpk(Odw9F)d]])