setfenv(1, VoiceOver)
Options = {}

local AceGUI = LibStub("AceGUI-3.0")
local AceConfigDialog = LibStub("AceConfigDialog-3.0")
local AceDBOptions = LibStub("AceDBOptions-3.0")

-- ============================================================
-- VALORES PREDETERMINADOS CENTRALIZADOS PARA ALTERNATIVEPORTRAIT
-- Todos los lugares deben usar estas constantes
-- ============================================================
AlternativePortraitDefaults = {
    -- Posiciones para eventos normales (Quests, Gossip, etc.)
    normal = {
        title = { x = -93, y = 1 },
        desc = { x = -40, y = -67 },
        icon = { x = -62, y = -16 },
        model = { x = -229, y = 1, size = 136 },
    },
    -- Posiciones para Gossip, Items, Dialogues y GameObjects
    itemsAndDialogues = {
        title = { x = -97, y = 16 },
        desc = { x = -68, y = -46 },
    },
}

------------------------------------------------------------
-- Construction of the options table for AceConfigDialog --

local function SortAceConfigOptions(a, b)
    return (a.order or 100) < (b.order or 100)
end

-- Needed to preserve order (modern AceGUI has support for custom sorting of dropdown items, but old versions don't)
local FRAME_STRATAS =
{
    "BACKGROUND",
    "LOW",
    "MEDIUM",
    "HIGH",
    "DIALOG",
}

local slashCommandsHandler = {}
function slashCommandsHandler:values(info)
    if not self.indexToName then
        self.indexToName = { "Nothing" }
        self.indexToCommand = { "" }
        self.commandToIndex = { [""] = 1 }
        for command, handler in Utils:Ordered(Options.table.args.SlashCommands.args, SortAceConfigOptions) do
            if not handler.dropdownHidden then
                table.insert(self.indexToName, handler.name)
                table.insert(self.indexToCommand, command)
                self.commandToIndex[command] = getn(self.indexToCommand)
            end
        end
    end
    return self.indexToName
end

function slashCommandsHandler:get(info)
    local config, key = info.arg()
    return self.commandToIndex[config[key]]
end

function slashCommandsHandler:set(info, value)
    local config, key = info.arg()
    config[key] = self.indexToCommand[value]
end

-- General Tab
---@type AceConfigOptionsTable
local GeneralTab =
{
    name = "General",
    type = "group",
    order = 10,
    args = {
        MinimapButton = {
            type = "group",
            order = 2,
            inline = true,
            name = "Minimap Button",
            args = {
                MinimapButtonShow = {
                    type = "toggle",
                    order = 1,
                    name = "Show Minimap Button",
                    get = function(info) return not Addon.db.profile.MinimapButton.LibDBIcon.hide end,
                    set = function(info, value)
                        Addon.db.profile.MinimapButton.LibDBIcon.hide = not value
                        if value then
                            LibStub("LibDBIcon-1.0"):Show("VoiceOver")
                        else
                            LibStub("LibDBIcon-1.0"):Hide("VoiceOver")
                        end
                    end,
                },
                MinimapButtonLock = {
                    type = "toggle",
                    order = 2,
                    name = "Lock Position",
                    get = function(info) return Addon.db.profile.MinimapButton.LibDBIcon.lock end,
                    set = function(info, value)
                        if value then
                            LibStub("LibDBIcon-1.0"):Lock("VoiceOver")
                        else
                            LibStub("LibDBIcon-1.0"):Unlock("VoiceOver")
                        end
                    end,
                },
                LineBreak1 = { type = "description", name = "", order = 3 },
                MinimapButtons = {
                    type = "group",
                    inline = true,
                    name = "",
                    handler = slashCommandsHandler,
                    args = {
                        MinimapButtonLeftClick = {
                            type = "select",
                            order = 4,
                            name = "Left Click",
                            desc = "Action performed by left-clicking the minimap button.",
                            values = "values",
                            get = "get",
                            set = "set",
                            arg = function(value) return Addon.db.profile.MinimapButton.Commands, "LeftButton" end,
                        },
                        MinimapButtonMiddleClick = {
                            type = "select",
                            order = 4,
                            name = "Middle Click",
                            desc = "Action performed by middle-clicking the minimap button.",
                            values = "values",
                            get = "get",
                            set = "set",
                            arg = function(value) return Addon.db.profile.MinimapButton.Commands, "MiddleButton" end,
                        },
                        MinimapButtonRightClick = {
                            type = "select",
                            order = 4,
                            name = "Right Click",
                            desc = "Action performed by right-clicking the minimap button.",
                            values = "values",
                            get = "get",
                            set = "set",
                            arg = function(value) return Addon.db.profile.MinimapButton.Commands, "RightButton" end,
                        }
                    }
                }
            }
        },
        Frame = {
            type = "group",
            order = 3,
            inline = true,
            name = "Frame",
            disabled = function(info) return Addon.db.profile.SoundQueueUI.HideFrame end,
            args = {
                LockFrame = {
                    type = "toggle",
                    order = 1,
                    name = "Lock Frame",
                    desc = "Prevent the frame from being moved or resized.",
                    get = function(info) return Addon.db.profile.SoundQueueUI.LockFrame end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.LockFrame = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                ResetFrame = {
                    type = "execute",
                    order = 2,
                    name = "Reset Frame",
                    desc = "Resets frame position and size back to default.",
                    func = function(info)
                        SoundQueueUI.frame:Reset()
                    end,
                },
                LineBreak1 = { type = "description", name = "", order = 3 },
                FrameStrata = {
                    type = "select",
                    order = 5,
                    name = "Frame Strata",
                    desc =
                    "Changes the \"depth\" of the frame, determining which other frames will it overlap or fall behind.",
                    values = FRAME_STRATAS,
                    get = function(info)
                        for k, v in ipairs(FRAME_STRATAS) do
                            if v == Addon.db.profile.SoundQueueUI.FrameStrata then
                                return k;
                            end
                        end
                    end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.FrameStrata = FRAME_STRATAS[value]
                        SoundQueueUI.frame:SetFrameStrata(Addon.db.profile.SoundQueueUI.FrameStrata)
                    end,
                },
                FrameScale = {
                    type = "range",
                    order = 4,
                    name = "Frame Scale",
                    softMin = 0.5,
                    softMax = 2,
                    bigStep = 0.05,
                    isPercent = true,
                    get = function(info) return Addon.db.profile.SoundQueueUI.FrameScale end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.FrameScale = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                LineBreak2 = { type = "description", name = "", order = 6 },
                HidePortrait = {
                    type = "toggle",
                    order = 7,
                    name = "Hide NPC Portrait",
                    desc = "Talking NPC portrait will not appear when voice over audio is played.\n\n" ..
                        Utils:ColorizeText(
                            "This might be useful when using other addons that replace the dialog experience, such as " ..
                            Utils:ColorizeText("Immersion", NORMAL_FONT_COLOR_CODE) .. ".",
                            GRAY_FONT_COLOR_CODE),
                    get = function(info) return Addon.db.profile.SoundQueueUI.HidePortrait end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.HidePortrait = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                HideFrame = {
                    type = "toggle",
                    order = 8,
                    name = "Hide Entirely",
                    desc = "Play voiceovers without ever displaying the frame.",
                    disabled = false,
                    get = function(info) return Addon.db.profile.SoundQueueUI.HideFrame end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.HideFrame = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
            }
        },
        AdvancedPlayback = {
            type = "group",
            order = 3.5,
            inline = true,
            name = function()
                local iconString = "|TInterface\\AddOns\\AI_VoiceOver\\Textures\\UI-ChatIcon-Share:14:14:0:0|t"
                return iconString .. " Advanced Playback"
            end,
            args = {
                ObjectiveTrackerButton = {
                    type = "toggle",
                    order = 1,
                    name = "Show Objective Tracker Buttons",
                    desc = "Toggle visibility of play buttons in the Objective Tracker",
                    get = function(info) return Addon.db.profile.ShowObjectiveTrackerButtons end,
                    set = function(info, value)
                        Addon.db.profile.ShowObjectiveTrackerButtons = value
                        QuestOverlayUI:UpdateAllButtonsVisibility()
                    end,
                },
                ManualMode = {
                    type = "toggle",
                    order = 2,
                    name = "Play Manual Mode",
                    desc = "Disable automatic playback when interacting with NPCs",
                    get = function(info) return Addon.db.profile.ManualMode end,
                    set = function(info, value)
                        Addon.db.profile.ManualMode = value
                        Debug:Print("ManualMode set to: " .. tostring(value), "Options")
                    end,
                },
                ShowQuestFrameButton = {
                    type = "toggle",
                    order = 3,
                    name = "Show Quest Frame Button",
                    desc = "Show a button below Accept/Complete in the quest frame to manually play quest text",
                    get = function(info) return Addon.db.profile.ShowQuestFrameButton end,
                    set = function(info, value)
                        Addon.db.profile.ShowQuestFrameButton = value
                        if value then
                            -- Si se activa la opción, inicializamos el botón si aún no existe
                            if not QuestFrameAudioButton.playButton then
                                QuestFrameAudioButton:Initialize()
                            end
                        end
                        -- Actualizamos la visibilidad del botón
                        if QuestFrameAudioButton and QuestFrameAudioButton.UpdateButtonVisibility then
                            QuestFrameAudioButton:UpdateButtonVisibility()
                        else
                            DebugPrint("QuestFrameAudioButton no está disponible o no tiene UpdateButtonVisibility")
                        end
                    end,
                },
                EnableSoundControl = {
                    type = "toggle",
                    order = 4,
                    name = "Enable Sound Control",
                    desc = "Enable automatic volume adjustment for other sound channels when VoiceOver is playing",
                    get = function(info) return Addon.db.profile.EnableSoundControl end,
                    set = function(info, value)
                        Addon.db.profile.EnableSoundControl = value
                        if value then
                            Debug:Print("Sound Control Enabled", "SoundControlUI")
                            -- Aquí puedes añadir lógica adicional si es necesario
                        else
                            Debug:Print("Sound Control Disabled", "SoundControlUI")
                            -- Restaurar volúmenes si está deshabilitado
                            if SoundControlUI and SoundControlUI.RestoreVolumes then
                                SoundControlUI:RestoreVolumes()
                            end
                        end
                    end,
                },
            },
        },
        Audio = {
            type = "group",
            order = 4,
            inline = true,
            name = "Audio",
            args = {
                SoundChannel = {
                    type = "select",
                    width = 0.75,
                    order = 1,
                    name = "Sound Channel",
                    desc = "Controls which sound channel VoiceOver will play in.",
                    values = Enums.SoundChannel:GetValueToNameMap(),
                    get = function(info) return Addon.db.profile.Audio.SoundChannel end,
                    set = function(info, value)
                        Addon.db.profile.Audio.SoundChannel = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                LineBreak = { type = "description", name = "", order = 2 },
                GossipFrequency = {
                    type = "select",
                    width = 1.1,
                    order = 3,
                    name = "NPC Greeting Playback Frequency",
                    desc = "Controls how often VoiceOver will play NPC greeting dialog.",
                    values = {
                        [Enums.GossipFrequency.Always] = "Always",
                        [Enums.GossipFrequency.OncePerQuestNPC] = "Play Once for Quest NPCs",
                        [Enums.GossipFrequency.OncePerNPC] = "Play Once for All NPCs",
                        [Enums.GossipFrequency.Never] = "Never",
                    },
                    get = function(info) return Addon.db.profile.Audio.GossipFrequency end,
                    set = function(info, value)
                        Addon.db.profile.Audio.GossipFrequency = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                AutoToggleDialog = {
                    type = "toggle",
                    width = 2.25,
                    order = 4,
                    name = "Mute Vocal NPCs Greetings While VoiceOver is Playing",
                    desc = "While VoiceOver is playing, the Dialog channel will be muted.",
                    disabled = function()
                        return Addon.db.profile.Audio.SoundChannel == Enums.SoundChannel.Dialog
                    end,
                    get = function(info) return Addon.db.profile.Audio.AutoToggleDialog end,
                    set = function(info, value)
                        Addon.db.profile.Audio.AutoToggleDialog = value
                        SoundQueueUI:RefreshConfig()
                        if Addon.db.profile.Audio.AutoToggleDialog then
                            SetCVar("Sound_EnableDialog", 1)
                        end
                    end,
                },
                LineBreak2 = { type = "description", name = "", order = 5 },
                ToggleSyncToWindowState = {
                    type = "toggle",
                    order = 6,
                    width = 2,
                    name = "Sync Dialog to Window State",
                    desc = "VoiceOver dialog will automatically stop when the gossip/quest window is closed.",
                    get = function(info) return Addon.db.profile.Audio.StopAudioOnDisengage end,
                    set = function(info, value)
                        Addon.db.profile.Audio.StopAudioOnDisengage = value
                    end,
                },
            }
        },
        QuestProgressModule = {
            type = "group",
            order = 4.5,
            name = "Quest Progress",
            inline = true,
            args = {
                EnableModule = {
                    type = "toggle",
                    order = 1,
                    width = 1.50,
                    name = "Enable Quest Progress",
                    desc =
                    "Enables or disables the module that reads the quest progress text when you reach 50% of a quest.",
                    get = function(info) return Addon.db.profile.QuestProgressEnabled end,
                    set = function(info, value)
                        Addon.db.profile.QuestProgressEnabled = value
                        if value then
                            VoiceOver.MissionProgress.Activate()
                            Addon:UnregisterEvent("QUEST_PROGRESS")
                        else
                            Addon.db.profile.QuestProgressDebugEnabled = false
                            VoiceOver.MissionProgress.Deactivate()
                            Addon:RegisterEvent("QUEST_PROGRESS")
                        end
                    end,
                },
                QuestProgressDebugEnabled = {
                    type = "toggle",
                    order = 2,
                    width = 0.75,
                    name = "DebugMode",
                    desc = "Enables debug messages specifically for Quest Progress module.",
                    disabled = function() return not Addon.db.profile.QuestProgressEnabled end,
                    get = function(info) return Addon.db.profile.QuestProgressDebugEnabled end,
                    set = function(info, value)
                        Addon.db.profile.QuestProgressDebugEnabled = value
                        if value then
                            Debug:Print(Utils:ColorizeText("Debug Mode for Quest Progress Activated", RED_FONT_COLOR_CODE), "Options")
                        else
                            Debug:Print(Utils:ColorizeText("Debug Mode for Quest Progress Deactivated", RED_FONT_COLOR_CODE), "Options")
                        end
                    end,
                },
                Description = {
                    type = "description",
                    order = 3,
                    name =
                    "\nThe Quest Progress module reads the progress text when you reach 50% of a quest, no matter where you are.\n",
                },
            },
        },
        RareAnnounceModule = {
            type = "group",
            order = 4.6,
            name = "Rare Announce",
            inline = true,
            args = {
                EnableModule = {
                    type = "toggle",
                    order = 1,
                    width = 1.50,
                    name = "Enable Rare Announce",
                    desc = "Enable or disable the narrator that alerts you when a rare is nearby.",
                    get = function(info) return Addon.db.profile.RareAnnounceEnabled end,
                    set = function(info, value)
                        Addon.db.profile.RareAnnounceEnabled = value
                        if value then
                            VoiceOver.RareAnnounce.Activate()
                        else
                            Addon.db.profile.RareAnnounceDebugEnabled = false
                            VoiceOver.RareAnnounce.Deactivate()
                        end
                    end,
                },
                RareAnnounceDebugEnabled = {
                    type = "toggle",
                    order = 2,
                    width = 0.75,
                    name = "DebugMode",
                    desc = "Enables debug messages specifically for Rare Announce module.",
                    disabled = function() return not Addon.db.profile.RareAnnounceEnabled end,
                    get = function(info) return Addon.db.profile.RareAnnounceDebugEnabled end,
                    set = function(info, value)
                        Addon.db.profile.RareAnnounceDebugEnabled = value
                        if value then
                            Debug:Print(Utils:ColorizeText("Debug Mode for Rare Announce Activated", RED_FONT_COLOR_CODE), "Options")
                        else
                            Debug:Print(Utils:ColorizeText("Debug Mode for Rare Announce Deactivated", RED_FONT_COLOR_CODE), "Options")
                        end
                        isDebugEnabled = value
                    end,
                },
                Description = {
                    type = "description",
                    order = 3,
                    name =
                    "\nThe Rare Announce module alerts you when a rare is nearby, enhancing your exploration experience.\n",
                },
            },
        },
        Debug = {
            type = "group",
            order = 5,
            inline = true,
            name = "Debugging Tools",
            args = {
                DebugEnabled = {
                    type = "toggle",
                    order = 1,
                    width = 1.25,
                    name = "Enable Debug Messages",
                    desc = "Enables printing of some \"useful\" debug messages to the chat window.",
                    get = function(info) return Addon.db.profile.DebugEnabled end,
                    set = function(info, value) Addon.db.profile.DebugEnabled = value end,
                }
            }
        }
    }
}

-- Visual Effects Tab
---@type AceConfigOptionsTable
local VisualEffectsTab = {
    name = "Visual Effects",
    type = "group",
    order = 15,
    args = {
        PortraitStyleGroup = {
            type = "group",
            order = 0,
            inline = true,
            name = "Portrait Style",
            args = {
                PortraitStyle = {
                    type = "select",
                    order = 1,
                    width = 1.5,
                    name = "Portrait Style",
                    desc = "Choose between the classic 3D portrait or the new Midnight style frame.",
                    values = {
                        [0] = "Classic (3D Portrait)",
                        [1] = "Midnight (Beta)",
                    },
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitStyle or 0 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitStyle = value
                        SoundQueueUI:RefreshConfig()
                        if AlternativePortrait then
                            AlternativePortrait:RefreshConfig()
                        end
                    end,
                },
                StyleDescription = {
                    type = "description",
                    order = 2,
                    name = "\n|cff888888Classic: Shows the NPC 3D model in a circular frame.\nMidnight: Shows a cinematic-style frame with quest info and logo.|r\n",
                },
            }
        },
        MidnightControls = {
            type = "group",
            order = 0.5,
            inline = true,
            name = "Midnight Style Position Controls",
            hidden = function(info) return Addon.db.profile.SoundQueueUI.PortraitStyle ~= 1 end,
            args = {
                InfoText = {
                    type = "description",
                    order = 0,
                    name = "|cff00ff00Adjust the position of all Midnight style elements. You can also drag elements in-game with left-click.|r\n",
                },
                ResetButton = {
                    type = "execute",
                    order = 1,
                    name = "Reset All Positions",
                    desc = "Reset all element positions to default",
                    func = function()
                        Addon.db.profile.AlternativePortrait = {}
                        local defaults = AlternativePortraitDefaults.normal
                        if AlternativePortrait and AlternativePortrait.frame then
                            -- Reset main frame
                            AlternativePortrait.frame:ClearAllPoints()
                            AlternativePortrait.frame:SetPoint("CENTER", UIParent, "CENTER", 0, -150)
                            
                            -- Reset model frame
                            if AlternativePortrait.frame.modelFrame then
                                AlternativePortrait.frame.modelFrame:ClearAllPoints()
                                AlternativePortrait.frame.modelFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", defaults.model.x, defaults.model.y)
                                AlternativePortrait.frame.modelFrame:SetSize(defaults.model.size, defaults.model.size)
                            end
                            
                            -- Reset title frame
                            if AlternativePortrait.frame.titleFrame then
                                AlternativePortrait.frame.titleFrame:ClearAllPoints()
                                AlternativePortrait.frame.titleFrame:SetPoint("LEFT", AlternativePortrait.frame, "CENTER", defaults.title.x, defaults.title.y)
                                AlternativePortrait.frame.titleFrame:SetSize(330, 60)
                            end
                            
                            -- Reset description frame
                            if AlternativePortrait.frame.descFrame then
                                AlternativePortrait.frame.descFrame:ClearAllPoints()
                                AlternativePortrait.frame.descFrame:SetPoint("LEFT", AlternativePortrait.frame, "CENTER", defaults.desc.x, defaults.desc.y)
                                AlternativePortrait.frame.descFrame:SetSize(280, 120)
                            end
                            
                            -- Reset icon frame
                            if AlternativePortrait.frame.iconFrame then
                                AlternativePortrait.frame.iconFrame:ClearAllPoints()
                                AlternativePortrait.frame.iconFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", defaults.icon.x, defaults.icon.y)
                                AlternativePortrait.frame.iconFrame:SetSize(26, 26)
                            end
                            
                            -- Reset logo frame
                            if AlternativePortrait.frame.logoFrame then
                                AlternativePortrait.frame.logoFrame:ClearAllPoints()
                                AlternativePortrait.frame.logoFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", -234, -71)
                                AlternativePortrait.frame.logoFrame:SetSize(126, 126)
                            end
                            
                            -- Reset background frame
                            if AlternativePortrait.frame.bgFrame then
                                AlternativePortrait.frame.bgFrame:ClearAllPoints()
                                AlternativePortrait.frame.bgFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", -229, 0)
                                AlternativePortrait.frame.bgFrame:SetSize(150, 150)
                            end

                            Debug:Print(Utils:ColorizeText("Midnight style positions reset to default", GREEN_FONT_COLOR_CODE), "VoiceOver")
                        end
                    end,
                },
                LineBreak0 = { type = "description", name = "", order = 2 },
                
                -- General Settings Header
                GeneralHeader = {
                    type = "header",
                    order = 5,
                    name = "General Settings",
                },
                FrameScale = {
                    type = "range",
                    order = 6,
                    name = "Frame Scale",
                    desc = "Scale the entire Midnight frame",
                    min = 0.5,
                    max = 2.0,
                    step = 0.05,
                    isPercent = true,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.frameScale) or 1.0
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.frameScale = value
                        if AlternativePortrait and AlternativePortrait.frame then
                            AlternativePortrait.frame:SetScale(value)
                        end
                    end,
                },
                ShowLogo = {
                    type = "toggle",
                    order = 7,
                    name = "Show Logo",
                    desc = "Show or hide the VoiceOver logo",
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        if config and config.showLogo ~= nil then
                            return config.showLogo
                        end
                        return true -- Default: show logo
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.showLogo = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.logoFrame then
                            if value then
                                AlternativePortrait.frame.logoFrame:Show()
                            else
                                AlternativePortrait.frame.logoFrame:Hide()
                            end
                        end
                    end,
                },
                LineBreak1 = { type = "description", name = "", order = 9 },
                
                -- Model 3D Controls
                ModelHeader = {
                    type = "header",
                    order = 10,
                    name = "3D Model Position",
                },
                ModelX = {
                    type = "range",
                    order = 11,
                    name = "Model X Position",
                    desc = "Horizontal position of the 3D model",
                    min = -400,
                    max = 400,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.modelPosition and config.modelPosition.x) or AlternativePortraitDefaults.normal.model.x
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.modelPosition = Addon.db.profile.AlternativePortrait.modelPosition or {}
                        Addon.db.profile.AlternativePortrait.modelPosition.x = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.modelFrame then
                            local y = Addon.db.profile.AlternativePortrait.modelPosition.y or AlternativePortraitDefaults.normal.model.y
                            AlternativePortrait.frame.modelFrame:ClearAllPoints()
                            AlternativePortrait.frame.modelFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", value, y)
                        end
                    end,
                },
                ModelY = {
                    type = "range",
                    order = 12,
                    name = "Model Y Position",
                    desc = "Vertical position of the 3D model",
                    min = -200,
                    max = 200,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.modelPosition and config.modelPosition.y) or AlternativePortraitDefaults.normal.model.y
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.modelPosition = Addon.db.profile.AlternativePortrait.modelPosition or {}
                        Addon.db.profile.AlternativePortrait.modelPosition.y = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.modelFrame then
                            local x = Addon.db.profile.AlternativePortrait.modelPosition.x or AlternativePortraitDefaults.normal.model.x
                            AlternativePortrait.frame.modelFrame:ClearAllPoints()
                            AlternativePortrait.frame.modelFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", x, value)
                        end
                    end,
                },
                ModelSize = {
                    type = "range",
                    order = 13,
                    name = "Model Size",
                    desc = "Size of the 3D model frame",
                    min = 50,
                    max = 300,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.modelSize) or AlternativePortraitDefaults.normal.model.size
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.modelSize = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.modelFrame then
                            AlternativePortrait.frame.modelFrame:SetSize(value, value)
                        end
                    end,
                },
                LineBreak1 = { type = "description", name = "", order = 14 },
                
                -- Title (NPC Name) Controls
                TitleHeader = {
                    type = "header",
                    order = 20,
                    name = "NPC Name Position",
                },
                TitleX = {
                    type = "range",
                    order = 21,
                    name = "Title X Position",
                    desc = "Horizontal position of the NPC name",
                    min = -400,
                    max = 400,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.titlePosition and config.titlePosition.x) or -93
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.titlePosition = Addon.db.profile.AlternativePortrait.titlePosition or {}
                        Addon.db.profile.AlternativePortrait.titlePosition.x = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.titleFrame then
                            local y = Addon.db.profile.AlternativePortrait.titlePosition.y or 16
                            AlternativePortrait.frame.titleFrame:ClearAllPoints()
                            AlternativePortrait.frame.titleFrame:SetPoint("LEFT", AlternativePortrait.frame, "CENTER", value, y)
                        end
                    end,
                },
                TitleY = {
                    type = "range",
                    order = 22,
                    name = "Title Y Position",
                    desc = "Vertical position of the NPC name",
                    min = -200,
                    max = 200,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.titlePosition and config.titlePosition.y) or 16
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.titlePosition = Addon.db.profile.AlternativePortrait.titlePosition or {}
                        Addon.db.profile.AlternativePortrait.titlePosition.y = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.titleFrame then
                            local x = Addon.db.profile.AlternativePortrait.titlePosition.x or -93
                            AlternativePortrait.frame.titleFrame:ClearAllPoints()
                            AlternativePortrait.frame.titleFrame:SetPoint("LEFT", AlternativePortrait.frame, "CENTER", x, value)
                        end
                    end,
                },
                LineBreak2 = { type = "description", name = "", order = 23 },
                
                -- Description (Quest Title) Controls
                DescHeader = {
                    type = "header",
                    order = 30,
                    name = "Quest Title Position",
                },
                DescX = {
                    type = "range",
                    order = 31,
                    name = "Quest Title X Position",
                    desc = "Horizontal position of the quest title",
                    min = -400,
                    max = 400,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.descPosition and config.descPosition.x) or -40
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.descPosition = Addon.db.profile.AlternativePortrait.descPosition or {}
                        Addon.db.profile.AlternativePortrait.descPosition.x = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.descFrame then
                            local y = Addon.db.profile.AlternativePortrait.descPosition.y or -22
                            AlternativePortrait.frame.descFrame:ClearAllPoints()
                            AlternativePortrait.frame.descFrame:SetPoint("LEFT", AlternativePortrait.frame, "CENTER", value, y)
                        end
                    end,
                },
                DescY = {
                    type = "range",
                    order = 32,
                    name = "Quest Title Y Position",
                    desc = "Vertical position of the quest title",
                    min = -200,
                    max = 200,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.descPosition and config.descPosition.y) or -22
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.descPosition = Addon.db.profile.AlternativePortrait.descPosition or {}
                        Addon.db.profile.AlternativePortrait.descPosition.y = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.descFrame then
                            local x = Addon.db.profile.AlternativePortrait.descPosition.x or -40
                            AlternativePortrait.frame.descFrame:ClearAllPoints()
                            AlternativePortrait.frame.descFrame:SetPoint("LEFT", AlternativePortrait.frame, "CENTER", x, value)
                        end
                    end,
                },
                LineBreak3 = { type = "description", name = "", order = 33 },
                
                -- Icon Controls
                IconHeader = {
                    type = "header",
                    order = 40,
                    name = "Quest Icon Position",
                },
                IconX = {
                    type = "range",
                    order = 41,
                    name = "Icon X Position",
                    desc = "Horizontal position of the quest icon",
                    min = -400,
                    max = 400,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.iconPosition and config.iconPosition.x) or -62
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.iconPosition = Addon.db.profile.AlternativePortrait.iconPosition or {}
                        Addon.db.profile.AlternativePortrait.iconPosition.x = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.iconFrame then
                            local y = Addon.db.profile.AlternativePortrait.iconPosition.y or -22
                            AlternativePortrait.frame.iconFrame:ClearAllPoints()
                            AlternativePortrait.frame.iconFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", value, y)
                        end
                    end,
                },
                IconY = {
                    type = "range",
                    order = 42,
                    name = "Icon Y Position",
                    desc = "Vertical position of the quest icon",
                    min = -200,
                    max = 200,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.iconPosition and config.iconPosition.y) or -22
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.iconPosition = Addon.db.profile.AlternativePortrait.iconPosition or {}
                        Addon.db.profile.AlternativePortrait.iconPosition.y = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.iconFrame then
                            local x = Addon.db.profile.AlternativePortrait.iconPosition.x or -62
                            AlternativePortrait.frame.iconFrame:ClearAllPoints()
                            AlternativePortrait.frame.iconFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", x, value)
                        end
                    end,
                },
                LineBreak4 = { type = "description", name = "", order = 43 },
                
                -- Logo Controls
                LogoHeader = {
                    type = "header",
                    order = 50,
                    name = "Logo Position",
                },
                LogoX = {
                    type = "range",
                    order = 51,
                    name = "Logo X Position",
                    desc = "Horizontal position of the logo",
                    min = -400,
                    max = 400,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.logoPosition and config.logoPosition.x) or -234
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.logoPosition = Addon.db.profile.AlternativePortrait.logoPosition or {}
                        Addon.db.profile.AlternativePortrait.logoPosition.x = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.logoFrame then
                            local y = Addon.db.profile.AlternativePortrait.logoPosition.y or -71
                            AlternativePortrait.frame.logoFrame:ClearAllPoints()
                            AlternativePortrait.frame.logoFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", value, y)
                        end
                    end,
                },
                LogoY = {
                    type = "range",
                    order = 52,
                    name = "Logo Y Position",
                    desc = "Vertical position of the logo",
                    min = -200,
                    max = 200,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.logoPosition and config.logoPosition.y) or -71
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.logoPosition = Addon.db.profile.AlternativePortrait.logoPosition or {}
                        Addon.db.profile.AlternativePortrait.logoPosition.y = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.logoFrame then
                            local x = Addon.db.profile.AlternativePortrait.logoPosition.x or -234
                            AlternativePortrait.frame.logoFrame:ClearAllPoints()
                            AlternativePortrait.frame.logoFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", x, value)
                        end
                    end,
                },
                LineBreak5 = { type = "description", name = "", order = 53 },
                
                -- Background Controls
                BgHeader = {
                    type = "header",
                    order = 60,
                    name = "Background Position",
                },
                BgX = {
                    type = "range",
                    order = 61,
                    name = "Background X Position",
                    desc = "Horizontal position of the background",
                    min = -400,
                    max = 400,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.bgPosition and config.bgPosition.x) or -229
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.bgPosition = Addon.db.profile.AlternativePortrait.bgPosition or {}
                        Addon.db.profile.AlternativePortrait.bgPosition.x = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.bgFrame then
                            local y = Addon.db.profile.AlternativePortrait.bgPosition.y or 0
                            AlternativePortrait.frame.bgFrame:ClearAllPoints()
                            AlternativePortrait.frame.bgFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", value, y)
                        end
                    end,
                },
                BgY = {
                    type = "range",
                    order = 62,
                    name = "Background Y Position",
                    desc = "Vertical position of the background",
                    min = -200,
                    max = 200,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.bgPosition and config.bgPosition.y) or 0
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.bgPosition = Addon.db.profile.AlternativePortrait.bgPosition or {}
                        Addon.db.profile.AlternativePortrait.bgPosition.y = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.bgFrame then
                            local x = Addon.db.profile.AlternativePortrait.bgPosition.x or -229
                            AlternativePortrait.frame.bgFrame:ClearAllPoints()
                            AlternativePortrait.frame.bgFrame:SetPoint("CENTER", AlternativePortrait.frame, "CENTER", x, value)
                        end
                    end,
                },
                BgSize = {
                    type = "range",
                    order = 63,
                    name = "Background Size",
                    desc = "Size of the background frame",
                    min = 50,
                    max = 300,
                    step = 1,
                    get = function(info)
                        local config = Addon.db.profile.AlternativePortrait
                        return (config and config.bgSize) or 150
                    end,
                    set = function(info, value)
                        Addon.db.profile.AlternativePortrait = Addon.db.profile.AlternativePortrait or {}
                        Addon.db.profile.AlternativePortrait.bgSize = value
                        if AlternativePortrait and AlternativePortrait.frame and AlternativePortrait.frame.bgFrame then
                            AlternativePortrait.frame.bgFrame:SetSize(value, value)
                        end
                    end,
                },
            }
        },
        PortraitBackground = {
            type = "group",
            order = 1,
            inline = true,
            name = "Portrait Background",
            hidden = function(info) return Addon.db.profile.SoundQueueUI.PortraitStyle == 1 end,
            disabled = function(info) return Addon.db.profile.SoundQueueUI.HideFrame or Addon.db.profile.SoundQueueUI.HidePortrait end,
            args = {
                BackgroundTexture = {
                    type = "select",
                    order = 1,
                    name = "Background Style",
                    desc = "Choose the background texture for the portrait frame.",
                    values = {
                        [0] = "Style 0",
                        [1] = "Style 1",
                        [2] = "Style 2",
                        [3] = "Style 3",
                    },
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitBackgroundTexture end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitBackgroundTexture = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                LineBreak = { type = "description", name = "", order = 2 },
                Preview = {
                    type = "execute",
                    order = 3,
                    name = "",
                    image = function()
                        local textureIndex = Addon.db.profile.SoundQueueUI.PortraitBackgroundTexture or 0
                        return [[Interface\AddOns\AI_VoiceOver\Textures\PortraitFrameBackground]] .. textureIndex
                    end,
                    imageWidth = 120,
                    imageHeight = 120,
                    func = function() end,
                },
            }
        },
        PortraitFrameStyle = {
            type = "group",
            order = 2,
            inline = true,
            name = "Portrait Frame Style",
            hidden = function(info) return Addon.db.profile.SoundQueueUI.PortraitStyle == 1 end,
            disabled = function(info) return Addon.db.profile.SoundQueueUI.HideFrame or Addon.db.profile.SoundQueueUI.HidePortrait end,
            args = {
                FrameAtlas = {
                    type = "select",
                    order = 1,
                    name = "Frame Style",
                    desc = "Choose the frame border and button style for the portrait.",
                    values = {
                        [1] = "Style 1",
                        [2] = "Style 2",
                        [3] = "Style 3",
                    },
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitFrameAtlas end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitFrameAtlas = value
                        SoundQueueUI:RefreshConfig()
                    end,
                },
                LineBreak = { type = "description", name = "", order = 2 },
                PreviewAtlas = {
                    type = "execute",
                    order = 3,
                    name = "",
                    image = function()
                        local atlasIndex = Addon.db.profile.SoundQueueUI.PortraitFrameAtlas or 0
                        local atlasPath = [[Interface\AddOns\AI_VoiceOver\Textures\PortraitFrameAtlas]]
                        if atlasIndex > 0 then
                            atlasPath = atlasPath .. atlasIndex
                        end
                        return atlasPath
                    end,
                    imageWidth = 120,
                    imageHeight = 120,
                    func = function() end,
                },
            }
        },
        PortraitEffectSettings = {
            type = "group",
            order = 3,
            inline = true,
            name = "Portrait Visual Effect",
            hidden = function(info) return Addon.db.profile.SoundQueueUI.PortraitStyle == 1 end,
            disabled = function(info) return Addon.db.profile.SoundQueueUI.HideFrame or Addon.db.profile.SoundQueueUI.HidePortrait end,
            args = {
                EffectEnabled = {
                    type = "toggle",
                    order = 0,
                    name = "Enable Visual Effect",
                    desc = "Enable or disable the visual effect overlay on the portrait",
                    width = "full",
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectEnabled end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectEnabled = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            local effectModel = SoundQueueUI.frame.portrait.effectModel
                            if value then
                                effectModel:Show()
                            else
                                effectModel:Hide()
                            end
                        end
                    end,
                },
                LineBreak0 = { type = "description", name = "", order = 0.5 },
                EffectFileID = {
                    type = "input",
                    order = 1,
                    name = "Effect FileDataID",
                    desc = "Enter the FileDataID of the visual effect model (e.g., 5868339 for green infusion)",
                    width = "full",
                    get = function(info) return tostring(Addon.db.profile.SoundQueueUI.PortraitEffectFileID or 5868339) end,
                    set = function(info, value)
                        local fileID = tonumber(value)
                        if fileID and fileID > 0 then
                            Addon.db.profile.SoundQueueUI.PortraitEffectFileID = fileID
                            -- Reload the effect model with new FileDataID
                            if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                                local effectModel = SoundQueueUI.frame.portrait.effectModel
                                local success = pcall(effectModel.SetModel, effectModel, fileID)
                                if success then
                                    effectModel:SetPortraitZoom(0)
                                    effectModel:ClearTransform()
                                    effectModel:SetPosition(0, 0, 0)
                                    effectModel:SetFacing(0)
                                    effectModel:SetAnimation(0)
                                    local effectScale = Addon.db.profile.SoundQueueUI.PortraitEffectScale or 1.0
                                    effectModel:SetScale(effectScale)
                                    Debug:Print(Utils:ColorizeText("Effect model updated to FileDataID: " .. tostring(fileID), GREEN_FONT_COLOR_CODE), "VoiceOver")
                                else
                                    Debug:Print(Utils:ColorizeText("Failed to load FileDataID: " .. tostring(fileID), RED_FONT_COLOR_CODE), "VoiceOver")
                                end
                            end
                        else
                            Debug:Print(Utils:ColorizeText("Invalid FileDataID. Please enter a valid number.", RED_FONT_COLOR_CODE), "VoiceOver")
                        end
                    end,
                },
                LineBreak1 = { type = "description", name = "", order = 2 },
                EffectDetached = {
                    type = "toggle",
                    order = 3,
                    name = "Detach Effect from Portrait",
                    desc = "When enabled, the effect can be moved independently from the portrait frame",
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectDetached or false end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectDetached = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            local effectModel = SoundQueueUI.frame.portrait.effectModel
                            local portrait = SoundQueueUI.frame.portrait
                            if value then
                                -- Detach: make it movable
                                effectModel:ClearAllPoints()
                                effectModel:SetParent(UIParent)
                                effectModel:SetMovable(true)
                                effectModel:EnableMouse(true)
                                effectModel:RegisterForDrag("LeftButton")
                                effectModel:SetScript("OnDragStart", function(self) self:StartMoving() end)
                                effectModel:SetScript("OnDragStop", function(self)
                                    self:StopMovingOrSizing()
                                    -- Save position
                                    local point, _, relativePoint, x, y = self:GetPoint()
                                    Addon.db.profile.SoundQueueUI.PortraitEffectPosition = {
                                        point = point,
                                        relativePoint = relativePoint,
                                        x = x,
                                        y = y
                                    }
                                end)
                                -- Restore saved position or default
                                local pos = Addon.db.profile.SoundQueueUI.PortraitEffectPosition
                                if pos then
                                    effectModel:SetPoint(pos.point, UIParent, pos.relativePoint, pos.x, pos.y)
                                else
                                    effectModel:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
                                end
                                local frameSize = Addon.db.profile.SoundQueueUI.PortraitEffectFrameSize or 120
                                effectModel:SetSize(frameSize, frameSize)

                                -- Hook visibility to portrait
                                portrait:HookScript("OnShow", function()
                                    if effectModel then effectModel:Show() end
                                end)
                                portrait:HookScript("OnHide", function()
                                    if effectModel then effectModel:Hide() end
                                end)

                                -- Set initial visibility
                                if portrait:IsShown() then
                                    effectModel:Show()
                                else
                                    effectModel:Hide()
                                end
                            else
                                -- Attach back to portrait
                                effectModel:SetMovable(false)
                                effectModel:EnableMouse(false)
                                effectModel:SetScript("OnDragStart", nil)
                                effectModel:SetScript("OnDragStop", nil)
                                effectModel:ClearAllPoints()
                                effectModel:SetAllPoints(portrait)
                                effectModel:SetParent(portrait)
                                -- Attached mode: always visible with portrait (parent handles visibility)
                                effectModel:Show()
                            end
                        end
                    end,
                },
                LineBreak2 = { type = "description", name = "", order = 4 },
                EffectFrameSize = {
                    type = "range",
                    order = 5,
                    name = "Frame Size",
                    desc = "Size of the frame container when detached",
                    min = 50,
                    max = 500,
                    step = 10,
                    disabled = function(info) return not Addon.db.profile.SoundQueueUI.PortraitEffectDetached end,
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectFrameSize or 120 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectFrameSize = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            if Addon.db.profile.SoundQueueUI.PortraitEffectDetached then
                                SoundQueueUI.frame.portrait.effectModel:SetSize(value, value)
                            end
                        end
                    end,
                },
                LineBreak3 = { type = "description", name = "", order = 6 },
                EffectPositionX = {
                    type = "range",
                    order = 7,
                    name = "Position X",
                    desc = "Horizontal position offset of the model (left/right)",
                    min = -10,
                    max = 10,
                    step = 0.1,
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectX or 0 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectX = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            local x = Addon.db.profile.SoundQueueUI.PortraitEffectX or 0
                            local y = Addon.db.profile.SoundQueueUI.PortraitEffectY or 0
                            local z = Addon.db.profile.SoundQueueUI.PortraitEffectZ or 0
                            SoundQueueUI.frame.portrait.effectModel:SetPosition(z, x, y)
                        end
                    end,
                },
                EffectPositionY = {
                    type = "range",
                    order = 8,
                    name = "Position Y",
                    desc = "Vertical position offset of the model (up/down)",
                    min = -10,
                    max = 10,
                    step = 0.1,
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectY or 0 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectY = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            local x = Addon.db.profile.SoundQueueUI.PortraitEffectX or 0
                            local y = Addon.db.profile.SoundQueueUI.PortraitEffectY or 0
                            local z = Addon.db.profile.SoundQueueUI.PortraitEffectZ or 0
                            SoundQueueUI.frame.portrait.effectModel:SetPosition(z, x, y)
                        end
                    end,
                },
                EffectPositionZ = {
                    type = "range",
                    order = 9,
                    name = "Position Z (Zoom)",
                    desc = "Depth/zoom of the model (closer/farther)",
                    min = -10,
                    max = 10,
                    step = 0.1,
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectZ or 0 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectZ = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            local x = Addon.db.profile.SoundQueueUI.PortraitEffectX or 0
                            local y = Addon.db.profile.SoundQueueUI.PortraitEffectY or 0
                            local z = Addon.db.profile.SoundQueueUI.PortraitEffectZ or 0
                            SoundQueueUI.frame.portrait.effectModel:SetPosition(z, x, y)
                        end
                    end,
                },
                EffectRotation = {
                    type = "range",
                    order = 10,
                    name = "Rotation",
                    desc = "Rotation angle of the model in degrees",
                    min = 0,
                    max = 360,
                    step = 1,
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectRotation or 0 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectRotation = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            local radians = math.rad(value)
                            SoundQueueUI.frame.portrait.effectModel:SetFacing(radians)
                        end
                    end,
                },
                LineBreak4 = { type = "description", name = "", order = 11 },
                EffectScale = {
                    type = "range",
                    order = 12,
                    name = "Effect Scale",
                    desc = "Scale/size of the visual effect model",
                    min = 0.1,
                    max = 5.0,
                    step = 0.1,
                    get = function(info) return Addon.db.profile.SoundQueueUI.PortraitEffectScale or 1.0 end,
                    set = function(info, value)
                        Addon.db.profile.SoundQueueUI.PortraitEffectScale = value
                        if SoundQueueUI and SoundQueueUI.frame and SoundQueueUI.frame.portrait and SoundQueueUI.frame.portrait.effectModel then
                            SoundQueueUI.frame.portrait.effectModel:SetScale(value)
                        end
                    end,
                },
            }
        },
    }
}

