-- =============================================================
-- Specs/MonkMistweaver.lua
-- Nem: Healer Alerts — Mistweaver Monk spec module
--
-- FULLY SELF-CONTAINED. This file contains ALL Mistweaver Monk
-- spell IDs, aura scan logic, state management, evaluate logic,
-- event handling, glow evaluation, and options panel additions.
--
-- It has ZERO dependencies on any other spec module. Adding or
-- removing other spec files will never affect this module.
--
-- Registers itself with the core via NHA:RegisterSpec().
-- =============================================================

local NHA = _G.NemHealerAlerts
if not NHA then return end  -- core must load first

local pairs, ipairs       = pairs, ipairs
local math_floor          = math.floor
local string_format       = string.format
local GetTime             = GetTime
local UnitExists          = UnitExists
local UnitAffectingCombat = UnitAffectingCombat
local UnitGUID            = UnitGUID
local C_UnitAuras         = C_UnitAuras
local issecretvalue       = NHA.issecretvalue

-- =============================================================
-- Spell / Aura IDs
-- =============================================================
local SOOTHING_MIST_ID   = 115175
local RENEWING_MIST_ID   = 119611
local ENVELOPING_MIST_ID = 124682
local ASPECT_HARMONY_ID  = 450769

-- =============================================================
-- Module State (owned entirely by this module)
-- =============================================================
local state = {
    -- Renewing Mist low
    rmActive       = false,
    rmWasActive    = false,
    -- Enveloping missing (conditional)
    envActive      = false,
    envWasActive   = false,
    -- Soothing Mist channel tracking
    soothingUnit   = nil,
    soothingGUID   = nil,
    -- Debounce
    playerAuraPending   = false,
    rosterUpdatePending = false,
}

-- =============================================================
-- Glow Frame Tables (owned entirely by this module)
-- =============================================================
local soothingGlowFrames = {}  -- buffFrame → true

-- =============================================================
-- Alert Definitions
-- =============================================================
local REML_Y = -30
local ENV_Y  = -60

local ALERTS = {
    {
        key          = "rm",
        label        = "Renewing Mist Low",
        text         = "* RENEWING MIST LOW *",
        defaultColor = { r = 0.000, g = 1.000, b = 0.596 },  -- Monk class color
        hasTimer     = false,
        yOffset      = REML_Y,
        defaultSound = "Ping",
    },
    {
        key          = "env",
        label        = "Enveloping Mist",
        text         = "* ENVELOPING MIST *",
        defaultColor = { r = 0.000, g = 0.800, b = 0.478 },  -- Slightly darker Monk
        hasTimer     = false,
        yOffset      = ENV_Y,
        defaultSound = "Bell",
    },
}

-- =============================================================
-- Saved-Variable Defaults (merged into core db)
-- =============================================================
local DEFAULTS = {
    rmEnabled            = true,
    rmSoundEnabled       = true,
    rmSoundName          = "Ping",
    envEnabled           = true,
    envSoundEnabled      = true,
    envSoundName         = "Bell",
    soothingGlowEnabled  = true,
    rmThresholdDungeon   = 2,
    rmThresholdRaid      = 4,
    alertColors          = {
        rm   = { r = 0.000, g = 1.000, b = 0.596 },
        env  = { r = 0.000, g = 0.800, b = 0.478 },
    },
}

-- =============================================================
-- Renewing Mist Count
-- =============================================================
local function CountActiveRenewingMist()
    local count = 0
    for _, unit in ipairs(NHA:GetGroupUnits()) do
        if UnitExists(unit) and C_UnitAuras and C_UnitAuras.GetAuraDataBySpellID then
            local aura = C_UnitAuras.GetAuraDataBySpellID(unit, RENEWING_MIST_ID, "HELPFUL")
            if aura and aura.auraInstanceID ~= 0
               and aura.sourceUnit == "player" then
                count = count + 1
            end
        end
    end
    return count
end

local function GetRMThreshold()
    local db = NHA.db
    if IsInRaid and IsInRaid() then
        return db.rmThresholdRaid or 4
    end
    return db.rmThresholdDungeon or 2
end

