if not select(2, ...).IS_LEGACY_CLIENT then return end -- HET-LEGACY-1207: 12.0.7 only
-------------------------------------------------------------------------------
-- SpatialParadox.lua
-- Detects Spatial Paradox via spell range polling (C_Spell.GetSpellInfo).
-- When a tracked spell's maxRange exceeds the threshold, the timer triggers.
-------------------------------------------------------------------------------
local addonName, ns = ...

local DISPLAY_ICON_SPELL = 406732

-- Key: specID, Value: { spellID, rangeThreshold }
-- Augmentation Evoker uses Prescience (Spatial only — not Innervate-eligible).
local SPEC_SPELLS = {
    [256]  = { 47540,  80 }, -- Discipline Priest: Penance
    [257]  = { 2050,   80 }, -- Holy Priest: Holy Word: Serenity
    [65]   = { 20473,  80 }, -- Holy Paladin: Holy Shock
    [270]  = { 115151, 80 }, -- Mistweaver Monk: Renewing Mist
    [105]  = { 774,    80 }, -- Restoration Druid: Rejuvenation
    [264]  = { 61295,  80 }, -- Restoration Shaman: Riptide
    [1468] = { 364343, 60 }, -- Preservation Evoker: Echo
    [1473] = { 409311, 60 }, -- Augmentation Evoker: Prescience
    [1467] = { 361469, 50 }, -- Devastation Evoker: Living Flame
}
ns.SPEC_SPELLS = SPEC_SPELLS

-- Innervate mana-cost check spells per spec.
-- Most specs use their single Spatial Paradox spell.
-- Holy Priest and Preservation Evoker use a 2nd spell for extra confirmation.
ns.INNERVATE_CHECK_SPELLS = {
    [256]  = { 47540 },          -- Disc Priest: Penance
    [257]  = { 2050, 33076 },    -- Holy Priest: Holy Word: Serenity, Prayer of Mending
    [65]   = { 20473 },          -- Holy Paladin: Holy Shock
    [270]  = { 115151 },         -- Mistweaver Monk: Renewing Mist
    [105]  = { 774 },            -- Resto Druid: Rejuvenation
    [264]  = { 61295 },          -- Resto Shaman: Riptide
    [1468] = { 364343, 361469 }, -- Preservation Evoker: Echo, Living Flame
}

local C_Spell_GetSpellInfo = C_Spell.GetSpellInfo
local GetSpecialization = GetSpecialization
local GetSpecializationInfo = GetSpecializationInfo

-- Per-instance state set during onSpecChanged
local trackedSpellID = nil
local trackedRangeThreshold = 80
local lastTriggerTime = 0
local GRACE_PERIOD = 15

ns.RegisterTracker("spatial", {
    dbPrefix         = "",
    displaySpell     = DISPLAY_ICON_SPELL,
    frameSuffix      = "SpatialParadox",
    label            = "Spatial Tracker",
    defaultPos       = { "CENTER", "CENTER", 0, 0 },

    ttsPhrase = "Spatial",

    defaults = {
        enabled          = true,
        dpsEvokerEnabled = true,
        iconSize         = 40,
        timerDuration    = 10,
        trackerStyle     = "icon",
        cooldownSwipe    = true,
        barCircleColor   = { 0.9, 0.75, 0.1, 1.0 },
        barOrientation   = "horizontal",
        barWidth         = 200,
        barHeight        = 20,
        circleRadius     = 80,
        ringTexture      = "Ring_20px",
        position         = nil,
        alertMode        = "none",
        soundName        = "None",
        soundChannel     = "Master",
        ttsVoice         = 0,
        ttsVolume        = 100,
    },

    hasConfigurableDuration = true,
    durationMin = 10,
    durationMax = 15,

    getTimerDuration = function(tracker)
        return tracker:GetDBVal("timerDuration")
    end,

    onSpecChanged = function(tracker)
        trackedSpellID = nil
        trackedRangeThreshold = 80

        local specIndex = GetSpecialization()
        if not specIndex then return end

        local specID = GetSpecializationInfo(specIndex)
        if specID and SPEC_SPELLS[specID] then
            -- DPS Evoker specs (Augmentation 1473, Devastation 1467) can be
            -- opted out via the "Enable for DPS Evoker" toggle.
            if (specID == 1473 or specID == 1467)
               and not tracker:GetDBVal("dpsEvokerEnabled") then
                return
            end
            trackedSpellID = SPEC_SPELLS[specID][1]
            trackedRangeThreshold = SPEC_SPELLS[specID][2]
        end
    end,

    -- True when the current spec has a Spatial Paradox spell mapped.
    -- Used by Core to skip the poll ticker entirely on non-healer alts.
    isApplicable = function(tracker)
        return trackedSpellID ~= nil
    end,

    isActive = function(tracker)
        if not trackedSpellID then return false end
        -- C_Spell.GetSpellInfo allocates a fresh table each call; we extract
        -- maxRange and let it become immediate garbage. No field-chain
        -- re-reads, no upvalue retention.
        local info = C_Spell_GetSpellInfo(trackedSpellID)
        local maxRange = info and info.maxRange or 0
        return maxRange >= trackedRangeThreshold
    end,

    poll = function(tracker)
        if not trackedSpellID then return false end
        local now = GetTime()
        if now - lastTriggerTime < GRACE_PERIOD then return false end
        local info = C_Spell_GetSpellInfo(trackedSpellID)
        local maxRange = info and info.maxRange or 0
        if maxRange >= trackedRangeThreshold then
            lastTriggerTime = now
            return true
        end
        return false
    end,
})

ns.DISPLAY_ICON_SPELL = DISPLAY_ICON_SPELL
