local NS = rawget(_G, "MCDVoiceCooldownNS") or {}
_G.MCDVoiceCooldownNS = NS

NS.Core = NS.Core or {}
NS.Core.Notifier = NS.Core.Notifier or {}
NS.Notifier = NS.Core.Notifier

local Notifier = NS.Core.Notifier
local CONST = NS.Constants or {}
local Utils = NS.Utils or {}

local MODE_TTS = CONST.MODE_TTS or "tts"
local MODE_SOUND = CONST.MODE_SOUND or "sound"
local TextToSpeech_SpeakText = rawget(_G, "TextToSpeech_SpeakText")

local function L(key, ...)
    if type(NS.L) == "function" then
        return NS.L(key, ...)
    end
    if select("#", ...) > 0 then
        return string.format(tostring(key), ...)
    end
    return tostring(key)
end

local function TrimText(value)
    if Utils.TrimText then
        return Utils.TrimText(value)
    end
    return tostring(value or ""):match("^%s*(.-)%s*$")
end

function Notifier:GetSelectedVoiceID()
    if C_TTSSettings and C_TTSSettings.GetVoiceOptionID then
        local ok, voiceID = pcall(C_TTSSettings.GetVoiceOptionID)
        if ok and tonumber(voiceID) and tonumber(voiceID) >= 0 then
            return tonumber(voiceID)
        end
    end

    if C_VoiceChat and C_VoiceChat.GetTtsVoices then
        local ok, voices = pcall(C_VoiceChat.GetTtsVoices)
        if ok and type(voices) == "table" then
            for _, voice in ipairs(voices) do
                local voiceID = tonumber(type(voice) == "table" and (voice.voiceID or voice[1])) or -1
                if voiceID >= 0 then
                    return voiceID
                end
            end
        end
    end

    return nil
end

function Notifier:SpeakTextTTS(text, rate)
    text = TrimText(text)
    if text == "" then
        return false
    end

    rate = math.max(-10, math.min(10, tonumber(rate) or 0))

    local voiceID = self:GetSelectedVoiceID()
    if not voiceID then
        if DEFAULT_CHAT_FRAME then
            DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_NO_TTS"))
        end
        return false
    end

    if C_VoiceChat and C_VoiceChat.SpeakText then
        local ok = pcall(C_VoiceChat.SpeakText, voiceID, text, rate, 100, false)
        if ok then
            return true
        end
    end

    if type(TextToSpeech_SpeakText) == "function" then
        local ok = pcall(TextToSpeech_SpeakText, text)
        if ok then
            return true
        end
    end

    if DEFAULT_CHAT_FRAME then
        DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_TTS_FAILED", text))
    end
    return false
end

function Notifier:NormalizeSoundPath(path)
    path = TrimText(path)
    if path == "" then
        return ""
    end

    if NS.MediaCatalog and type(NS.MediaCatalog.NormalizeSoundPath) == "function" then
        return NS.MediaCatalog:NormalizeSoundPath(path)
    end

    if NS.AceOptions and type(NS.AceOptions.NormalizeSoundPath) == "function" then
        return NS.AceOptions:NormalizeSoundPath(path)
    end

    if Utils.CanonicalPath then
        return Utils.CanonicalPath(path)
    end
    return (path:gsub("/", "\\"))
end

function Notifier:ResolveEntrySoundPath(entry)
    if type(entry) == "table" then
        local soundSource = tostring(entry.soundSource or "")
        if soundSource == "tts" then
            return ""
        end
        if NS.AceOptions and type(NS.AceOptions.ResolveSharedMediaSoundPath) == "function" then
            local sharedMediaSound = TrimText(entry.sharedMediaSound or entry.sharedMediaName or "")
            if soundSource == "sharedmedia" and sharedMediaSound ~= "" then
                return NS.AceOptions:ResolveSharedMediaSoundPath(sharedMediaSound, entry.soundPath or "")
            end
        end
    end
    return self:NormalizeSoundPath(type(entry) == "table" and entry.soundPath or entry)
end

function Notifier:PlayVoiceFile(path)
    path = self:NormalizeSoundPath(path)
    if path == "" then
        return false
    end

    if type(PlaySoundFile) ~= "function" then
        if DEFAULT_CHAT_FRAME then
            DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_NO_CUSTOM_AUDIO"))
        end
        return false
    end

    -- PlaySoundFile returns true only when the sound is actually queued.
    -- nil means it did not play, so do not treat nil as success.
    local ok, willPlay = pcall(PlaySoundFile, path, "Master")
    if ok and willPlay == true then
        return true
    end

    if DEFAULT_CHAT_FRAME then
        DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_SOUND_FAILED", path))
    end
    return false
end

function Notifier:GetRandomNonEmptyPath(paths)
    local candidates = {}
    if type(paths) ~= "table" then
        return nil
    end

    for _, value in ipairs(paths) do
        value = self:NormalizeSoundPath(value)
        if value ~= "" then
            candidates[#candidates + 1] = value
        end
    end

    if #candidates <= 0 then
        return nil
    end

    if #candidates == 1 then
        return candidates[1]
    end

    return candidates[math.random(1, #candidates)]
end

function Notifier:PlayReadyNotification(cfg)
    if type(cfg) ~= "table" then
        return false
    end

    local mode = tostring(cfg.notifyMode or MODE_SOUND)
    if mode == MODE_TTS then
        local text = TrimText(cfg.ttsText or "")
        if text == "" then
            text = (tostring(cfg.spellName or "") ~= "" and tostring(cfg.spellName) or tostring(cfg.spellId or "")) ..
                L("TTS_READY_DEFAULT")
        end
        return self:SpeakTextTTS(text, cfg.ttsRate)
    end

    local path = self:ResolveEntrySoundPath(cfg)
    if path == "" then
        if DEFAULT_CHAT_FRAME then
            DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_NO_SOUND_PATH"))
        end
        return false
    end
    return self:PlayVoiceFile(path)
end

function Notifier:PlayCastSuccessNotification(cfg)
    if type(cfg) ~= "table" then
        return false
    end

    local mode = tostring(cfg.notifyMode or MODE_SOUND)
    if mode == MODE_TTS then
        local text = TrimText(cfg.ttsText or "")
        if text == "" then
            text = (tostring(cfg.spellName or "") ~= "" and tostring(cfg.spellName) or tostring(cfg.spellId or "")) ..
                L("TTS_CAST_SUCCESS_DEFAULT")
        end
        return self:SpeakTextTTS(text, cfg.ttsRate)
    end

    local path = self:ResolveEntrySoundPath(cfg)
    if path == "" then
        if DEFAULT_CHAT_FRAME then
            DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_NO_CAST_SOUND_PATH"))
        end
        return false
    end
    return self:PlayVoiceFile(path)
end

function Notifier:PlayBloodlustNotification(cfg, fallbackPaths)
    local paths = type(cfg) == "table" and cfg.customSoundPaths or fallbackPaths
    local path = self:GetRandomNonEmptyPath(paths)
    if not path then
        if DEFAULT_CHAT_FRAME then
            DEFAULT_CHAT_FRAME:AddMessage((NS.ADDON_CHAT_PREFIX or "") .. " " .. L("MSG_NO_BLOODLUST_SOUND_PATH"))
        end
        return false
    end
    return self:PlayVoiceFile(path)
end
