local _, core = ...

local LSM = core.LSM or (LibStub and LibStub("LibSharedMedia-3.0", true))
core.LSM = LSM

core.Theme = core.Theme or {}
local Theme = core.Theme

Theme.media = Theme.media or {
    font = "Continuum",
    background = "RHH Solid",
    border = "RHH Border",
}

local FALLBACK_WHITE = "Interface\\Buttons\\WHITE8X8"
local FONT_PATH = "Interface\\AddOns\\WipedQoL\\Fonts\\CONTINUUM.TTF"
local DEFAULT_FONT_SIZE = 12
local PRIEST_CLASS_COLOR_FALLBACK = { r = 1, g = 0.49, b = 0.04 }

local function ResolveFontSize(size)
    local resolvedSize = tonumber(size)
    if resolvedSize and resolvedSize > 0 then
        return resolvedSize
    end
    return DEFAULT_FONT_SIZE
end

local function HasElvUI()
    return C_AddOns and C_AddOns.IsAddOnLoaded and C_AddOns.IsAddOnLoaded("ElvUI") and type(_G.ElvUI) == "table"
end

local function GetElvUISkins()
    if not HasElvUI() then
        return nil, nil
    end

    local E = unpack(_G.ElvUI)
    if not E or not E.GetModule then
        return nil, nil
    end

    local ok, S = pcall(E.GetModule, E, "Skins")
    if not ok then
        return E, nil
    end

    return E, S
end

local function RegisterThemeMedia()
    if not LSM then
        return
    end

    if not LSM:IsValid("background", Theme.media.background) then
        LSM:Register("background", Theme.media.background, FALLBACK_WHITE)
    end

    if not LSM:IsValid("border", Theme.media.border) then
        LSM:Register("border", Theme.media.border, FALLBACK_WHITE)
    end

    if not LSM:IsValid("font", Theme.media.font) then
        LSM:Register("font", Theme.media.font, FONT_PATH)
    end
end

RegisterThemeMedia()

local function FetchMedia(kind, name, fallback)
    if LSM and name and LSM:IsValid(kind, name) then
        return LSM:Fetch(kind, name, true)
    end
    return fallback
end

function core.GetFontPath()
    return FetchMedia("font", Theme.media.font, FONT_PATH)
end

function core.ApplyFont(fontString, size, flags)
    if not fontString or not fontString.SetFont then
        return
    end

    local _, currentSize, currentFlags = fontString:GetFont()
    local resolvedSize = ResolveFontSize(size or currentSize)
    local resolvedFlags = flags

    if resolvedFlags == nil then
        resolvedFlags = currentFlags or ""
    end

    fontString._fontSize = resolvedSize
    fontString._fontFlags = resolvedFlags
    fontString:SetFont(core.GetFontPath(), resolvedSize, resolvedFlags)
end

local function ApplyFontToRegions(owner)
    if not owner or not owner.GetRegions then
        return
    end

    for _, region in ipairs({ owner:GetRegions() }) do
        if region and region.GetObjectType and region:GetObjectType() == "FontString" then
            core.ApplyFont(region)
        end
    end
end

local function ApplyFontToChildren(owner)
    if not owner or not owner.GetChildren then
        return
    end

    for _, child in ipairs({ owner:GetChildren() }) do
        ApplyFontToRegions(child)
        ApplyFontToChildren(child)
    end
end

function core.ApplyFontToFrame(frame)
    if not frame then
        return
    end

    ApplyFontToRegions(frame)
    ApplyFontToChildren(frame)
end

local function KillTextureRegion(region)
    if not region then return end
    if region.SetTexture then region:SetTexture(nil) end
    if region.SetAtlas then region:SetAtlas(nil) end
    if region.SetColorTexture then region:SetColorTexture(0, 0, 0, 0) end
    if region.SetAlpha then region:SetAlpha(0) end
    if region.Hide then region:Hide() end
end

function core.GetPlayerClassColor()
    local classTag
    if UnitClass then
        _, classTag = UnitClass("player")
    end

    if classTag == "PRIEST" then
        return PRIEST_CLASS_COLOR_FALLBACK.r, PRIEST_CLASS_COLOR_FALLBACK.g, PRIEST_CLASS_COLOR_FALLBACK.b
    end

    local colors = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
    local color = colors and classTag and colors[classTag]
    if color then
        return color.r or 1, color.g or 0.82, color.b or 0
    end

    return 1, 0.82, 0
