local _, addonTable = ...
local WipedQoL = addonTable.WipedQoL
local UI = addonTable.UI
local TRANSLATE = addonTable.TRANSLATE or function(msg) return msg end

local PLAY_ICON = "Interface\\AddOns\\WipedQoL\\Icons\\play"
local PAUSE_ICON = "Interface\\AddOns\\WipedQoL\\Icons\\pause"
local STOP_ICON = "Interface\\AddOns\\WipedQoL\\Icons\\stop"

local SOUND_SECTIONS = {
    {
        sectionLabel = "Mount sound",
        description = "Plays when you receive a mount item in your inventory.",
        getSettings = "GetMountSoundSettings",
        setEnabled = "SetMountSoundEnabled",
        setSound = "SetMountSound",
        isPreviewPlaying = "IsMountSoundPreviewPlaying",
        playPreview = "PlayMountSoundPreview",
        stopPreview = "StopMountSoundPreview",
        refreshMethod = "RefreshMountSoundUI",
        hasMountAnimation = true,
    },
    {
        sectionLabel = "Death sound",
        description = "Plays when your character dies.",
        getSettings = "GetDeathSoundSettings",
        setEnabled = "SetDeathSoundEnabled",
        setSound = "SetDeathSound",
        setRandom = "SetDeathSoundRandomEnabled",
        setRandomSound = "SetDeathSoundRandomSound",
        isPreviewPlaying = "IsDeathSoundPreviewPlaying",
        playPreview = "PlayDeathSoundPreview",
        stopPreview = "StopDeathSoundPreview",
        refreshMethod = "RefreshDeathSoundUI",
    },
    {
        sectionLabel = "Group death sound",
        description = "Plays when a group member dies.",
        getSettings = "GetGroupDeathSoundSettings",
        setEnabled = "SetGroupDeathSoundEnabled",
        setSound = "SetGroupDeathSound",
        setRandom = "SetGroupDeathSoundRandomEnabled",
        setRandomSound = "SetGroupDeathSoundRandomSound",
        isPreviewPlaying = "IsGroupDeathSoundPreviewPlaying",
        playPreview = "PlayGroupDeathSoundPreview",
        stopPreview = "StopGroupDeathSoundPreview",
        refreshMethod = "RefreshGroupDeathSoundUI",
    },
    {
        sectionLabel = "Keystone sound",
        description = "Plays after successfully completing a keystone.",
        getSettings = "GetKeystoneCompletionSoundSettings",
        setEnabled = "SetKeystoneCompletionSoundEnabled",
        setSound = "SetKeystoneCompletionSound",
        setRandom = "SetKeystoneCompletionSoundRandomEnabled",
        setRandomSound = "SetKeystoneCompletionSoundRandomSound",
        isPreviewPlaying = "IsKeystoneCompletionSoundPreviewPlaying",
        playPreview = "PlayKeystoneCompletionSoundPreview",
        stopPreview = "StopKeystoneCompletionSoundPreview",
        refreshMethod = "RefreshKeystoneCompletionSoundUI",
    },
}

local SECTION_HEIGHT = 138
local SECTION_GAP = 12
local SOUND_DROPDOWN_WIDTH = 190
local CUSTOM_SOUND_PATH = "Interface\\Sounds\\"

local function CreatePreviewButton(parent, activeIcon, activeLabel)
    local button = CreateFrame("Button", nil, parent)
    button:SetSize(30, 26)
    UI.SkinButton(button)

    button.stateBackground = button:CreateTexture(nil, "BACKGROUND", nil, -7)
    button.stateBackground:SetPoint("TOPLEFT", button, "TOPLEFT", 1, -1)
    button.stateBackground:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -1, 1)
    button.stateBackground:SetTexture("Interface\\Buttons\\WHITE8X8")

    button.icon = button:CreateTexture(nil, "ARTWORK")
    button.icon:SetPoint("CENTER")
    button.icon:SetSize(14, 14)

    function button:SetPlaying(playing)
        self.playing = playing == true
        self.icon:SetTexture(self.playing and (activeIcon or PAUSE_ICON) or PLAY_ICON)
        if self.playing then
            self.stateBackground:SetVertexColor(0.62, 0.12, 0.12, 0.88)
        else
            self.stateBackground:SetVertexColor(0.12, 0.52, 0.22, 0.88)
        end
    end

    button:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:AddLine(TRANSLATE(self.playing and (activeLabel or "Pause") or "Play"))
        GameTooltip:Show()
    end)
    button:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    return button
end