---@type AceConfigOptionsTable




-- LegacyWrathTab removed - no longer needed for Midnight

-- Pestaña Agradecimientos
---@type AceConfigOptionsTable
local AcknowledgementsTab = {
    name = "Colaboradores",
    type = "group",
    order = 30,
    args = {
        Header = {
            type = "header",
            order = 1,
            name = "|cFFFFD700Creadores de Contenido y Colaboradores|r",
        },
        CreatorList = {
            type = "group",
            order = 2,
            inline = true,
            name = "Colaboradores",
            args = {
                Creator1 = {
                    type = "group",
                    order = 1, -- Ajustado a 1
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\Deiris",
                            imageWidth = 64,
                            imageHeight = 64,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de Deiris")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.youtube.com/@Deiris")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "|cFF9966CCDeiris|r\n|cFF00CCFFhttps://www.youtube.com/@Deiris|r",
                            width = 2.6,
                        },
                    },
                },
                Creator2 = {
                    type = "group",
                    order = 2, -- Ajustado a 2
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\Andtres",
                            imageWidth = 64,
                            imageHeight = 64,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de Andtres")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.twitch.tv/andtres")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "|cFF9966CCAndtres|r\n|cFF00CCFFhttps://www.twitch.tv/andtres|r",
                            width = 2.6,
                        },
                    },
                },
                Creator3 = {
                    type = "group",
                    order = 3, -- Ajustado a 3
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\Dantaes",
                            imageWidth = 64,
                            imageHeight = 64,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de FARMERS DE AZEROTH")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.youtube.com/@dantaes")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "|cFF9966CCFARMERS DE AZEROTH|r\n|cFF00CCFFhttps://www.youtube.com/@dantaes|r",
                            width = 2.6,
                        },
                    },
                },
                Creator4 = {
                    type = "group",
                    order = 4, -- Ajustado a 4
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\Karlana",
                            imageWidth = 64,
                            imageHeight = 66,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de El Diario de Karlana")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.youtube.com/@ElDiarioDeKarlana")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name =
                            "|cFF9966CCEl Diario de Karlana|r\n|cFF00CCFFhttps://www.youtube.com/@ElDiarioDeKarlana|r",
                            width = 2.6,
                        },
                    },
                },
                Creator5 = {
                    type = "group",
                    order = 5, -- Ajustado a 5
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\Moverlorde",
                            imageWidth = 64,
                            imageHeight = 64,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de Moverlorde")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.youtube.com/@Moverlorde")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "|cFF9966CCMoverlorde|r\n|cFF00CCFFhttps://www.youtube.com/@Moverlorde|r",
                            width = 2.6,
                        },
                    },
                },
                Creator6 = {
                    type = "group",
                    order = 6, -- Ajustado a 6
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\ZNoF354",
                            imageWidth = 64,
                            imageHeight = 64,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de ZNoF354")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.youtube.com/@ZNoF354")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "|cFF9966CCZNoF354|r\n|cFF00CCFFhttps://www.youtube.com/@ZNoF354|r",
                            width = 2.6,
                        },
                    },
                },
                Creator7 = {
                    type = "group",
                    order = 7, -- Ajustado a 7
                    inline = true,
                    name = "",
                    args = {
                        Icon = {
                            type = "execute",
                            order = 1,
                            name = "",
                            width = 0.4,
                            image = "Interface\\AddOns\\AI_VoiceOver\\Icons\\Saiztico",
                            imageWidth = 64,
                            imageHeight = 64,
                            func = function()
                                if _G["LinkFrame"] then
                                    _G["LinkFrame"]:Hide()
                                    _G["LinkFrame"] = nil
                                end

                                local f = CreateFrame("Frame", "LinkFrame", UIParent, "BasicFrameTemplateWithInset")
                                f:SetSize(400, 150)
                                f:SetPoint("CENTER")
                                f:SetFrameStrata("TOOLTIP")

                                f.title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
                                f.title:SetPoint("TOP", 0, -5)
                                f.title:SetText("Canal de Saiztico")

                                local eb = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
                                eb:SetSize(300, 20)
                                eb:SetPoint("CENTER", 0, 0)
                                eb:SetText("https://www.youtube.com/@saiztico")
                                eb:SetAutoFocus(true)
                                eb:HighlightText()

                                local closeButton = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
                                closeButton:SetText("Cerrar")
                                closeButton:SetSize(100, 25)
                                closeButton:SetPoint("BOTTOM", 0, 15)
                                closeButton:SetScript("OnClick", function()
                                    f:Hide(); _G["LinkFrame"] = nil
                                end)

                                f:Show()
                            end,
                        },
                        Info = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "|cFF9966CCSaiztico|r\n|cFF00CCFFhttps://www.youtube.com/@saiztico|r",
                            width = 2.6,
                        },
                    },
                },
            },
        },
        Footer = {
            type = "description",
            order = 7, -- Ajustado para estar justo después de los creadores
            fontSize = "medium",
            name = "\n|cFFFFD700Estos creadores de contenido han apoyado y contribuido al crecimiento de VoiceOver.|r\n" ..
                "|cFFCCCCCCGracias por su apoyo continuo a la comunidad de WoW.|r",
        },
    },
}