end

function core.GetAddonColor()
    local addon = core.WipedQoL
    if addon and addon.GetAddonColor then
        return addon:GetAddonColor()
    end

    return core.GetPlayerClassColor()
end

-- Compatibility alias: existing theme callers now intentionally use the
-- profile-wide addon color.
function core.GetClassColor()
    return core.GetAddonColor()
end

local function GetClassColor()
    return core.GetAddonColor()
end

local addonColorTargets = setmetatable({}, { __mode = "k" })

local function ApplyAddonColorTarget(target, settings)
    if not target or not settings then
        return
    end

    local method = target[settings.method]
    if not method then
        return
    end

    local r, g, b = GetClassColor()
    local brightness = settings.brightness or 1
    method(target, r * brightness, g * brightness, b * brightness, settings.alpha or 1)
end

function core.SetAddonColorTarget(target, method, brightness, alpha)
    if not target or type(method) ~= "string" then
        return
    end

    local settings = {
        method = method,
        brightness = tonumber(brightness) or 1,
        alpha = tonumber(alpha) or 1,
    }
    addonColorTargets[target] = settings
    ApplyAddonColorTarget(target, settings)
end

function core.RefreshAddonColorTargets()
    for target, settings in pairs(addonColorTargets) do
        ApplyAddonColorTarget(target, settings)
    end
end

local function EnsureBaseLayers(frame)
    if not frame or frame._rhhThemeLayersReady or frame._rhhThemeLayersUnavailable then
        return
    end

    local bg = frame:CreateTexture(nil, "BACKGROUND", nil, -8)
    if not bg then
        frame._rhhThemeLayersUnavailable = true
        return
    end

    local border = frame:CreateTexture(nil, "BORDER", nil, -7)
    if not border then
        bg:Hide()
        frame._rhhThemeLayersUnavailable = true
        return
    end

    local inner = frame:CreateTexture(nil, "BORDER", nil, -6)
    if not inner then
        bg:Hide()
        border:Hide()
        frame._rhhThemeLayersUnavailable = true
        return
    end

    bg:SetAllPoints(frame)
    bg:SetTexture(FALLBACK_WHITE)
    bg:SetVertexColor(0, 0, 0, 0.84)
    frame._rhhBG = bg

    border:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
    border:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
    border:SetTexture(FALLBACK_WHITE)
    border:SetVertexColor(0, 0, 0, 0)
    frame._rhhBorder = border

    inner:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
    inner:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -1, 1)
    inner:SetTexture(FALLBACK_WHITE)
    inner:SetVertexColor(0, 0, 0, 0)
    frame._rhhInner = inner

    frame._rhhThemeLayersReady = true
end

local function SetFrameFill(frame, alpha)
    EnsureBaseLayers(frame)
    if frame and frame._rhhBG then
        frame._rhhBG:SetVertexColor(0, 0, 0, alpha or 0.84)
        frame._rhhBG:Show()
    end
end

local function SetFrameBorder(frame, alpha)
    EnsureBaseLayers(frame)
    if frame and frame._rhhBorder then
        frame._rhhBorder:SetVertexColor(0, 0, 0, alpha or 0)
    end
    if frame and frame._rhhInner then
        frame._rhhInner:SetVertexColor(1, 1, 1, 0.035)
    end
end

local function EnsureButtonLayers(button)
    EnsureBaseLayers(button)

    if button._rhhButtonLayersReady or button._rhhThemeLayersUnavailable then
        return
    end

    local borderSize = 1
    local top = button:CreateTexture(nil, "ARTWORK", nil, 1)
    top:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0)
    top:SetPoint("TOPRIGHT", button, "TOPRIGHT", 0, 0)
    top:SetHeight(borderSize)
    top:SetTexture(FALLBACK_WHITE)
    top:SetVertexColor(0, 0, 0, 0)

    local bottom = button:CreateTexture(nil, "ARTWORK", nil, 1)
    bottom:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", 0, 0)
    bottom:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, 0)
    bottom:SetHeight(borderSize)
    bottom:SetTexture(FALLBACK_WHITE)
    bottom:SetVertexColor(0, 0, 0, 0)

    local left = button:CreateTexture(nil, "ARTWORK", nil, 1)
    left:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0)
    left:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", 0, 0)
    left:SetWidth(borderSize)
    left:SetTexture(FALLBACK_WHITE)
    left:SetVertexColor(0, 0, 0, 0)

    local right = button:CreateTexture(nil, "ARTWORK", nil, 1)
    right:SetPoint("TOPRIGHT", button, "TOPRIGHT", 0, 0)
    right:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, 0)
    right:SetWidth(borderSize)
    right:SetTexture(FALLBACK_WHITE)
    right:SetVertexColor(0, 0, 0, 0)

    button._rhhHover = { top = top, bottom = bottom, left = left, right = right }
    button._rhhButtonLayersReady = true
