local _, addonTable = ...
local WipedQoL = addonTable.WipedQoL

local WHITE_TEXTURE = "Interface\\Buttons\\WHITE8X8"
local RESIZE_GRIP_TEXTURE = "Interface\\AddOns\\WipedQoL\\Icons\\triangle"
local BLIZZARD_TIMER_FILL_WIDTH = 234

local function GetClassColor()
    if addonTable.GetClassColor then
        return addonTable.GetClassColor()
    end

    return 1, 0.82, 0
end

local function SkinFrame(frame, options)
    if addonTable.SkinFrame then
        addonTable.SkinFrame(frame, options)
    end
end

local function SkinButton(button)
    if addonTable.SkinButton then
        addonTable.SkinButton(button)
    end
end

local function SkinCloseButton(button)
    if addonTable.SkinCloseButton then
        addonTable.SkinCloseButton(button)
    elseif addonTable.SkinButton then
        addonTable.SkinButton(button)
    end
end

local function ApplyFontToFrame(frame)
    if addonTable.ApplyFontToFrame then
        addonTable.ApplyFontToFrame(frame)
    end
end

local function HideTexture(texture)
    if not texture then
        return
    end

    if texture.SetTexture then
        texture:SetTexture(nil)
    end
    if texture.SetAtlas then
        texture:SetAtlas(nil)
    end
    texture:SetAlpha(0)
    texture:Hide()
end

local function HideTextureRegions(frame)
    if not frame or not frame.GetRegions then
        return
    end

    for _, region in ipairs({ frame:GetRegions() }) do
        if region and region.GetObjectType and region:GetObjectType() == "Texture" then
            HideTexture(region)
        end
    end
end

local function CreateFlatBorder(frame)
    if not frame or frame._wqolFlatBorder then
        return frame and frame._wqolFlatBorder
    end

    local border = {}
    border.top = frame:CreateTexture(nil, "OVERLAY", nil, 7)
    border.top:SetPoint("TOPLEFT")
    border.top:SetPoint("TOPRIGHT")
    border.top:SetHeight(1)

    border.bottom = frame:CreateTexture(nil, "OVERLAY", nil, 7)
    border.bottom:SetPoint("BOTTOMLEFT")
    border.bottom:SetPoint("BOTTOMRIGHT")
    border.bottom:SetHeight(1)

    border.left = frame:CreateTexture(nil, "OVERLAY", nil, 7)
    border.left:SetPoint("TOPLEFT")
    border.left:SetPoint("BOTTOMLEFT")
    border.left:SetWidth(1)

    border.right = frame:CreateTexture(nil, "OVERLAY", nil, 7)
    border.right:SetPoint("TOPRIGHT")
    border.right:SetPoint("BOTTOMRIGHT")
    border.right:SetWidth(1)

    for _, texture in pairs(border) do
        texture:SetTexture(WHITE_TEXTURE)
    end

    frame._wqolFlatBorder = border
    return border
end

local function SetFlatBorderColor(frame, r, g, b, a)
    local border = CreateFlatBorder(frame)
    if not border then
        return
    end

    for _, texture in pairs(border) do
        texture:SetVertexColor(r, g, b, a or 1)
    end
end

local function SkinLootHistoryItem(item)
    if not item then
        return
    end

    local icon = item.icon or item.Icon
    local iconBorder = item.IconBorder
    local r, g, b = 0.45, 0.45, 0.45
    if iconBorder and iconBorder.GetVertexColor then
        r, g, b = iconBorder:GetVertexColor()
    end

    if not item._wqolRaidLootHistorySkinned then
        SkinFrame(item, { light = true })
        if icon then
            icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        end
        item._wqolRaidLootHistorySkinned = true
    end

    HideTexture(iconBorder)
    SetFlatBorderColor(item, r or 0.45, g or 0.45, b or 0.45, 0.95)
end