-- Pestaña Donaciones
---@type AceConfigOptionsTable
-- Variables para los textos en diferentes idiomas
-- Variables para los textos en diferentes idiomas
local donationTexts = {
    es =
    "|cffffffffLas donaciones serán usadas para crear la versión en Español Latino y Español de España, y a medida que avance el proyecto, se extenderá a todos los idiomas.|r",
    en =
    "|cffffffffDonations will be used to create the Latin American Spanish and European Spanish versions, and as the project progresses, it will expand to all languages.|r",
    fr =
    "|cffffffffLes dons seront utilisés pour créer les versions espagnoles d'Amérique latine et d'Espagne, et à mesure que le projet avancera, il s'étendra à toutes les langues.|r",
    it =
    "|cffffffffLe donazioni saranno utilizzate per creare le versioni spagnole latinoamericane e spagnole europee e, man mano che il progetto progredirà, si espanderà a tutte le lingue.|r",
    pt =
    "|cffffffffAs doações serão usadas para criar as versões em espanhol latino-americano e espanhol europeu e, à medida que o projeto avança, expandir-se-ão para todos os idiomas.|r",
}

-- Variable para almacenar el idioma seleccionado
local selectedLanguage = "es"

-- Función auxiliar para cargar el archivo de donadores de forma segura
local function LoadAddOnSafelyAndReturnDonors()
    if not VoiceOver_DonorsList then
        return "|cFFFF0000No donors list available|r"
    end

    -- Orden de los niveles (de más alto a más bajo)
    local levels = {
        {
            key = "LEGENDARY",
            name = "Legendary Donors",
            color = "|cffff8000",
            icon = "|TInterface\\Icons\\INV_Hammer_Unique_Sulfuras:16:16|t"
        },
        {
            key = "EPIC",
            name = "Epic Donors",
            color = "|cFFA335EE",
            icon = "|TInterface\\Icons\\INV_Enchant_VoidCrystal:16:16|t"
        },
        {
            key = "RARE",
            name = "Rare Donors",
            color = "|cFF0070DD",
            icon = "|TInterface\\Icons\\INV_Enchant_ShardBrilliantSmall:16:16|t"
        },
        {
            key = "UNCOMMON",
            name = "Uncommon Donors",
            color = "|cFF1EFF00",
            icon = "|TInterface\\Icons\\INV_Enchant_ShardGlowingSmall:16:16|t"
        }
    }

    local donorsStr = ""

    -- Agregar encabezado con explicación de rangos
    donorsStr = donorsStr .. "|cFFFFD700Donor Ranks:|r\n\n"

    for _, level in ipairs(levels) do
        if VoiceOver_DonorsList[level.key] and #VoiceOver_DonorsList[level.key] > 0 then
            -- Agregar encabezado de nivel con icono
            donorsStr = donorsStr .. level.icon .. " " .. level.color .. level.name .. "|r\n"

            -- Agregar donadores de este nivel
            for _, donor in ipairs(VoiceOver_DonorsList[level.key]) do
                donorsStr = donorsStr .. level.color .. "    • " .. donor .. "|r\n"
            end

            donorsStr = donorsStr .. "\n"
        end
    end

    return donorsStr
