-- core.lua

local addonName = ...
local LSM = LibStub("LibSharedMedia-3.0")

CCS = CCS or {}

--------------------------------------------------
-- LSM Registration
--------------------------------------------------

local basePath = "Interface\\AddOns\\" .. addonName .. "\\sounds\\"

local lsmSounds = {
    "break","breath","burn","dot","marked","move","pools","soak","spread","targeted",
}

for _, name in ipairs(lsmSounds) do
    LSM:Register("sound", "CCS: " .. name, basePath .. name .. ".ogg")
end

-- Public countdown sounds (registered with CCS: prefix for other addons)
local publicCountdowns = {
    "2s","3s","4s","5s","6s","7s","8s","9s","10s","11s","12s","13s",
}
for _, name in ipairs(publicCountdowns) do
    LSM:Register("sound", "CCS: " .. name, basePath .. name .. ".ogg")
end

local soundPaths = {}
for _, name in ipairs(lsmSounds) do
    soundPaths[name] = basePath .. name .. ".ogg"
end

local function resolvePath(s)
    if not s then return nil end
    if s:sub(1, 5) == "file:" then
        return basePath .. s:sub(6) .. ".ogg"
    end
    if soundPaths[s] then return soundPaths[s] end
    return LSM:Fetch("sound", s)
end

--------------------------------------------------
-- Difficulty
--------------------------------------------------

local function getCurrentDifficulty()
    local _, _, difficultyID = GetInstanceInfo()
    if difficultyID == 16 then return "M"
    elseif difficultyID == 15 then return "H"
    end
    return nil
end

CCS.getCurrentDifficulty = getCurrentDifficulty

--------------------------------------------------
-- SavedVariables
-- CCS_DB = {
--   warnEnabled       = { [key] = true/false },
--   warnOverride      = { [key] = "soundKey" },
--   countdownEnabled  = { [key] = { H=bool, M=bool } },
--   countdownOverride = { [key] = { H="Xs", M="Xs" } },
-- }
--------------------------------------------------

function CCS.isWarnEnabled(key)
    if not CCS_DB or not CCS_DB.warnEnabled then return false end
    return CCS_DB.warnEnabled[key] == true
end

function CCS.SetWarnEnabled(key, val)
    CCS_DB.warnEnabled = CCS_DB.warnEnabled or {}
    CCS_DB.warnEnabled[key] = val
end

function CCS.GetWarnOverride(key)
    if not CCS_DB or not CCS_DB.warnOverride then return nil end
    return CCS_DB.warnOverride[key]
end

function CCS.SetWarnOverride(key, soundKey)
    CCS_DB.warnOverride = CCS_DB.warnOverride or {}
    CCS_DB.warnOverride[key] = soundKey
end

function CCS.IsCDEnabled(key, diff)
    if not CCS_DB or not CCS_DB.countdownEnabled then return false end
    local t = CCS_DB.countdownEnabled[key]
    return t and t[diff] == true or false
end

function CCS.SetCDEnabled(key, diff, val)
    CCS_DB.countdownEnabled = CCS_DB.countdownEnabled or {}
    CCS_DB.countdownEnabled[key] = CCS_DB.countdownEnabled[key] or {}
    CCS_DB.countdownEnabled[key][diff] = val
end

function CCS.GetCountdownOverride(key, diff)
    if not CCS_DB or not CCS_DB.countdownOverride then return nil end
    local t = CCS_DB.countdownOverride[key]
    return t and t[diff] or nil
end

function CCS.SetCountdownOverride(key, diff, soundKey)
    CCS_DB.countdownOverride = CCS_DB.countdownOverride or {}
    CCS_DB.countdownOverride[key] = CCS_DB.countdownOverride[key] or {}
    CCS_DB.countdownOverride[key][diff] = soundKey
end

function CCS.SetAllWarn(val)
    CCS_DB.warnEnabled = CCS_DB.warnEnabled or {}
    for _, entry in ipairs(CCS_Spells) do
        if entry.abilities then
            for _, ability in ipairs(entry.abilities) do
                CCS_DB.warnEnabled[ability.key] = val
            end
        end
    end
end

function CCS.SetAllCD(val)
    CCS_DB.countdownEnabled = CCS_DB.countdownEnabled or {}
    for _, entry in ipairs(CCS_Spells) do
        if entry.abilities then
            for _, ability in ipairs(entry.abilities) do
                local function hasDefault(diff)
                    local s = diff == "M" and ability.soundM or ability.soundH
                    return type(s) == "table" and s[2] ~= nil
                end
                if hasDefault("H") then
                    CCS_DB.countdownEnabled[ability.key] = CCS_DB.countdownEnabled[ability.key] or {}
                    CCS_DB.countdownEnabled[ability.key].H = val
                end
                if hasDefault("M") then
                    CCS_DB.countdownEnabled[ability.key] = CCS_DB.countdownEnabled[ability.key] or {}
                    CCS_DB.countdownEnabled[ability.key].M = val
                end
            end
        end
    end
end

--------------------------------------------------
-- Sound resolution
--------------------------------------------------