local function SkinLootHistoryElement(element)
    if not element then
        return
    end

    local item = element.Item
    if not element._wqolRaidLootHistorySkinned then
        local artFrame = element.BackgroundArtFrame
        if artFrame then
            HideTexture(artFrame.NameFrame)
            HideTexture(artFrame.BorderFrame)
            SkinFrame(artFrame, { light = true })
        end
        HideTexture(element.NameFrame)
        HideTexture(element.BorderFrame)

        ApplyFontToFrame(element)
        element._wqolRaidLootHistorySkinned = true
    end

    -- Blizzard reapplies the quality border whenever a recycled row gets new data.
    SkinLootHistoryItem(item)
end

local function SkinVisibleLootHistoryElements(frame)
    local scrollBox = frame and frame.ScrollBox
    if scrollBox and scrollBox.ForEachFrame then
        scrollBox:ForEachFrame(SkinLootHistoryElement)
    end
end

local function SkinLootHistoryScrollArrow(button, label)
    if not button then
        return
    end

    HideTextureRegions(button)
    SkinButton(button)

    local text = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    text:SetPoint("CENTER", button, "CENTER", 0, label == "^" and -1 or 1)
    if addonTable.ApplyFont then
        addonTable.ApplyFont(text, 10, "OUTLINE")
    end
    text:SetText(label)
    text:SetTextColor(0.82, 0.82, 0.82)
    button._wqolFlatArrow = text
end

local function SkinLootHistoryScrollBar(scrollBar)
    if not scrollBar or scrollBar._wqolRaidLootHistorySkinned then
        return
    end

    HideTexture(scrollBar.Background)
    SkinLootHistoryScrollArrow(scrollBar.Back, "^")
    SkinLootHistoryScrollArrow(scrollBar.Forward, "v")

    local frame = scrollBar:GetParent()
    local scrollBox = frame and frame.ScrollBox
    if scrollBox then
        scrollBar:ClearAllPoints()
        scrollBar:SetPoint("TOPLEFT", scrollBox, "TOPRIGHT", 3, 2)
        scrollBar:SetPoint("BOTTOMLEFT", scrollBox, "BOTTOMRIGHT", 0, 22)
    end

    if scrollBar.Track and scrollBar.Track.DisableDrawLayer then
        scrollBar.Track:DisableDrawLayer("ARTWORK")
    end

    local thumb = scrollBar.GetThumb and scrollBar:GetThumb()
    if thumb then
        if thumb.DisableDrawLayer then
            thumb:DisableDrawLayer("ARTWORK")
            thumb:DisableDrawLayer("BACKGROUND")
        end

        local fill = thumb:CreateTexture(nil, "OVERLAY")
        fill:SetPoint("TOPLEFT", thumb, "TOPLEFT", 2, -1)
        fill:SetPoint("BOTTOMRIGHT", thumb, "BOTTOMRIGHT", -2, 1)
        fill:SetTexture(WHITE_TEXTURE)
        if addonTable.SetAddonColorTarget then
            addonTable.SetAddonColorTarget(fill, "SetVertexColor", 1, 1)
        else
            fill:SetVertexColor(GetClassColor())
        end
        thumb._wqolFlatFill = fill
    end

    scrollBar._wqolRaidLootHistorySkinned = true
end

local function HideNamedVisual(owner, key)
    local visual = owner and owner[key]
    if not visual then
        return
    end

    local objectType = visual.GetObjectType and visual:GetObjectType()
    if objectType == "Texture" then
        HideTexture(visual)
    elseif objectType == "FontString" then
        visual:SetAlpha(0)
        visual:Hide()
    elseif visual.GetRegions then
        HideTextureRegions(visual)
    end
end

local function SkinLootHistoryDropdownHighlightTexture(texture, r, g, b)
    if not texture then
        return
    end

    if texture.SetAtlas then
        texture:SetAtlas(nil)
    end
    texture:SetTexture(WHITE_TEXTURE)
    texture:SetVertexColor(r, g, b, 1)
    texture:SetAlpha(0.2)
    if hooksecurefunc and not texture._wqolRaidLootHistoryAlphaHooked then
        hooksecurefunc(texture, "SetAlpha", function(self, alpha)
            if self._wqolRaidLootHistoryApplyingAlpha
                or not alpha
                or alpha <= 0
                or math.abs(alpha - 0.2) < 0.001
            then
                return
            end
            self._wqolRaidLootHistoryApplyingAlpha = true
            self:SetAlpha(0.2)
            self._wqolRaidLootHistoryApplyingAlpha = nil
        end)
        texture._wqolRaidLootHistoryAlphaHooked = true
    end
    if texture.SetBlendMode then
        texture:SetBlendMode("BLEND")
    end