end

-- Pestaña Support
local DonationsTab = {
    name = "Support",
    type = "group",
    order = 35,
    childGroups = "tab",
    args = {
        contributors = {
            name = "Contributors",
            type = "group",
            order = 0,
            args = AcknowledgementsTab.args,
        },
        donorsList = {
            name = "Donors",
            type = "group",
            order = 1,
            args = {
                Header = {
                    type = "header",
                    order = 1,
                    name = "|cFFFFD700Our Donors|r",
                },
                Description = {
                    type = "description",
                    order = 2,
                    fontSize = "medium",
                    name = "Thanks to all these amazing people who support VoiceOver development:",
                },
                Donors = {
                    type = "description",
                    order = 3,
                    fontSize = "medium",
                    name = function()
                        return LoadAddOnSafelyAndReturnDonors()
                    end
                }
            }
        },
        supportInfo = {
            name = "Donate",
            type = "group",
            order = 2,
            args = {
                DiscordSection = {
                    type = "group",
                    order = 1,
                    inline = true,
                    name = "Community",
                    args = {
                        DiscordHeader = {
                            type = "header",
                            order = 1,
                            name = "Join Our Discord Community",
                        },
                        DiscordDescription = {
                            type = "description",
                            order = 2,
                            fontSize = "medium",
                            name = "Join our community to get support, updates and participate in development:",
                        },
                        DiscordLink = {
                            type = "input",
                            order = 3,
                            width = "full",
                            name = "|cff7289DADiscord Server|r",
                            get = function() return "https://discord.gg/jHPsF2dt9y" end,
                            set = function() end,
                        },
                    }
                },
                DonationSection = {
                    type = "group",
                    order = 2,
                    inline = true,
                    name = "Support Options",
                    args = {
                        LanguageSelection = {
                            type = "select",
                            order = 1,
                            name = "Language",
                            desc = "Select your language",
                            values = {
                                es = "Español",
                                en = "English",
                                fr = "Français",
                                it = "Italiano",
                                pt = "Português",
                            },
                            get = function() return selectedLanguage end,
                            set = function(_, newValue)
                                selectedLanguage = newValue
                            end,
                        },
                        Description = {
                            type = "description",
                            order = 2,
                            name = function()
                                return donationTexts[selectedLanguage]
                            end,
                        },
                        PayPalLink = {
                            type = "input",
                            order = 3,
                            width = "full",
                            name = "|cff0079C1PayPal|r",
                            desc = "Support via PayPal",
                            get = function() return "neo4400@hotmail.com" end,
                            set = function() end,
                        },
                        PatreonLink = {
                            type = "input",
                            order = 4,
                            width = "full",
                            name = "|cff3399FFPatreon|r",
                            desc = "Become a Patron",
                            get = function() return "https://patreon.com/NeoAuras" end,
                            set = function() end,
                        },
                    }
                }
            }
        }
    }
}