-- =============================================================
-- Soothing Mist Channel Detection
-- =============================================================
-- Soothing Mist is a channel; the buff appears on the TARGET.
local function FindSoothingMistTarget()
    for _, unit in ipairs(NHA:GetGroupUnits()) do
        if UnitExists(unit) and C_UnitAuras and C_UnitAuras.GetAuraDataBySpellID then
            local aura = C_UnitAuras.GetAuraDataBySpellID(unit, SOOTHING_MIST_ID, "HELPFUL")
            if aura and aura.auraInstanceID ~= 0
               and aura.sourceUnit == "player" then
                return unit
            end
        end
    end
    return nil
end

-- Check if a unit has player's Enveloping Mist
local function UnitHasEnvelopingMist(unit)
    if not unit or not UnitExists(unit) then return false end
    if not C_UnitAuras or not C_UnitAuras.GetAuraDataBySpellID then return false end
    local aura = C_UnitAuras.GetAuraDataBySpellID(unit, ENVELOPING_MIST_ID, "HELPFUL")
    return aura and aura.auraInstanceID ~= 0 and aura.sourceUnit == "player"
end

-- =============================================================
-- MODULE TABLE
-- =============================================================
local MonkMW = {
    class     = "MONK",
    specIndex = 2,  -- Mistweaver is spec index 2
    specName  = "Mistweaver",
    alerts    = ALERTS,
    defaults  = DEFAULTS,
    state     = state,

    extraEvents = {
        "UNIT_AURA",
    },
}

-- =============================================================
-- ClearAllGlows
-- =============================================================
function MonkMW:ClearAllGlows()
    for buffFrame in pairs(soothingGlowFrames) do
        NHA:HideProcGlow(buffFrame)
    end
    wipe(soothingGlowFrames)
end

-- =============================================================
-- ClearAllState
-- =============================================================
function MonkMW:ClearAllState()
    state.rmActive          = false
    state.rmWasActive       = false
    state.envActive         = false
    state.envWasActive      = false
    state.soothingUnit      = nil
    state.soothingGUID      = nil
    state.playerAuraPending = false
    state.rosterUpdatePending = false
    self:ClearAllGlows()
end

-- =============================================================
-- NeedsTicker — keep ticker alive when Soothing glow is active
-- =============================================================
function MonkMW:NeedsTicker(db)
    return db.soothingGlowEnabled
end

-- =============================================================
-- RefreshGlows (called every 0.25s by ticker)
-- =============================================================
function MonkMW:RefreshGlows(db)
    -- Soothing Mist glow is static while channel exists
end

-- =============================================================
-- EvaluateFrameGlow (called from hook pipeline)
-- =============================================================
function MonkMW:EvaluateFrameGlow(db, buffFrame, aura)
    if not db or not db.glowEnabled or not db.enabled then
        if soothingGlowFrames[buffFrame] then
            NHA:HideProcGlow(buffFrame)
            soothingGlowFrames[buffFrame] = nil
        end
        return
    end

    local tracked = soothingGlowFrames[buffFrame]

    if not aura then
        if tracked then
            NHA:HideProcGlow(buffFrame)
            soothingGlowFrames[buffFrame] = nil
        end
        return
    end

    if issecretvalue(aura.spellId) then
        if tracked then
            NHA:HideProcGlow(buffFrame)
            soothingGlowFrames[buffFrame] = nil
        end
        return
    end

    if aura.sourceUnit ~= "player" then
        if tracked then
            NHA:HideProcGlow(buffFrame)
            soothingGlowFrames[buffFrame] = nil
        end
        return
    end

    -- ── Soothing Mist glow on channeled target ───────────────
    if aura.spellId == SOOTHING_MIST_ID and db.soothingGlowEnabled then
        soothingGlowFrames[buffFrame] = true
        local c = db.alertColors and db.alertColors.rm
                  or { r=0, g=1, b=0.596 }
        NHA:ShowProcGlow(buffFrame, c.r, c.g, c.b)
        return
    end

    -- ── Not a spell we care about ────────────────────────────
    if tracked then
        NHA:HideProcGlow(buffFrame)
        soothingGlowFrames[buffFrame] = nil
    end
end

