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 {
    background = "RHH Solid",
    border = "RHH Border",
}

local FALLBACK_WHITE = "Interface\\Buttons\\WHITE8X8"
local SYSTEM_FONT_FALLBACK = "Fonts\\FRIZQT__.TTF"
local DEFAULT_FONT_SIZE = 12
local PANEL_EMPTY_PROMPT_TEXT_SIZE = 14
local PANEL_EMPTY_PROMPT_TEXT_COLOR = { 1, 0.82, 0, 1 }

core.PANEL_EMPTY_PROMPT_TEXT_SIZE = PANEL_EMPTY_PROMPT_TEXT_SIZE
core.PANEL_EMPTY_PROMPT_TEXT_COLOR = PANEL_EMPTY_PROMPT_TEXT_COLOR

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

end

RegisterThemeMedia()

local function GetSystemFontPath()
    if type(STANDARD_TEXT_FONT) == "string" and STANDARD_TEXT_FONT ~= "" then
        return STANDARD_TEXT_FONT
    end

    if _G.GameFontNormal and _G.GameFontNormal.GetFont then
        local fontPath = _G.GameFontNormal:GetFont()
        if type(fontPath) == "string" and fontPath ~= "" then
            return fontPath
        end
    end

    return SYSTEM_FONT_FALLBACK
end

function core.GetFontPath()
    return GetSystemFontPath()
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

function core.ApplyPanelEmptyPromptStyle(fontString)
    if not fontString then
        return
    end

    local color = core.PANEL_EMPTY_PROMPT_TEXT_COLOR or PANEL_EMPTY_PROMPT_TEXT_COLOR
    if fontString.SetTextColor and color then
        fontString:SetTextColor(color[1], color[2], color[3], color[4] or 1)
    end

    core.ApplyFont(fontString, core.PANEL_EMPTY_PROMPT_TEXT_SIZE or PANEL_EMPTY_PROMPT_TEXT_SIZE, "")
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

local function GetClassColor()
    local classTag = select(2, UnitClass("player"))
    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

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

    local bg = frame:CreateTexture(nil, "BACKGROUND", nil, -8)
    bg:SetAllPoints(frame)
    bg:SetTexture(FALLBACK_WHITE)
    bg:SetVertexColor(0, 0, 0, 0.84)
    frame._rhhBG = bg

    local border = frame:CreateTexture(nil, "BORDER", nil, -7)
    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

    local inner = frame:CreateTexture(nil, "BORDER", nil, -6)
    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 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

    StripBasicFrameTemplate(frame)
    EnsureBaseLayers(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)
    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 cr, cg, cb = GetClassColor()

    local thumb = scrollBar.ThumbTexture or (scrollBar.GetThumbTexture and scrollBar:GetThumbTexture())
    if thumb then
        thumb:SetTexture(FALLBACK_WHITE)
        thumb:SetSize(8, 24)
        thumb:SetVertexColor(cr * 0.9, cg * 0.9, cb * 0.9, 1)
    end

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