---@type AceConfigOptionsTable
local DataModulesTab =
{
    name = "Data Modules",
    type = "group",
    childGroups = "tree",
    order = 1,
    args = {
        LoadedPacksHeader = {
            type = "header",
            order = 3,
            name = "Loaded Sound Packages",
        },
        LoadedPacks = {
            type = "group",
            name = "",
            order = 4,
            inline = true,
            args = {}
        },
        Available = {
            type = "group",
            name = "|cFF00CCFFMore Sound Packs|r",
            order = 100000,
            hidden = function(info) return not next(Options.table.args.DataModules.args.Available.args) end,
            args = {}
        }
    }
}

---@type AceConfigOptionsTable
local SlashCommands = {
    type = "group",
    name = "Commands",
    order = 110,
    inline = true,
    dialogHidden = true,
    args = {
        PlayPause = {
            type = "execute",
            order = 1,
            name = "Play/Pause Audio",
            desc = "Play/Pause voiceovers",
            hidden = true,
            func = function(info)
                SoundQueue:TogglePauseQueue()
            end
        },
        Play = {
            type = "execute",
            order = 2,
            name = "Play Audio",
            desc = "Resume the playback of voiceovers",
            func = function(info)
                SoundQueue:ResumeQueue()
            end
        },
        Pause = {
            type = "execute",
            order = 3,
            name = "Pause Audio",
            desc = "Pause the playback of voiceovers",
            func = function(info)
                SoundQueue:PauseQueue()
            end
        },
        Skip = {
            type = "execute",
            order = 4,
            name = "Skip Line",
            desc = "Skip the currently played voiceover",
            func = function(info)
                local soundData = SoundQueue:GetCurrentSound()
                if soundData then
                    SoundQueue:RemoveSoundFromQueue(soundData)
                end
            end
        },
        Clear = {
            type = "execute",
            order = 5,
            name = "Clear Queue",
            desc = "Stop the playback and clears the voiceovers queue",
            func = function(info)
                SoundQueue:RemoveAllSoundsFromQueue()
            end
        },
        Options = {
            type = "execute",
            order = 100,
            name = "Open Options",
            desc = "Open the options panel",
            func = function(info)
                Options:OpenConfigWindow()
            end
        },
    }
}