end

local function GetLootHistoryDropdownMenuButtonText(button)
    if not button then
        return nil
    end
    if button.fontString then
        return button.fontString
    end
    for _, key in ipairs({ "Text", "text", "Label", "label" }) do
        local region = button[key]
        if region and region.GetObjectType and region:GetObjectType() == "FontString" then
            return region
        end
    end
    for _, region in ipairs({ button:GetRegions() }) do
        if region and region.GetObjectType and region:GetObjectType() == "FontString" then
            return region
        end
    end
    return nil
end

local function SetLootHistoryDropdownMenuButtonSelected(button, selected)
    if not button then
        return
    end

    local text = GetLootHistoryDropdownMenuButtonText(button)
    if text then
        if selected == true then
            local r, g, b = GetClassColor()
            text:SetTextColor(r, g, b, 1)
        elseif button.IsEnabled and not button:IsEnabled() then
            text:SetTextColor(0.5, 0.5, 0.5, 1)
        else
            text:SetTextColor(0.92, 0.92, 0.92, 1)
        end
    end
end

local function HideLootHistoryDropdownSelectionVisual(texture)
    if not texture then
        return
    end

    texture:SetAlpha(0)
end

local function SkinLootHistoryDropdownMenuButton(button)
    if not button then
        return
    end

    local r, g, b = GetClassColor()
    local selectionBorder = button.leftTexture1
    local selectionMark = button.leftTexture2
    local selected = selectionMark ~= nil
    if button.GetElementDescription then
        local description = button:GetElementDescription()
        if description and description.IsSelected then
            local ok, value = pcall(description.IsSelected, description)
            if ok then
                selected = value == true
            end
        end
    end

    HideLootHistoryDropdownSelectionVisual(selectionBorder)
    HideLootHistoryDropdownSelectionVisual(selectionMark)

    local text = GetLootHistoryDropdownMenuButtonText(button)
    if text then
        text:ClearAllPoints()
        text:SetPoint("LEFT", button, "LEFT", 8, 0)
        text:SetPoint("RIGHT", button, "RIGHT", -8, 0)
        text:SetJustifyH("LEFT")
    end
    SetLootHistoryDropdownMenuButtonSelected(button, selected)

    if button.GetHighlightTexture then
        local highlight = button:GetHighlightTexture()
        SkinLootHistoryDropdownHighlightTexture(highlight, r, g, b)
    end
    SkinLootHistoryDropdownHighlightTexture(button.highlight, r, g, b)
    for _, key in ipairs({ "Background", "BG" }) do
        HideNamedVisual(button, key)
    end

end

local function SkinLootHistoryDropdownMenuChildren(frame, depth, visited)
    if not frame or depth > 8 then
        return
    end

    visited = visited or {}
    if visited[frame] then
        return
    end
    visited[frame] = true

    local objectType = frame.GetObjectType and frame:GetObjectType()
    if objectType == "Button" or objectType == "CheckButton" then
        SkinLootHistoryDropdownMenuButton(frame)
        return
    end

    if frame.ForEachFrame then
        frame:ForEachFrame(function(child)
            SkinLootHistoryDropdownMenuChildren(child, depth + 1, visited)
        end)
    end

    if frame.GetChildren then
        for _, child in ipairs({ frame:GetChildren() }) do
            SkinLootHistoryDropdownMenuChildren(child, depth + 1, visited)
        end
    end
end

local function SkinExistingMenuTexture(texture, r, g, b, a)
    if not texture
        or not texture.GetObjectType
        or texture:GetObjectType() ~= "Texture"
    then
        return
    end

    if texture.SetAtlas then
        texture:SetAtlas(nil)
    end
    texture:SetTexture(WHITE_TEXTURE)
    texture:SetVertexColor(r, g, b, a)
    texture:SetAlpha(1)
    texture:Show()
end