end

local function StripBasicFrameTemplate(frame)
    if not frame or frame._rhhTemplateStripped then
        return
    end

    for _, key in ipairs({
        "TitleBg", "Bg", "InsetBg", "Inset", "TopBorder", "BottomBorder",
        "LeftBorder", "RightBorder", "TopLeftCorner", "TopRightCorner",
        "BotLeftCorner", "BotRightCorner", "BorderLeft", "BorderRight",
        "BorderTop", "BorderBottom", "BorderTopLeft", "BorderTopRight",
        "BorderBottomLeft", "BorderBottomRight",
        "InsetBorderTopLeft", "InsetBorderTopRight", "InsetBorderBottomLeft", "InsetBorderBottomRight",
        "InsetBorderTop", "InsetBorderBottom", "InsetBorderLeft", "InsetBorderRight"
    }) do
        KillTextureRegion(frame[key])
    end

    if frame.NineSlice then
        frame.NineSlice:Hide()
    end

    if frame.CloseButton and frame.CloseButton:GetParent() == frame then
        frame.CloseButton:SetFrameLevel(frame:GetFrameLevel() + 10)
    end

    frame._rhhTemplateStripped = true
end

local function SetHoverBorder(button, r, g, b, a)
    if not button or not button._rhhHover then
        return
    end

    if type(button._rhhHover) == "table" then
        for _, tex in pairs(button._rhhHover) do
            tex:SetVertexColor(r or 0, g or 0, b or 0, a or 0)
        end
    else
        button._rhhHover:SetVertexColor(r or 0, g or 0, b or 0, a or 0)
    end
end

function core.SkinFrame(frame, opts)
    if not frame then return end
    opts = opts or {}

    local _, S = GetElvUISkins()
    if S and S.HandleFrame then
        S:HandleFrame(frame)
        return
    end

    EnsureBaseLayers(frame)
    if frame._rhhThemeLayersUnavailable then
        return
    end
    StripBasicFrameTemplate(frame)

    if frame.SetBackdrop then
        frame:SetBackdrop(nil)
    end
    if frame.SetBackdropColor then
        frame:SetBackdropColor(0, 0, 0, 0)
    end
    if frame.SetBackdropBorderColor then
        frame:SetBackdropBorderColor(0, 0, 0, 0)
    end

    SetFrameFill(frame, opts.light and 0.88 or 0.84)
    SetFrameBorder(frame, 0)
end

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

    EnsureButtonLayers(button)

    local cr, cg, cb = GetClassColor()

    if not button:IsEnabled() then
        SetFrameFill(button, 0.56)
        SetHoverBorder(button, 0, 0, 0, 0)
        return
    end

    if button:IsMouseOver() then
        SetFrameFill(button, 0.72)
        SetHoverBorder(button, cr, cg, cb, 1)
    else
        SetFrameFill(button, 0.72)
        SetHoverBorder(button, 0, 0, 0, 0)
    end
end

function core.SkinButton(button)
    if not button then return end

    local _, S = GetElvUISkins()
    if S and S.HandleButton then
        S:HandleButton(button)
        return
    end

    EnsureButtonLayers(button)
    if button._rhhThemeLayersUnavailable then
        return
    end
    SetFrameBorder(button, 0)

    local normal = button.GetNormalTexture and button:GetNormalTexture()
    local pushed = button.GetPushedTexture and button:GetPushedTexture()
    local highlight = button.GetHighlightTexture and button:GetHighlightTexture()
    local disabled = button.GetDisabledTexture and button:GetDisabledTexture()

    KillTextureRegion(normal)
    KillTextureRegion(pushed)
    KillTextureRegion(highlight)
    KillTextureRegion(disabled)

    if button.Left then button.Left:SetAlpha(0) end
    if button.Middle then button.Middle:SetAlpha(0) end
    if button.Right then button.Right:SetAlpha(0) end

    UpdateButtonState(button)

    if not button._rhhSkinned then
        button:EnableMouse(true)
        button:HookScript("OnEnter", UpdateButtonState)
        button:HookScript("OnLeave", UpdateButtonState)
        button:HookScript("OnShow", UpdateButtonState)
        button:HookScript("OnEnable", UpdateButtonState)
        button:HookScript("OnDisable", UpdateButtonState)
        button:HookScript("OnMouseDown", function(self)
            SetFrameFill(self, 0.82)
            local cr, cg, cb = GetClassColor()
            SetHoverBorder(self, cr, cg, cb, 1)
        end)
        button:HookScript("OnMouseUp", UpdateButtonState)

        local fs = button:GetFontString()
        if fs then
            core.ApplyFont(fs, fs._fontSize or 12, fs._fontFlags or "OUTLINE")
            fs:SetTextColor(1, 1, 1)
        end

        button._rhhSkinned = true
    end