---@type AceConfigOptionsTable
Options.table = {
    name = "Voice Over Mod 2.0",
    type = "group",
    childGroups = "tab",
    args = {
        General = GeneralTab,
        VisualEffects = VisualEffectsTab, -- Nueva pestaña Visual Effects
        DataModules = DataModulesTab,
        Donations = DonationsTab,
        Profiles = nil, -- Filled in Options:OnInitialize, order is implicitly 100
        SlashCommands = SlashCommands,
    }
}
------------------------------------------------------------

---@param module DataModuleMetadata
---@param order number
function Options:AddDataModule(module, order)
    local descriptionOrder = 0
    local function GetNextOrder()
        descriptionOrder = descriptionOrder + 1
        return descriptionOrder
    end
    local function MakeDescription(header, text)
        return {
            type = "description",
            order = GetNextOrder(),
            name = function()
                return format("%s%s: |r%s",
                    NORMAL_FONT_COLOR_CODE, header, type(text) == "function" and text() or text)
            end
        }
    end

    local name, title, notes, loadable, reason = DataModules:GetModuleAddOnInfo(module)
    if reason == "DEMAND_LOADED" or reason == "INTERFACE_VERSION" then
        reason = nil
    end
    DataModulesTab.args[module.AddonName] = {
        name = function()
            local isLoaded = C_AddOns.IsAddOnLoaded(module.AddonName)
            local isDisabled = DataModules:IsModuleDisabled(module.AddonName)
            return format("%d. %s%s%s|r",
                order,
                reason and RED_FONT_COLOR_CODE or isLoaded and HIGHLIGHT_FONT_COLOR_CODE or GRAY_FONT_COLOR_CODE,
                string.gsub(module.Title, "VoiceOver Data %- ", ""),
                isLoaded and "" or (isDisabled and " (disabled)" or " (not loaded)"))
        end,
        type = "group",
        order = order,
        args = {
            AddonName = MakeDescription("Addon Name", module.AddonName),
            Title = MakeDescription("Title", module.Title),
            ModuleVersion = MakeDescription("Module Data Format Version", module.ModuleVersion),
            ModulePriority = MakeDescription("Module Priority", module.ModulePriority),
            ContentVersion = MakeDescription("Content Version", module.ContentVersion),
            LoadOnDemand = MakeDescription("Load on Demand", module.LoadOnDemand and "Yes" or "No"),
            Loaded = MakeDescription("Is Loaded",
                function() return C_AddOns.IsAddOnLoaded(module.AddonName) and "Yes" or "No" end),
            Enabled = {
                type = "toggle",
                order = GetNextOrder(),
                name = "Enable Module",
                desc = "Enable or disable this data module. When disabled, the module will not be loaded until re-enabled.",
                width = "full",
                get = function() return not DataModules:IsModuleDisabled(module.AddonName) end,
                set = function(info, value)
                    DataModules:SetModuleDisabled(module.AddonName, not value)
                    -- Actualizar la interfaz para reflejar el cambio
                    AceConfigDialog:SelectGroup("VoiceOver", "DataModules")
                end,
            },
            NotLoadableReason = {
                type = "description",
                order = GetNextOrder(),
                name = format("%sReason: |r%s%s|r", NORMAL_FONT_COLOR_CODE, RED_FONT_COLOR_CODE,
                    reason and _G["ADDON_" .. reason] or ""),
                hidden = not reason,
            },
            IncompatibleFixHeader = {
                type = "description",
                order = GetNextOrder(),
                width = "full",
                name = "\n" .. YELLOW_FONT_COLOR_CODE .. "To fix this, go to the path below, edit the .toc file, save and Reload UI:|r",
                hidden = reason ~= "INCOMPATIBLE",
            },
            IncompatibleFixPath = {
                type = "description",
                order = GetNextOrder(),
                width = "full",
                name = "Path:|n" .. RED_FONT_COLOR_CODE .. [[Interface\AddOns\]] .. module.AddonName .. [[\]] .. "|n" .. module.AddonName .. ".toc|r",
                hidden = reason ~= "INCOMPATIBLE",
            },
            IncompatibleFixFind = {
                type = "input",
                order = GetNextOrder(),
                width = "full",
                name = "Find:",
                get = function()
                    return "## Interface: " .. tostring(C_AddOns.GetAddOnInterfaceVersion(module.AddonName) or "?")
                end,
                set = function() end,
                hidden = reason ~= "INCOMPATIBLE",
            },
            IncompatibleFixReplace = {
                type = "input",
                order = GetNextOrder(),
                width = "full",
                name = "Replace with:",
                get = function() return "## Interface: " .. tostring(Version.Interface) end,
                set = function() end,
                hidden = reason ~= "INCOMPATIBLE",
            },
            Load = {
                type = "execute",
                order = GetNextOrder(),
                name = "Load",
                hidden = function() 
                    return reason or 
                           not module.LoadOnDemand or 
                           C_AddOns.IsAddOnLoaded(module.AddonName) or
                           DataModules:IsModuleDisabled(module.AddonName)
                end,
                func = function()
                    local loaded, reason = DataModules:LoadModule(module)
                    if not loaded then
                        StaticPopup_Show("VOICEOVER_ERROR",
                            format([[Failed to load data module "%s". Reason: %s]], module.AddonName,
                                reason and _G["ADDON_" .. reason] or "Unknown"))
                    end
                end,
            },
            ReloadUI = {
                type = "execute",
                order = GetNextOrder(),
                name = "Reload UI",
                desc = "Reload the user interface to apply changes",
                func = function()
                    ReloadUI()
                end,
            },
        }
    }