local function resolveAbilitySounds(ability, diff)
    local paths = {}

    if CCS.isWarnEnabled(ability.key) then
        local warnField = ability.soundH or ability.soundM
        local defaultWarn = type(warnField) == "table" and warnField[1] or warnField
        local warnKey = CCS.GetWarnOverride(ability.key) or defaultWarn
        local p = resolvePath(warnKey)
        if p then
            paths[#paths + 1] = p
        else
            print("|cffff9900CCS:|r Warning — could not resolve sound '" .. tostring(warnKey) .. "' for " .. ability.key)
        end
    end

    if CCS.IsCDEnabled(ability.key, diff) then
        local soundField = diff == "M" and ability.soundM or ability.soundH
        local defaultCD = type(soundField) == "table" and soundField[2] or nil
        local cdKey = CCS.GetCountdownOverride(ability.key, diff) or defaultCD
        local p = resolvePath(cdKey)
        if p then
            paths[#paths + 1] = p
        else
            print("|cffff9900CCS:|r Warning — could not resolve countdown sound '" .. tostring(cdKey) .. "' for " .. ability.key)
        end
    end

    return #paths > 0 and paths or nil
end

--------------------------------------------------
-- Private Aura Registration (all local)
--------------------------------------------------

local handles = {}

local function unregisterAbility(key)
    if not handles[key] then return end
    for _, id in ipairs(handles[key]) do
        C_UnitAuras.RemovePrivateAuraAppliedSound(id)
    end
    handles[key] = nil
end

local function registerAbility(ability, diff)
    if not ability or not ability.privateID then return end

    if not C_UnitAuras.AuraIsPrivate(ability.privateID) then
        -- Only warn in actual raid difficulties so we don't spam outside instances
        if diff then
            print("|cffff9900CCS:|r " .. ability.key .. " (spellID " .. ability.privateID .. ") is not a private aura — skipping.")
        end
        return
    end

    local paths = resolveAbilitySounds(ability, diff)
    if not paths then return end

    handles[ability.key] = {}
    for _, path in ipairs(paths) do
        local id = C_UnitAuras.AddPrivateAuraAppliedSound({
            unitToken     = "player",
            spellID       = ability.privateID,
            soundFileName = path,
            outputChannel = "master",
        })
        if id then
            handles[ability.key][#handles[ability.key] + 1] = id
        end
    end
end

local function unregisterAll()
    for key in pairs(handles) do
        unregisterAbility(key)
    end
end

local function registerAll()
    local diff = getCurrentDifficulty()
    if not diff then return end
    for _, entry in ipairs(CCS_Spells) do
        if entry.abilities then
            for _, ability in ipairs(entry.abilities) do
                registerAbility(ability, diff)
            end
        end
    end
end

--------------------------------------------------
-- Combat-safe refresh queue
--------------------------------------------------

local pendingRefreshAll = false
local pendingRefreshKeys = {}  -- { [key] = abilityRef }

local function flushPending()
    if pendingRefreshAll then
        pendingRefreshAll = false
        pendingRefreshKeys = {}
        unregisterAll()
        registerAll()
    else
        for key, ability in pairs(pendingRefreshKeys) do
            unregisterAbility(key)
            local diff = getCurrentDifficulty()
            if diff then registerAbility(ability, diff) end
        end
        pendingRefreshKeys = {}
    end
end

--------------------------------------------------
-- Public API
--------------------------------------------------

function CCS.RefreshAbility(key, ability)
    if InCombatLockdown() then
        pendingRefreshKeys[key] = ability
        return
    end
    unregisterAbility(key)
    local diff = getCurrentDifficulty()
    if diff then registerAbility(ability, diff) end
end

function CCS.RefreshAll()
    if InCombatLockdown() then
        pendingRefreshAll = true
        return
    end
    unregisterAll()
    registerAll()
end

function CCS.EnableAll()
    CCS.SetAllWarn(true)
    CCS.SetAllCD(true)
    CCS.RefreshAll()
end

function CCS.DisableAll()
    CCS.SetAllWarn(false)
    CCS.SetAllCD(false)
    CCS.RefreshAll()
end

--------------------------------------------------
-- Events
--------------------------------------------------

local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
eventFrame:RegisterEvent("ENCOUNTER_START")
eventFrame:RegisterEvent("ENCOUNTER_END")

eventFrame:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local name = ...
        if name ~= addonName then return end
        CCS_DB = CCS_DB or {}
        CCS_DB.warnEnabled       = CCS_DB.warnEnabled       or {}
        CCS_DB.warnOverride      = CCS_DB.warnOverride      or {}
        CCS_DB.countdownEnabled  = CCS_DB.countdownEnabled  or {}
        CCS_DB.countdownOverride = CCS_DB.countdownOverride or {}
        self:UnregisterEvent("ADDON_LOADED")

    elseif event == "PLAYER_ENTERING_WORLD" then
        unregisterAll()
        registerAll()

    elseif event == "PLAYER_REGEN_ENABLED" then
        -- Flush any changes the user made during combat
        if pendingRefreshAll or next(pendingRefreshKeys) then
            flushPending()
            print("|cffffff00CCS:|r Settings applied after combat.")
        end

    elseif event == "ENCOUNTER_START" then
        unregisterAll()
        registerAll()

    elseif event == "ENCOUNTER_END" then
        unregisterAll()
    end
end)