end

function core.SkinScrollBar(scrollBar)
    if not scrollBar then return end

    local _, S = GetElvUISkins()
    if S and S.HandleScrollBar then
        S:HandleScrollBar(scrollBar)
        return
    end

    for _, key in ipairs({"Back", "Forward", "Track", "Top", "Bottom", "Middle", "BG", "ScrollUpButton", "ScrollDownButton"}) do
        if scrollBar[key] then
            KillTextureRegion(scrollBar[key])
        end
    end

    local thumb = scrollBar.ThumbTexture or (scrollBar.GetThumbTexture and scrollBar:GetThumbTexture())
    if thumb then
        thumb:SetTexture(FALLBACK_WHITE)
        thumb:SetWidth(8)
        core.SetAddonColorTarget(thumb, "SetVertexColor", 0.9, 1)
    end

    if not scrollBar._rhhTrack then
        local track = scrollBar:CreateTexture(nil, "BACKGROUND")
        track:SetPoint("TOPLEFT", scrollBar, "TOPLEFT", 2, 0)
        track:SetPoint("BOTTOMRIGHT", scrollBar, "BOTTOMRIGHT", -2, 0)
        track:SetColorTexture(0, 0, 0, 0.5)
        scrollBar._rhhTrack = track
    end

    if not scrollBar._rhhAutoHideHooked then
        local function RefreshScrollBarVisibility(self)
            local minValue, maxValue = self:GetMinMaxValues()
            local canScroll = (tonumber(maxValue) or 0) > (tonumber(minValue) or 0)

            if self._rhhTrack then
                self._rhhTrack:SetShown(canScroll)
            end
            if thumb then
                thumb:SetShown(canScroll)
            end
            self:SetAlpha(canScroll and 1 or 0)
            self:EnableMouse(canScroll)
        end

        scrollBar:HookScript("OnShow", RefreshScrollBarVisibility)
        scrollBar:HookScript("OnSizeChanged", RefreshScrollBarVisibility)
        scrollBar:HookScript("OnMinMaxChanged", RefreshScrollBarVisibility)
        scrollBar:HookScript("OnValueChanged", RefreshScrollBarVisibility)
        scrollBar._rhhAutoHideHooked = true
        RefreshScrollBarVisibility(scrollBar)
    end
end

function core.SkinCloseButton(button)
    if not button then return end

    local _, S = GetElvUISkins()
    if S and S.HandleCloseButton then
        S:HandleCloseButton(button)
        return
    end

    KillTextureRegion(button.GetNormalTexture and button:GetNormalTexture())
    KillTextureRegion(button.GetPushedTexture and button:GetPushedTexture())
    KillTextureRegion(button.GetHighlightTexture and button:GetHighlightTexture())
    KillTextureRegion(button.GetDisabledTexture and button:GetDisabledTexture())

    EnsureButtonLayers(button)
    SetFrameBorder(button, 0)
    button:SetSize(18, 18)
    UpdateButtonState(button)

    if not button._rhhCloseText then
        local text = button:CreateFontString(nil, "OVERLAY")
        text:SetPoint("CENTER", 0, 0)
        core.ApplyFont(text, 12, "OUTLINE")
        text:SetText("×")
        text:SetTextColor(1, 1, 1)
        button._rhhCloseText = text
    end

    if not button._rhhCloseHook then
        button:EnableMouse(true)
        button:HookScript("OnEnter", UpdateButtonState)
        button:HookScript("OnLeave", UpdateButtonState)
        button:HookScript("OnShow", UpdateButtonState)
        button._rhhCloseHook = true
    end
end