local function SkinLootHistoryDropdownMenuSurface(menu)
    if not menu then
        return
    end

    for _, region in ipairs({ menu:GetRegions() }) do
        SkinExistingMenuTexture(region, 0.025, 0.025, 0.025, 0.96)
    end

    local nineSlice = menu.NineSlice
    if nineSlice then
        local center = nineSlice.Center or nineSlice.CenterTile
        for _, region in ipairs({ nineSlice:GetRegions() }) do
            if region == center then
                SkinExistingMenuTexture(region, 0.025, 0.025, 0.025, 0.96)
            else
                SkinExistingMenuTexture(region, 1, 1, 1, 0.12)
            end
        end
        nineSlice:Show()
    end

    for _, key in ipairs({ "Background", "BG" }) do
        local texture = menu[key]
        SkinExistingMenuTexture(texture, 0.025, 0.025, 0.025, 0.96)
    end
end

local function SkinLootHistoryDropdownMenu(menu)
    if not menu then
        return
    end

    -- Blizzard's menu frames reject CreateTexture calls. Reuse their existing
    -- regions so the menu can also be skinned while protected.
    SkinLootHistoryDropdownMenuSurface(menu)
    SkinLootHistoryDropdownMenuChildren(menu, 0)
    if C_Timer and C_Timer.After then
        C_Timer.After(0, function()
            if menu:IsShown() then
                SkinLootHistoryDropdownMenuSurface(menu)
                SkinLootHistoryDropdownMenuChildren(menu, 0)
            end
        end)
    end
end

local raidLootHistoryDropdownMenuHooked = false

local function IsLootHistoryDropdownOwner(owner, dropdown)
    local current = owner
    for _ = 1, 8 do
        if current == dropdown then
            return true
        end
        current = current and current.GetParent and current:GetParent()
        if not current then
            break
        end
    end
    return false
end

local function HookLootHistoryDropdownMenu(dropdown)
    if raidLootHistoryDropdownMenuHooked
        or not Menu
        or not Menu.GetManager
        or not hooksecurefunc
    then
        return
    end

    local manager = Menu.GetManager()
    if not manager or not manager.OpenMenu then
        return
    end

    hooksecurefunc(manager, "OpenMenu", function(menuManager, ownerRegion, menuDescription)
        if not IsLootHistoryDropdownOwner(ownerRegion, dropdown) then
            return
        end

        if menuDescription and menuDescription.AddMenuAcquiredCallback then
            menuDescription:AddMenuAcquiredCallback(SkinLootHistoryDropdownMenu)
        end

        if menuManager.GetOpenMenu then
            SkinLootHistoryDropdownMenu(menuManager:GetOpenMenu())
        end
    end)
    raidLootHistoryDropdownMenuHooked = true
end

local function SkinLootHistoryDropdown(dropdown)
    if not dropdown or dropdown._wqolRaidLootHistorySkinned then
        return
    end

    HideTextureRegions(dropdown)
    for _, key in ipairs({ "Left", "Middle", "Right", "Background", "BG", "Arrow", "ArrowTexture", "NineSlice" }) do
        HideNamedVisual(dropdown, key)
    end

    local dropdownButton = dropdown.Button or dropdown.DropDownButton
    if dropdownButton and dropdownButton.GetRegions then
        HideTextureRegions(dropdownButton)
    end

    SkinFrame(dropdown, { light = true })
    SetFlatBorderColor(dropdown, 1, 1, 1, 0.12)
    ApplyFontToFrame(dropdown)

    local arrow = dropdown:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    arrow:SetPoint("RIGHT", dropdown, "RIGHT", -9, 1)
    if addonTable.ApplyFont then
        addonTable.ApplyFont(arrow, 11, "OUTLINE")
    end
    arrow:SetText("v")
    if addonTable.SetAddonColorTarget then
        addonTable.SetAddonColorTarget(arrow, "SetTextColor", 1, 0.8)
    else
        local r, g, b = GetClassColor()
        arrow:SetTextColor(r, g, b, 0.8)
    end

    local function SetHovered(hovered)
        local r, g, b = GetClassColor()
        if hovered then
            SetFlatBorderColor(dropdown, r, g, b, 0.8)
            arrow:SetAlpha(1)
        else
            SetFlatBorderColor(dropdown, 1, 1, 1, 0.12)
            arrow:SetAlpha(0.8)
        end
    end

    dropdown:HookScript("OnEnter", function()
        SetHovered(true)
    end)
    dropdown:HookScript("OnLeave", function()
        SetHovered(false)
    end)
    if dropdownButton and dropdownButton.HookScript then
        dropdownButton:HookScript("OnEnter", function()
            SetHovered(true)
        end)
        dropdownButton:HookScript("OnLeave", function()
            SetHovered(dropdown:IsMouseOver())
        end)
    end

    HookLootHistoryDropdownMenu(dropdown)
    dropdown._wqolRaidLootHistorySkinned = true