local function CreateSoundInput(parent, width)
    local input = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
    input:SetSize(width, 27)
    input:SetAutoFocus(false)
    input:SetTextInsets(8, 8, 0, 0)
    UI.ApplyFont(input, 12, "")
    if input.Left then input.Left:Hide() end
    if input.Middle then input.Middle:Hide() end
    if input.Right then input.Right:Hide() end
    UI.SkinFrame(input, { light = true })
    return input
end

local function EnsureCustomSoundManager(addon)
    if addon.customSoundManager then
        return addon.customSoundManager
    end

    local manager = CreateFrame(
        "Frame",
        "WipedQoLCustomSoundManagerFrame",
        addon.mainWindow or UIParent,
        "BackdropTemplate"
    )
    manager:SetSize(580, 470)
    manager:SetPoint("CENTER", UIParent, "CENTER")
    manager:SetFrameStrata("DIALOG")
    manager:SetFrameLevel((addon.mainWindow and addon.mainWindow:GetFrameLevel() or 0) + 60)
    manager:SetMovable(true)
    manager:EnableMouse(true)
    manager:RegisterForDrag("LeftButton")
    manager:SetScript("OnDragStart", manager.StartMoving)
    manager:SetScript("OnDragStop", manager.StopMovingOrSizing)
    UI.SkinFrame(manager)

    local title = UI.CreateLabel(manager, TRANSLATE("Custom sounds"), 14)
    title:SetPoint("TOPLEFT", manager, "TOPLEFT", 14, -13)
    title:SetTextColor(1, 1, 1)

    local closeButton = CreateFrame("Button", nil, manager, "UIPanelCloseButton")
    closeButton:SetPoint("TOPRIGHT", manager, "TOPRIGHT", -5, -5)
    closeButton:SetScript("OnClick", function() manager:Hide() end)
    UI.SkinCloseButton(closeButton)

    local instructions = UI.CreateLabel(
        manager,
        TRANSLATE("1. Create the Interface\\Sounds folder in your World of Warcraft retail directory.\n2. Copy an .ogg or .mp3 file directly into this folder.\n3. Enter a display name and the exact file name below."),
        11
    )
    instructions:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -12)
    instructions:SetPoint("RIGHT", manager, "RIGHT", -14, 0)
    instructions:SetJustifyV("TOP")
    instructions:SetTextColor(0.8, 0.8, 0.8)

    local restartWarning = UI.CreateLabel(
        manager,
        TRANSLATE("Game restart required; /reload is not enough."),
        15
    )
    restartWarning:SetPoint("TOPLEFT", instructions, "BOTTOMLEFT", 0, -9)
    restartWarning:SetPoint("RIGHT", manager, "RIGHT", -14, 0)
    restartWarning:SetTextColor(1, 0.16, 0.12)
    UI.ApplyFont(restartWarning, 15, "OUTLINE")

    local nameLabel = UI.CreateLabel(manager, TRANSLATE("Display name"), 11)
    nameLabel:SetPoint("TOPLEFT", restartWarning, "BOTTOMLEFT", 0, -15)
    nameLabel:SetTextColor(0.72, 0.72, 0.72)

    local fileLabel = UI.CreateLabel(manager, TRANSLATE("File name"), 11)
    fileLabel:SetPoint("TOPLEFT", restartWarning, "BOTTOMLEFT", 272, -15)
    fileLabel:SetTextColor(0.72, 0.72, 0.72)

    local nameInput = CreateSoundInput(manager, 254)
    nameInput:SetPoint("TOPLEFT", nameLabel, "BOTTOMLEFT", 0, -5)
    nameInput:SetMaxLetters(80)

    local fileInput = CreateSoundInput(manager, 280)
    fileInput:SetPoint("TOPLEFT", fileLabel, "BOTTOMLEFT", 0, -5)
    fileInput:SetMaxLetters(120)

    local pathHint = UI.CreateLabel(manager, CUSTOM_SOUND_PATH, 10)
    pathHint:SetPoint("TOPLEFT", fileInput, "BOTTOMLEFT", 2, -4)
    pathHint:SetTextColor(0.5, 0.58, 0.68)

    local addButton = CreateFrame("Button", nil, manager, "UIPanelButtonTemplate")
    addButton:SetSize(126, 26)
    addButton:SetPoint("TOPLEFT", nameInput, "BOTTOMLEFT", 0, -22)
    addButton:SetText(TRANSLATE("Add sound"))
    UI.SkinButton(addButton)

    manager.statusLabel = UI.CreateLabel(manager, "", 10)
    manager.statusLabel:SetPoint("LEFT", addButton, "RIGHT", 10, 0)
    manager.statusLabel:SetPoint("RIGHT", manager, "RIGHT", -14, 0)
    manager.statusLabel:SetJustifyH("LEFT")

    local listTitle = UI.CreateLabel(manager, TRANSLATE("Added sounds"), 11)
    listTitle:SetPoint("TOPLEFT", addButton, "BOTTOMLEFT", 0, -15)
    listTitle:SetTextColor(0.72, 0.72, 0.72)

    local listScroll = CreateFrame("ScrollFrame", nil, manager)
    listScroll:SetPoint("TOPLEFT", listTitle, "BOTTOMLEFT", 0, -6)
    listScroll:SetPoint("BOTTOMRIGHT", manager, "BOTTOMRIGHT", -34, 14)
    listScroll:EnableMouseWheel(true)
    if listScroll.SetClipsChildren then
        listScroll:SetClipsChildren(true)
    end

    local listChild = CreateFrame("Frame", nil, listScroll)
    listChild:SetSize(1, 1)
    listScroll:SetScrollChild(listChild)

    local emptyLabel = UI.CreateLabel(listScroll, TRANSLATE("No custom sounds added."), 11)
    emptyLabel:SetPoint("CENTER", listScroll, "CENTER", 0, 0)
    emptyLabel:SetTextColor(0.62, 0.62, 0.62)

    local scrollbar = CreateFrame("Slider", nil, manager, "BackdropTemplate")
    scrollbar:SetPoint("TOPRIGHT", listScroll, "TOPRIGHT", 20, 0)
    scrollbar:SetPoint("BOTTOMRIGHT", listScroll, "BOTTOMRIGHT", 20, 0)
    scrollbar:SetWidth(10)
    scrollbar:SetOrientation("VERTICAL")
    scrollbar:SetValueStep(46)
    UI.SkinFrame(scrollbar, { light = true })
    scrollbar:SetThumbTexture("Interface\\Buttons\\WHITE8X8")
    local thumb = scrollbar:GetThumbTexture()
    if thumb then
        thumb:SetSize(10, 30)
        UI.SetAddonColor(thumb, "SetVertexColor")
    end
    scrollbar:SetScript("OnValueChanged", function(_, value)
        listScroll:SetVerticalScroll(value or 0)
    end)
    listScroll:SetScript("OnMouseWheel", function(_, delta)
        local minValue, maxValue = scrollbar:GetMinMaxValues()
        scrollbar:SetValue(math.max(minValue, math.min(
            maxValue,
            (scrollbar:GetValue() or 0) - (delta * 46)
        )))
    end)

    manager.rows = {}
    local function EnsureRow(index)
        local row = manager.rows[index]
        if row then
            return row
        end

        row = CreateFrame("Frame", nil, listChild, "BackdropTemplate")
        row:SetHeight(42)
        UI.SkinFrame(row, { light = true })

        row.name = UI.CreateLabel(row, "", 11)
        row.name:SetPoint("TOPLEFT", row, "TOPLEFT", 9, -6)
        row.name:SetPoint("RIGHT", row, "RIGHT", -80, 0)
        row.name:SetTextColor(1, 1, 1)

        row.path = UI.CreateLabel(row, "", 9)
        row.path:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 9, 5)
        row.path:SetPoint("RIGHT", row, "RIGHT", -80, 0)
        row.path:SetTextColor(0.52, 0.6, 0.7)

        row.playButton = CreatePreviewButton(row)
        row.playButton:SetSize(28, 26)
        row.playButton:SetPoint("RIGHT", row, "RIGHT", -39, 0)
        row.playButton:SetPlaying(false)
        row.playButton:SetScript("OnClick", function()
            local played = addon:PlayCustomSoundPreview(row.soundKey)
            if played then
                manager.statusLabel:SetText("")
            else
                manager.statusLabel:SetText(TRANSLATE("Sound file could not be played."))
                manager.statusLabel:SetTextColor(1, 0.3, 0.24)
            end
        end)

        row.removeButton = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
        row.removeButton:SetSize(28, 26)
        row.removeButton:SetPoint("RIGHT", row, "RIGHT", -6, 0)
        row.removeButton:SetText("x")
        row.removeButton:SetScript("OnClick", function()
            if addon:RemoveCustomSound(row.soundKey) then
                manager.statusLabel:SetText(TRANSLATE("Sound removed."))
                manager.statusLabel:SetTextColor(0.42, 0.9, 0.5)
                manager:Refresh()
            end
        end)
        UI.SkinButton(row.removeButton)

        manager.rows[index] = row
        return row
    end

    function manager:Refresh()
        for _, row in ipairs(self.rows) do
            row:Hide()
        end

        local sounds = addon:GetCustomSounds()
        emptyLabel:SetShown(#sounds == 0)
        for index, entry in ipairs(sounds) do
            local row = EnsureRow(index)
            row.soundKey = entry.key
            row.name:SetText(entry.name or "")
            row.path:SetText(CUSTOM_SOUND_PATH .. tostring(entry.file or ""))
            row:ClearAllPoints()
            row:SetPoint("TOPLEFT", listChild, "TOPLEFT", 0, -((index - 1) * 46))
            row:SetPoint("RIGHT", listChild, "RIGHT", 0, 0)
            row:Show()
        end

        local contentHeight = math.max(#sounds * 46, 1)
        listChild:SetSize(math.max(listScroll:GetWidth() or 1, 1), contentHeight)
        local maxScroll = math.max(contentHeight - (listScroll:GetHeight() or 0), 0)
        scrollbar:SetMinMaxValues(0, maxScroll)
        scrollbar:SetValue(math.min(scrollbar:GetValue() or 0, maxScroll))
        scrollbar:SetShown(maxScroll > 0)
    end

    local function AddSound()
        local success, reason = addon:AddCustomSound(nameInput:GetText(), fileInput:GetText())
        if success then
            nameInput:SetText("")
            fileInput:SetText("")
            manager.statusLabel:SetText(TRANSLATE("Sound added."))
            manager.statusLabel:SetTextColor(0.42, 0.9, 0.5)
            manager:Refresh()
            return
        end

        local messages = {
            missingName = "Name is required.",
            invalidFile = "Enter a valid .ogg or .mp3 file name.",
            duplicateFile = "This sound file has already been added.",
        }
        manager.statusLabel:SetText(TRANSLATE(messages[reason] or "The sound could not be added."))
        manager.statusLabel:SetTextColor(1, 0.3, 0.24)
    end

    addButton:SetScript("OnClick", AddSound)
    nameInput:SetScript("OnEnterPressed", function(self)
        fileInput:SetFocus()
        self:ClearFocus()
    end)
    fileInput:SetScript("OnEnterPressed", function(self)
        AddSound()
        self:ClearFocus()
    end)
    manager:SetScript("OnShow", function(self)
        self.statusLabel:SetText("")
        self:Refresh()
    end)
    manager:Hide()

    addon.RefreshCustomSoundsUI = function()
        if manager:IsShown() then
            manager:Refresh()
        end
    end
    addon.customSoundManager = manager
    return manager
end

local function CreateSoundSection(addon, content, config, index)
    local settings = addon[config.getSettings](addon)
    local checkbox
    local dropdown
    local previewButton
    local animationSettings = config.hasMountAnimation and addon:GetMountAnimationSettings() or nil
    local animationCheckbox
    local animationPreviewButton
    local randomCheckbox
    local randomDropdown

    local function Refresh()
        settings = addon[config.getSettings](addon)
        local enabled = settings.enabled == true
        local randomEnabled = config.setRandom and settings.random == true

        if checkbox and checkbox.Refresh then
            checkbox.Refresh()
        end

        dropdown:SetAlpha(enabled and not randomEnabled and 1 or 0.45)
        if dropdown.RefreshText then
            dropdown.RefreshText()
        end
        if dropdown._wqolClickOverlay then
            dropdown._wqolClickOverlay:EnableMouse(enabled and not randomEnabled)
        end

        previewButton:SetAlpha(enabled and 1 or 0.45)
        previewButton:EnableMouse(enabled)
        previewButton:SetPlaying(addon[config.isPreviewPlaying](addon))

        if animationCheckbox then
            animationSettings = addon:GetMountAnimationSettings()
            if animationCheckbox.Refresh then
                animationCheckbox.Refresh()
            end
            if animationPreviewButton then
                local animationEnabled = animationSettings.enabled == true
                animationPreviewButton:SetAlpha(animationEnabled and 1 or 0.45)
                animationPreviewButton:EnableMouse(animationEnabled)
                animationPreviewButton:SetPlaying(addon:IsMountAnimationPreviewPlaying())
            end
        end

        if randomCheckbox then
            randomCheckbox:SetAlpha(enabled and 1 or 0.45)
            randomCheckbox:EnableMouse(enabled)
            if randomCheckbox.Refresh then
                randomCheckbox.Refresh()
            end
        end

        if randomDropdown then
            local listEnabled = enabled and randomEnabled
            randomDropdown:SetAlpha(listEnabled and 1 or 0.45)
            if randomDropdown._wqolClickOverlay then
                randomDropdown._wqolClickOverlay:EnableMouse(listEnabled)
            end
            if not listEnabled and randomDropdown.Close then
                randomDropdown.Close()
            end
            if randomDropdown.RefreshText then
                randomDropdown.RefreshText()
            end
        end
    end

    addon[config.refreshMethod] = Refresh
    if config.hasMountAnimation then
        addon.RefreshMountAnimationUI = Refresh
    end

    local section = UI.CreateSection(content, TRANSLATE(config.sectionLabel), TRANSLATE(config.description))
    local column = (index - 1) % 2
    local row = math.floor((index - 1) / 2)
    local offsetY = -14 - (row * (SECTION_HEIGHT + SECTION_GAP))
    if column == 0 then
        section:SetPoint("TOPLEFT", content, "TOPLEFT", 14, offsetY)
        section:SetPoint("TOPRIGHT", content, "TOP", -6, offsetY)
    else
        section:SetPoint("TOPLEFT", content, "TOP", 6, offsetY)
        section:SetPoint("TOPRIGHT", content, "TOPRIGHT", -14, offsetY)
    end
    section:SetHeight(SECTION_HEIGHT)

    checkbox = UI.CreateSectionToggle(section, function()
        return settings.enabled == true
    end, function(value)
        addon[config.setEnabled](addon, value)
        Refresh()
    end)

    dropdown = UI.CreateDropdown(section, SOUND_DROPDOWN_WIDTH, function()
        return addon:GetSoundOptions()
    end, function()
        return settings.sound
    end, function(value)
        addon[config.setSound](addon, value)
        settings = addon[config.getSettings](addon)
    end)
    dropdown:SetPoint("TOPLEFT", section.description, "BOTTOMLEFT", -20, -8)

    if config.hasMountAnimation then
        animationCheckbox = UI.CreateCheckBox(section, TRANSLATE("Animation"), function()
            return animationSettings.enabled == true
        end, function(value)
            addon:SetMountAnimationEnabled(value)
            Refresh()
        end)
        animationCheckbox:SetPoint("TOPLEFT", dropdown, "BOTTOMLEFT", 20, -6)
        animationCheckbox:SetSize(140, 24)

        animationPreviewButton = CreatePreviewButton(section, STOP_ICON, "Stop")
        animationPreviewButton:SetPoint("LEFT", animationCheckbox, "RIGHT", 8, 0)
        animationPreviewButton:SetScript("OnClick", function()
            if addon:IsMountAnimationPreviewPlaying() then
                addon:StopMountAnimationPreview()
            else
                addon:PlayMountAnimationPreview()
            end
            Refresh()
        end)
    end

    if config.setRandom then
        randomCheckbox = UI.CreateCheckBox(section, TRANSLATE("Random"), function()
            return settings.random == true
        end, function(value)
            addon[config.setRandom](addon, value)
            Refresh()
        end)
        randomCheckbox:SetPoint("TOPLEFT", dropdown, "BOTTOMLEFT", 20, -6)
        randomCheckbox:SetSize(100, 24)

        randomDropdown = UI.CreateMultiSelectDropdown(section, 154, function()
            return addon:GetSoundOptions()
        end, function(soundName)
            return settings.randomSounds and settings.randomSounds[soundName] == true
        end, function(soundName, selected)
            addon[config.setRandomSound](addon, soundName, selected)
            settings = addon[config.getSettings](addon)
        end, function()
            return addon:GetRandomSoundSelectionText(settings)
        end, {
            emptyText = "No sounds available",
            menuWidth = 280,
        })
        randomDropdown:SetPoint("LEFT", randomCheckbox, "RIGHT", 4, 0)
    end

    previewButton = CreatePreviewButton(section)
    previewButton:SetPoint("LEFT", dropdown, "RIGHT", 0, 0)
    previewButton:SetScript("OnClick", function()
        if addon[config.isPreviewPlaying](addon) then
            addon[config.stopPreview](addon)
        else
            addon[config.playPreview](addon)
        end
        Refresh()
    end)

    Refresh()
end

WipedQoL:RegisterTabBuilder("sounds", function(addon, parent)
    local frame = CreateFrame("Frame", nil, parent)

    for index, config in ipairs(SOUND_SECTIONS) do
        CreateSoundSection(addon, frame, config, index)
    end

    local customSoundsButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    customSoundsButton:SetSize(220, 24)
    customSoundsButton:SetPoint("BOTTOM", frame, "BOTTOM", 0, 0)
    customSoundsButton:SetText(TRANSLATE("Manage custom sounds"))
    customSoundsButton:SetScript("OnClick", function()
        EnsureCustomSoundManager(addon):Show()
    end)
    UI.SkinButton(customSoundsButton)

    return frame
end)