end

local LANGUAGE_GROUPS = {
    enUS = { name = "|cFFFFD700English (US)|r",    order = 1 },
    esMX = { name = "|cFFFFD700Español (MX)|r",    order = 2 },
    esES = { name = "|cFFFFD700Español (ES)|r",    order = 3 },
    deDE = { name = "|cFFFFD700Deutsch (DE)|r",    order = 4 },
    ptBR = { name = "|cFFFFD700Português (BR)|r",  order = 5 },
}

---@param module AvailableDataModule
---@param order number
---@param update boolean Data module has update
function Options:AddAvailableDataModule(module, order, update)
    local descriptionOrder = 0
    local function GetNextOrder()
        descriptionOrder = descriptionOrder + 1
        return descriptionOrder
    end
    local function MakeDescription(header, text)
        return {
            type = "description",
            order = GetNextOrder(),
            name = function()
                return format("%s%s: |r%s",
                    NORMAL_FONT_COLOR_CODE, header, type(text) == "function" and text() or text)
            end
        }
    end

    local args = {
        AddonName = MakeDescription("Addon Name", module.AddonName),
        Title = MakeDescription("Title", module.Title),
        ContentVersion = MakeDescription("Content Version",
            format(update and "%2$s -> |cFF00CCFF%1$s|r" or "%s", module.ContentVersion,
                update and DataModules:GetPresentModule(module.AddonName).ContentVersion)),
    }

    if module.Parts then
        -- Múltiples partes: un input por cada parte
        for i, part in ipairs(module.Parts) do
            args["URL_" .. i] = {
                type = "input",
                order = GetNextOrder(),
                width = "full",
                multiline = 2,
                name = part.name,
                get = function() return part.URL end,
                set = function() end,
            }
        end
    else
        -- Un solo URL
        args.URL = {
            type = "input",
            order = GetNextOrder(),
            width = "full",
            multiline = 2,
            name = "Download URL",
            get = function() return module.URL end,
            set = function() end,
        }
    end

    local entry = {
        name = Utils:ColorizeText(
            format(update and "%s (Update)" or "%s", string.gsub(module.Title, "VoiceOver Data %- ", "")), "|cFF00CCFF"),
        type = "group",
        order = order,
        args = args,
    }

    -- Si tiene Language, va dentro del subgrupo por idioma
    local lang = module.Language
    if lang and LANGUAGE_GROUPS[lang] then
        local langKey = "Lang_" .. lang
        -- Crear el subgrupo si no existe
        if not DataModulesTab.args.Available.args[langKey] then
            DataModulesTab.args.Available.args[langKey] = {
                type = "group",
                name = LANGUAGE_GROUPS[lang].name,
                order = LANGUAGE_GROUPS[lang].order,
                args = {},
            }
        end
        DataModulesTab.args.Available.args[langKey].args[module.AddonName] = entry
    else
        -- Sin idioma, va directo en Available (como el entry de proyectos)
        DataModulesTab.args.Available.args[module.AddonName] = entry
    end