end

local function SkinLootHistoryTimer(timer, dropdown)
    if not timer or timer._wqolRaidLootHistorySkinned then
        return
    end

    HideTexture(timer.Background)
    HideTexture(timer.Border)
    SkinFrame(timer, { light = true })

    if dropdown then
        timer:ClearAllPoints()
        timer:SetPoint("TOPLEFT", dropdown, "BOTTOMLEFT", 0, -6)
        timer:SetPoint("TOPRIGHT", dropdown, "BOTTOMRIGHT", 0, -6)
    end

    if timer.Fill then
        timer.Fill:SetTexture(WHITE_TEXTURE)
        timer.Fill:SetVertexColor(WipedQoL:GetRaidLootHistoryTimerColor())
        timer.Fill:ClearAllPoints()
        timer.Fill:SetPoint("TOPLEFT", timer, "TOPLEFT", 0, -1)
        timer.Fill:SetPoint("BOTTOMLEFT", timer, "BOTTOMLEFT", 0, 1)

        if not timer.Fill._wqolWidthHooked then
            hooksecurefunc(timer.Fill, "SetWidth", function(fill, width)
                if fill._wqolApplyingWidth then
                    return
                end

                local timerWidth = timer:GetWidth() or 0
                if timerWidth <= 0 then
                    return
                end

                fill._wqolApplyingWidth = true
                fill:SetWidth((tonumber(width) or 0) * timerWidth / BLIZZARD_TIMER_FILL_WIDTH)
                fill._wqolApplyingWidth = nil
            end)
            timer.Fill._wqolWidthHooked = true
            timer.Fill:SetWidth(timer.Fill:GetWidth() or BLIZZARD_TIMER_FILL_WIDTH)
        end
    end

    timer._wqolRaidLootHistorySkinned = true
end

local function SkinLootHistoryResizeButton(frame, button)
    if not frame or not button or button._wqolRaidLootHistorySkinned then
        return
    end

    HideTextureRegions(button)
    if button.SetBackdrop then
        button:SetBackdrop(nil)
    end
    for _, regionKey in ipairs({ "backdrop", "Backdrop", "Background", "BG", "NineSlice" }) do
        local region = button[regionKey]
        if region and region.Hide then
            region:Hide()
        end
    end

    button:ClearAllPoints()
    if frame.ScrollBar then
        button:SetPoint("TOP", frame.ScrollBar, "BOTTOM", 0, -4)
    else
        button:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -7, 6)
    end
    button:SetSize(20, 20)
    button:SetHitRectInsets(-4, -4, -4, -4)

    local r, g, b = GetClassColor()
    button.grip = button:CreateTexture(nil, "OVERLAY", nil, 7)
    button.grip:SetAllPoints(button)
    button.grip:SetTexture(RESIZE_GRIP_TEXTURE)
    if addonTable.SetAddonColorTarget then
        addonTable.SetAddonColorTarget(button.grip, "SetVertexColor", 1, 0.95)
    else
        button.grip:SetVertexColor(r, g, b, 0.95)
    end
    button.grip:SetAlpha(0.72)

    button:HookScript("OnEnter", function(resizeButton)
        if resizeButton.grip then
            resizeButton.grip:SetAlpha(1)
        end
    end)
    button:HookScript("OnLeave", function(resizeButton)
        if resizeButton.grip then
            resizeButton.grip:SetAlpha(0.72)
        end
    end)
    button:HookScript("OnMouseDown", function(resizeButton)
        if resizeButton.grip then
            resizeButton.grip:SetAlpha(0.82)
        end
    end)
    button:HookScript("OnMouseUp", function(resizeButton)
        if resizeButton.grip then
            resizeButton.grip:SetAlpha(resizeButton:IsMouseOver() and 1 or 0.72)
        end
    end)

    button._wqolRaidLootHistorySkinned = true