-- =============================================================
-- Evaluate (called by core ticker and events)
-- =============================================================
function MonkMW:Evaluate(db)
    -- ── Renewing Mist low coverage (combat only) ─────────────
    local rmLow = false
    if db.rmEnabled and UnitAffectingCombat("player") then
        local count     = CountActiveRenewingMist()
        local threshold = GetRMThreshold()
        rmLow = count < threshold
    end
    state.rmActive = rmLow

    -- ── Enveloping Mist missing on Soothing Mist target ──────
    local envMissing = false
    if db.envEnabled then
        local soothUnit = FindSoothingMistTarget()
        state.soothingUnit = soothUnit
        if soothUnit then
            envMissing = not UnitHasEnvelopingMist(soothUnit)
        end
    end
    state.envActive = envMissing

    -- ── Rising-edge sounds ───────────────────────────────────
    if rmLow      and not state.rmWasActive  then NHA:PlayAlertSound("rm")  end
    if envMissing  and not state.envWasActive then NHA:PlayAlertSound("env") end
    state.rmWasActive  = rmLow
    state.envWasActive = envMissing
end

-- =============================================================
-- OnActivate
-- =============================================================
function MonkMW:OnActivate(db)
    -- Nothing spec-specific at login
end

-- =============================================================
-- OnDeactivate
-- =============================================================
function MonkMW:OnDeactivate(db)
    -- State is cleared by core calling ClearAllState + ClearAllGlows
end

-- =============================================================
-- OnEvent
-- =============================================================
function MonkMW:OnEvent(db, event, ...)
    if event == "UNIT_AURA" then
        local unit = ...
        if not state.playerAuraPending then
            state.playerAuraPending = true
            C_Timer.After(0.1, function()
                state.playerAuraPending = false
                NHA.Evaluate()
            end)
        end
        return true
    end

    if event == "GROUP_ROSTER_UPDATE" then
        if not state.rosterUpdatePending then
            state.rosterUpdatePending = true
            C_Timer.After(0.5, function()
                state.rosterUpdatePending = false
                NHA.Evaluate()
            end)
        end
        return true
    end

    return false
end

-- =============================================================
-- BuildOptions (spec-specific options in the Display section)
-- =============================================================
function MonkMW:BuildOptions(parent, db, helpers)
    helpers.MakeCheckbox("Soothing Mist Glow", 230, -90,
        function() return db.soothingGlowEnabled end,
        function(v) db.soothingGlowEnabled = v and true or false end)
end

-- =============================================================
-- Slash Commands (spec-specific)
-- =============================================================
MonkMW.slashCommands = {
    ["debug"] = function(self, db)
        NHA.Msg("|cffffd700Mistweaver Monk Debug:|r")
        NHA.Msg("  enabled: " .. tostring(db.enabled))
        NHA.Msg("  rmEnabled: " .. tostring(db.rmEnabled))
        NHA.Msg("  envEnabled: " .. tostring(db.envEnabled))
        NHA.Msg("  glowEnabled: " .. tostring(db.glowEnabled))
        NHA.Msg("  soothingGlowEnabled: " .. tostring(db.soothingGlowEnabled))
        NHA.Msg("  rmActive: " .. tostring(state.rmActive))
        NHA.Msg("  envActive: " .. tostring(state.envActive))
        NHA.Msg("  soothingUnit: " .. tostring(state.soothingUnit))
        NHA.Msg("  rmThresholdDungeon: " .. tostring(db.rmThresholdDungeon))
        NHA.Msg("  rmThresholdRaid: " .. tostring(db.rmThresholdRaid))

        -- Glow frame count
        local soothCount = 0
        for _ in pairs(soothingGlowFrames) do soothCount = soothCount + 1 end
        NHA.Msg("  soothingGlowFrames: " .. soothCount)

        -- Active Renewing Mist count
        local rmCount = CountActiveRenewingMist()
        local rmThreshold = GetRMThreshold()
        NHA.Msg("  activeRenewingMist: " .. rmCount .. " / threshold: " .. rmThreshold)

        -- Soothing Mist target check
        local soothTarget = FindSoothingMistTarget()
        NHA.Msg("  soothingMistTarget: " .. tostring(soothTarget))
        if soothTarget then
            local hasEnv = UnitHasEnvelopingMist(soothTarget)
            NHA.Msg("  hasEnvelopingMist: " .. tostring(hasEnv))
        end
    end,
}

-- =============================================================
-- Register with core
-- =============================================================
NHA:RegisterSpec("MONK_MW", MonkMW)