end

---Initialization of opens panel
function Options:Initialize()
    self.table.args.Profiles = AceDBOptions:GetOptionsTable(Addon.db)

    -- Create options table
    Debug:Print("Registering options table...", "Options")
    local AceConfig = LibStub("AceConfig-3.0")
    AceConfig:RegisterOptionsTable("VoiceOver", self.table, "vo")
    AceConfigDialog:AddToBlizOptions("VoiceOver")
    for key, tab in Utils:Ordered(Options.table.args, SortAceConfigOptions) do
        if not tab.hidden and not tab.dialogHidden then
            AceConfigDialog:AddToBlizOptions("VoiceOver", type(tab.name) == "function" and tab.name() or tab.name,
                "VoiceOver", key)
        end
    end
    Debug:Print("Done!", "Options")

    -- Create the option frame
    ---@type AceGUIFrame|AceGUIWidget
    self.frame = AceGUI:Create("Frame")
    self.frame:SetTitle("VoiceOver")
    self.frame:SetLayout("Fill")
    self.frame:SetWidth(800)
    self.frame:SetHeight(600)
    self.frame:EnableResize(false)

    -- Open dialog - this will create the TabGroup
    AceConfigDialog:Open("VoiceOver", self.frame)

    -- Función que reaplica tamaño y posición de pestañas
    local function FixTabs(tabGroup)
        if not tabGroup or tabGroup.type ~= "TabGroup" or not tabGroup.tabs then return end

        local fixedWidth = 140
        local fixedHeight = 40

        for i, tab in ipairs(tabGroup.tabs) do
            local tabName = tab:GetName()

            tab:SetWidth(fixedWidth)
            tab:SetHeight(fixedHeight)

            local buttonMiddle = tab.Middle or tab.middleTexture or (tabName and _G[tabName.."Middle"])
            if buttonMiddle then
                buttonMiddle:SetWidth(fixedWidth - 40)
                buttonMiddle:SetHeight(fixedHeight)
            end
            local buttonMiddleDisabled = tab.MiddleDisabled or (tabName and _G[tabName.."MiddleDisabled"])
            if buttonMiddleDisabled then
                buttonMiddleDisabled:SetWidth(fixedWidth - 40)
                buttonMiddleDisabled:SetHeight(fixedHeight)
            end

            local left = tab.Left or tab.leftTexture or (tabName and _G[tabName.."Left"])
            if left then left:SetHeight(fixedHeight) end
            local leftDisabled = tab.LeftDisabled or (tabName and _G[tabName.."LeftDisabled"])
            if leftDisabled then leftDisabled:SetHeight(fixedHeight) end
            local right = tab.Right or tab.rightTexture or (tabName and _G[tabName.."Right"])
            if right then right:SetHeight(fixedHeight) end
            local rightDisabled = tab.RightDisabled or (tabName and _G[tabName.."RightDisabled"])
            if rightDisabled then rightDisabled:SetHeight(fixedHeight) end

            local highlightTexture = tab.HighlightTexture or (tabName and _G[tabName.."HighlightTexture"])
            if highlightTexture then
                highlightTexture:SetWidth(fixedWidth)
                highlightTexture:SetHeight(fixedHeight)
            end

            tab:ClearAllPoints()
            if i == 1 then
                tab:SetPoint("TOPLEFT", tabGroup.frame, "TOPLEFT", 0, -2)
            else
                tab:SetPoint("LEFT", tabGroup.tabs[i-1], "RIGHT", -10, 0)
            end

            -- Hook SetSelected para font size (solo una vez)
            if not tab._originalSetSelected then
                tab._originalSetSelected = tab.SetSelected
                tab.SetSelected = function(self, selected)
                    tab._originalSetSelected(self, selected)
                    local fontString = self.Text or self:GetFontString()
                    if fontString then
                        fontString:SetFont(fontString:GetFont(), selected and 12 or 11, "OUTLINE")
                    end
                    -- Reaplica después de cambio de tab
                    C_Timer.After(0, function() FixTabs(tabGroup) end)
                end
            end
        end

        tabGroup.borderoffset = 40
        if tabGroup.border then
            tabGroup.border:SetPoint("TOPLEFT", 1, -40)
        end
    end

    -- Guardar referencia global para usarla desde OpenConfigWindow
    Options._fixTabs = FixTabs

    -- Hook BuildTabs + trigger inicial
    if self.frame.children and self.frame.children[1] then
        local tabGroup = self.frame.children[1]
        if tabGroup.type == "TabGroup" then
            local originalBuildTabs = tabGroup.BuildTabs
            tabGroup.BuildTabs = function(self)
                -- Forzar ancho grande solo para que BuildTabs no parta los tabs en varias filas
                local oldWidth = self.frame.width
                self.frame.width = 5000
                originalBuildTabs(self)
                self.frame.width = oldWidth
                FixTabs(self)
            end

            tabGroup.frame.width = 5000
            tabGroup:BuildTabs()
            tabGroup.frame.width = 780
        end
    end

    self.frame:Hide()

    -- Enable the frame to be closed with Escape key
    _G["VoiceOverOptions"] = self.frame.frame
    tinsert(UISpecialFrames, "VoiceOverOptions")

end

function Options:OpenConfigWindow()
    if self.frame:IsShown() then
        PlaySound(SOUNDKIT.IG_MAINMENU_CLOSE)
        self.frame:Hide()
    else
        PlaySound(SOUNDKIT.IG_MAINMENU_OPEN)

        self.frame:Show()
        AceConfigDialog:Open("VoiceOver", self.frame)

        -- Reaplica tabs inmediatamente y también en el siguiente frame
        -- (BuildTabsOnUpdate de AceGUI resetea los tabs en el siguiente frame)
        local tabGroup = self.frame.children and self.frame.children[1]
        Options._fixTabs(tabGroup)
        C_Timer.After(0, function() Options._fixTabs(tabGroup) end)
    end
end