end


function WipedQoL:SkinRaidLootHistoryFrame()
    local frame = _G.GroupLootHistoryFrame
    if not frame then
        return false
    end

    if not frame._wqolRaidLootHistorySkinned then
        SkinFrame(frame)
        HideTexture(frame.Bg)

        WipedQoL:CreateTopAccentBar(frame)

        local closeButton = frame.ClosePanelButton or frame.CloseButton
        if closeButton then
            SkinCloseButton(closeButton)
            closeButton:ClearAllPoints()
            closeButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -6, -6)
        end

        SkinLootHistoryDropdown(frame.EncounterDropdown)

        SkinLootHistoryTimer(frame.Timer, frame.EncounterDropdown)
        SkinLootHistoryScrollBar(frame.ScrollBar)
        SkinLootHistoryResizeButton(frame, frame.ResizeButton)
        ApplyFontToFrame(frame)

        if frame.ScrollBox and not frame.ScrollBox._wqolRaidLootHistoryHooked then
            hooksecurefunc(frame.ScrollBox, "Update", function()
                SkinVisibleLootHistoryElements(frame)
            end)
            frame.ScrollBox._wqolRaidLootHistoryHooked = true
        end

        frame:HookScript("OnShow", function()
            SkinVisibleLootHistoryElements(frame)
            if C_Timer and C_Timer.After then
                C_Timer.After(0, function()
                    SkinVisibleLootHistoryElements(frame)
                end)
            end
        end)

        frame._wqolRaidLootHistorySkinned = true
    end

    SkinVisibleLootHistoryElements(frame)
    return true
end

function WipedQoL:GetRaidLootHistorySkinSettings()
    local modules = self.db and self.db.profile and self.db.profile.modules
    if not modules then
        return nil
    end

    modules.raidLootHistory = modules.raidLootHistory or {}
    local settings = modules.raidLootHistory
    settings.enabled = settings.enabled == true
    return settings
end

function WipedQoL:IsRaidLootHistorySkinEnabled()
    local settings = self:GetRaidLootHistorySkinSettings()
    return settings and settings.enabled == true
end

function WipedQoL:SetRaidLootHistorySkinEnabled(enabled)
    local settings = self:GetRaidLootHistorySkinSettings()
    if not settings then
        return false
    end

    settings.enabled = enabled == true
    self.raidLootHistorySkinReloadSuggested = settings.enabled ~= (self.raidLootHistorySkinLoadedForSession == true)
    if self.RefreshRaidLootHistorySkinningUI then
        self:RefreshRaidLootHistorySkinningUI()
    end
    return true
end

function WipedQoL:GetRaidLootHistoryTimerColor()
    return GetClassColor()
end

function WipedQoL:ApplyRaidLootHistoryTimerColor()
    local frame = _G.GroupLootHistoryFrame
    local fill = frame and frame.Timer and frame.Timer.Fill
    if not fill then
        return
    end

    fill:SetVertexColor(self:GetRaidLootHistoryTimerColor())
end

function WipedQoL:SetupRaidLootHistorySkin()
    if self:SkinRaidLootHistoryFrame() then
        return
    end

    if self.raidLootHistorySkinEventFrame then
        return
    end

    local eventFrame = CreateFrame("Frame")
    eventFrame:RegisterEvent("ADDON_LOADED")
    eventFrame:SetScript("OnEvent", function(self)
        if WipedQoL:SkinRaidLootHistoryFrame() then
            self:UnregisterEvent("ADDON_LOADED")
        end
    end)
    self.raidLootHistorySkinEventFrame = eventFrame
end

function WipedQoL:InitializeRaidLootHistorySkin()
    local enabled = self:IsRaidLootHistorySkinEnabled()
    self.raidLootHistorySkinLoadedForSession = enabled
    self.raidLootHistorySkinReloadSuggested = false

    if enabled then
        self:SetupRaidLootHistorySkin()
    end
end
