local addonName, ns = ...
local L = ns.L

local frame
local controls = {}
local refreshing = false
local statusText
local borderTextureButton
local borderColorSwatch
local borderTexturePicker
local borderColorPicker
local horizontalButton
local verticalButton

local function T(key, fallback)
    local value = L[key]
    if value == nil or value == key then
        return fallback
    end
    return value
end

local function DB()
    return ChatFloaterDB
end

local function Apply()
    if ns.OnChatBarChanged then
        ns.OnChatBarChanged()
    end
end

local function ClassColor()
    local classFile = select(2, UnitClass("player"))
    if C_ClassColor and C_ClassColor.GetClassColor and classFile then
        local c = C_ClassColor.GetClassColor(classFile)
        if c then return c.r, c.g, c.b end
    end
    if RAID_CLASS_COLORS and classFile and RAID_CLASS_COLORS[classFile] then
        local c = RAID_CLASS_COLORS[classFile]
        return c.r, c.g, c.b
    end
    return 1.0, 0.55, 0.02
end

local function Text(parent, text, template)
    local fs = parent:CreateFontString(nil, "OVERLAY", template or "GameFontNormalSmall")
    fs:SetText(text)
    return fs
end

local function ApplyMainTheme(target)
    target:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    target:SetBackdropColor(0.018, 0.020, 0.026, 0.985)
    target:SetBackdropBorderColor(1, 1, 1, 0.10)
end

local function ApplyPanelTheme(target)
    target:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    target:SetBackdropColor(0, 0, 0, 0.34)
    target:SetBackdropBorderColor(1, 1, 1, 0.08)
end

local function EnsureSoftButtonEdges(btn)
    if btn._cfEdges then return end

    local edges = {}
    edges.top = btn:CreateTexture(nil, "OVERLAY")
    edges.bottom = btn:CreateTexture(nil, "OVERLAY")
    edges.left = btn:CreateTexture(nil, "OVERLAY")
    edges.right = btn:CreateTexture(nil, "OVERLAY")

    for _, edge in pairs(edges) do
        edge:SetTexture("Interface\\Buttons\\WHITE8X8")
    end

    -- Keep the 1 px lines inside the button bounds. This avoids the bottom
    -- edge disappearing at fractional UI scales due to pixel rounding.
    edges.top:SetPoint("TOPLEFT", 1, -1)
    edges.top:SetPoint("TOPRIGHT", -1, -1)
    edges.top:SetHeight(1)

    edges.bottom:SetPoint("BOTTOMLEFT", 1, 1)
    edges.bottom:SetPoint("BOTTOMRIGHT", -1, 1)
    edges.bottom:SetHeight(1)

    edges.left:SetPoint("TOPLEFT", 1, -1)
    edges.left:SetPoint("BOTTOMLEFT", 1, 1)
    edges.left:SetWidth(1)

    edges.right:SetPoint("TOPRIGHT", -1, -1)
    edges.right:SetPoint("BOTTOMRIGHT", -1, 1)
    edges.right:SetWidth(1)

    btn._cfEdges = edges
end

local function ApplySoftButtonTheme(btn, active, hovered)
    if not btn then return end

    btn:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = nil,
    })

    EnsureSoftButtonEdges(btn)

    local r, g, b = ClassColor()
    local er, eg, eb, ea
    if active then
        btn:SetBackdropColor(r, g, b, hovered and 0.15 or 0.10)
        er, eg, eb, ea = r, g, b, hovered and 1.00 or 0.94
    else
        btn:SetBackdropColor(1, 1, 1, hovered and 0.055 or 0.025)
        er, eg, eb, ea = 1, 1, 1, hovered and 0.18 or 0.10
    end

    for _, edge in pairs(btn._cfEdges) do
        edge:SetVertexColor(er, eg, eb, ea)
    end

    if btn._text then
        if active then
            btn._text:SetTextColor(1, 0.98, 0.90, 0.98)
        else
            btn._text:SetTextColor(0.88, 0.91, 0.96, 0.90)
        end
    end
end

local function Section(parent, title, x, y, width, height)
    local box = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    box:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    box:SetSize(width, height)
    ApplyPanelTheme(box)

    local label = Text(box, title, "GameFontNormal")
    label:SetPoint("TOPLEFT", 10, -8)
    label:SetTextColor(0.36, 0.78, 1.0, 0.96)

    local line = box:CreateTexture(nil, "ARTWORK")
    line:SetPoint("TOPLEFT", 10, -28)
    line:SetPoint("TOPRIGHT", -10, -28)
    line:SetHeight(1)
    line:SetColorTexture(0.36, 0.78, 1.0, 0.18)

    return box
end

local function CreateFlatCheckbox(parent, label, key, x, y, callback, width)
    local wrap = CreateFrame("Button", nil, parent)
    wrap:SetPoint("TOPLEFT", x, y)
    wrap:SetSize(width or 100, 18)
    wrap:EnableMouse(true)
    wrap.key = key
    wrap.kind = "check"

    local box = CreateFrame("Frame", nil, wrap, "BackdropTemplate")
    box:SetSize(15, 15)
    box:SetPoint("LEFT", 0, 0)
    box:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    box:SetBackdropColor(0, 0, 0, 0.36)
    box:SetBackdropBorderColor(1, 1, 1, 0.14)

    local check = box:CreateTexture(nil, "OVERLAY")
    check:SetSize(9, 9)
    check:SetPoint("CENTER")
    check:SetTexture("Interface\\Buttons\\WHITE8X8")
    check:Hide()

    local text = Text(wrap, label, "GameFontNormalSmall")
    text:SetPoint("LEFT", box, "RIGHT", 7, 0)
    text:SetTextColor(1, 1, 1, 0.86)

    wrap._box = box
    wrap._check = check
    wrap._text = text
    wrap._value = false

    function wrap:SetValue(v)
        self._value = v and true or false
        local r, g, b = ClassColor()
        if self._value then
            check:SetVertexColor(r, g, b, 0.96)
            check:Show()
            box:SetBackdropBorderColor(r, g, b, 0.76)
            box:SetBackdropColor(0, 0, 0, 0.43)
        else
            check:Hide()
            box:SetBackdropBorderColor(1, 1, 1, 0.14)
            box:SetBackdropColor(0, 0, 0, 0.36)
        end
    end

    wrap:SetScript("OnEnter", function(self)
        local r, g, b = ClassColor()
        if not self._value then
            box:SetBackdropBorderColor(r, g, b, 0.42)
        end
        text:SetTextColor(1, 1, 1, 0.98)
    end)

    wrap:SetScript("OnLeave", function(self)
        self:SetValue(self._value)
        text:SetTextColor(1, 1, 1, 0.86)
    end)

    wrap:SetScript("OnClick", function(self)
        if refreshing then return end
        local newValue = not self._value
        self:SetValue(newValue)
        DB()[key] = newValue

        if callback then
            callback(newValue)
        else
            Apply()
        end
    end)

    controls[#controls + 1] = wrap
    return wrap
end

local function CreateModernSlider(parent, label, key, minValue, maxValue, step, x, y, width)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", x, y)
    row:SetSize(width, 31)

    local title = Text(row, label, "GameFontNormalSmall")
    title:SetPoint("TOPLEFT", 0, 0)
    title:SetTextColor(1, 1, 1, 0.84)

    local valueText = Text(row, "", "GameFontHighlightSmall")
    valueText:SetPoint("TOPRIGHT", 0, 0)
    valueText:SetJustifyH("RIGHT")
    valueText:SetTextColor(1, 1, 1, 0.66)

    local slider = CreateFrame("Slider", nil, row)
    slider:SetOrientation("HORIZONTAL")
    slider:SetPoint("TOPLEFT", 0, -15)
    slider:SetSize(width, 15)
    slider:SetMinMaxValues(minValue, maxValue)
    slider:SetValueStep(step)
    slider:SetObeyStepOnDrag(true)
    slider.key = key
    slider.kind = "slider"

    local track = slider:CreateTexture(nil, "BACKGROUND")
    track:SetHeight(4)
    track:SetPoint("LEFT", slider, "LEFT", 7, 0)
    track:SetPoint("RIGHT", slider, "RIGHT", -7, 0)
    track:SetColorTexture(1, 1, 1, 0.13)

    local fill = slider:CreateTexture(nil, "ARTWORK")
    fill:SetHeight(4)
    fill:SetPoint("LEFT", track, "LEFT", 0, 0)
    fill:SetWidth(1)

    local knob = CreateFrame("Frame", nil, slider, "BackdropTemplate")
    knob:SetSize(8, 12)
    knob:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    knob:EnableMouse(false)

    slider:SetThumbTexture("Interface\\Buttons\\WHITE8X8")
    local nativeThumb = slider:GetThumbTexture()
    if nativeThumb then
        nativeThumb:SetSize(14, 18)
        nativeThumb:SetAlpha(0)
    end

    local function Normalize(current)
        if step < 1 then
            current = math.floor(current / step + 0.5) * step
        else
            current = math.floor(current + 0.5)
        end
        if current < minValue then current = minValue end
        if current > maxValue then current = maxValue end
        return current
    end

    local function UpdateVisual(current)
        current = Normalize(current)
        local pct = 0
        if maxValue > minValue then
            pct = (current - minValue) / (maxValue - minValue)
        end
        if pct < 0 then pct = 0 end
        if pct > 1 then pct = 1 end

        local trackWidth = math.max(1, width - 14)
        local xPos = pct * trackWidth
        local r, g, b = ClassColor()

        fill:SetColorTexture(r, g, b, 0.88)
        fill:SetWidth(math.max(1, xPos))

        knob:ClearAllPoints()
        knob:SetPoint("CENTER", track, "LEFT", xPos, 0)
        knob:SetBackdropColor(r, g, b, 0.95)
        knob:SetBackdropBorderColor(1, 1, 1, 0.18)

        if step < 1 then
            valueText:SetText(string.format("%.2f", current))
        else
            valueText:SetText(tostring(current))
        end
    end

    slider:SetScript("OnValueChanged", function(self, current)
        current = Normalize(current)
        UpdateVisual(current)
        if refreshing then return end
        DB()[key] = current
        Apply()
    end)

    slider:SetScript("OnEnter", function()
        track:SetColorTexture(1, 1, 1, 0.19)
    end)
    slider:SetScript("OnLeave", function()
        track:SetColorTexture(1, 1, 1, 0.13)
    end)

    slider._UpdateVisual = UpdateVisual
    controls[#controls + 1] = slider
    return slider
end

local function CreateSoftButton(parent, label, x, y, width, height, callback)
    local button = CreateFrame("Button", nil, parent, "BackdropTemplate")
    button:SetPoint("TOPLEFT", x, y)
    button:SetSize(width, height)
    button._active = false
    ApplySoftButtonTheme(button, false, false)

    local text = Text(button, label, "GameFontNormalSmall")
    text:SetPoint("CENTER")
    button._text = text
    ApplySoftButtonTheme(button, false, false)

    button:SetScript("OnEnter", function(self)
        ApplySoftButtonTheme(self, self._active, true)
    end)
    button:SetScript("OnLeave", function(self)
        ApplySoftButtonTheme(self, self._active, false)
    end)
    button:SetScript("OnMouseDown", function(self)
        self:SetBackdropColor(1, 1, 1, 0.09)
    end)
    button:SetScript("OnMouseUp", function(self)
        ApplySoftButtonTheme(self, self._active, self:IsMouseOver())
    end)
    if callback then
        button:SetScript("OnClick", callback)
    end

    function button:SetActive(active)
        self._active = active and true or false
        ApplySoftButtonTheme(self, self._active, self:IsMouseOver())
    end

    return button
end

local function CreateCloseButton(parent, offsetX, offsetY, size)
    local close = CreateFrame("Button", nil, parent, "BackdropTemplate")
    close:SetSize(size or 20, size or 20)
    close:SetPoint("TOPRIGHT", offsetX or -7, offsetY or -5)
    close:SetFrameLevel((parent:GetFrameLevel() or 1) + 10)
    ApplySoftButtonTheme(close, false, false)

    local a = close:CreateTexture(nil, "OVERLAY")
    local b = close:CreateTexture(nil, "OVERLAY")
    a:SetTexture("Interface\\Buttons\\WHITE8X8")
    b:SetTexture("Interface\\Buttons\\WHITE8X8")
    a:SetSize(12, 2)
    b:SetSize(12, 2)
    a:SetPoint("CENTER", 0, 0)
    b:SetPoint("CENTER", 0, 0)
    a:SetRotation(math.rad(45))
    b:SetRotation(math.rad(-45))
    a:SetVertexColor(1, 1, 1, 0.92)
    b:SetVertexColor(1, 1, 1, 0.92)

    close:SetScript("OnEnter", function(self)
        ApplySoftButtonTheme(self, false, true)
        local r, g, bl = ClassColor()
        a:SetVertexColor(r, g, bl, 1)
        b:SetVertexColor(r, g, bl, 1)
    end)
    close:SetScript("OnLeave", function(self)
        ApplySoftButtonTheme(self, false, false)
        a:SetVertexColor(1, 1, 1, 0.92)
        b:SetVertexColor(1, 1, 1, 0.92)
    end)
    close:SetScript("OnClick", function() parent:Hide() end)

    return close
end

local function MakePopupMovable(window)
    window:SetMovable(true)
    window:EnableMouse(true)
    window:RegisterForDrag("LeftButton")

    local dragBar = CreateFrame("Frame", nil, window)
    dragBar:SetPoint("TOPLEFT", 0, 0)
    dragBar:SetPoint("TOPRIGHT", -32, 0)
    dragBar:SetHeight(28)
    dragBar:SetFrameLevel((window:GetFrameLevel() or 1) + 2)
    dragBar:EnableMouse(true)
    dragBar:RegisterForDrag("LeftButton")
    dragBar:SetScript("OnDragStart", function() window:StartMoving() end)
    dragBar:SetScript("OnDragStop", function() window:StopMovingOrSizing(); window._hasCustomPosition = true end)
    window._dragBar = dragBar
end

local function HexFromColor(r, g, b)
    local rr = math.floor((r or 0) * 255 + 0.5)
    local gg = math.floor((g or 0) * 255 + 0.5)
    local bb = math.floor((b or 0) * 255 + 0.5)
    if rr < 0 then rr = 0 elseif rr > 255 then rr = 255 end
    if gg < 0 then gg = 0 elseif gg > 255 then gg = 255 end
    if bb < 0 then bb = 0 elseif bb > 255 then bb = 255 end
    return string.format("#%02X%02X%02X", rr, gg, bb)
end

local function ApplyBorderColorValue(r, g, b, triggerApply)
    local db = DB()
    db.chatPanelBorderColor = db.chatPanelBorderColor or {}
    db.chatPanelBorderColor.r = r
    db.chatPanelBorderColor.g = g
    db.chatPanelBorderColor.b = b

    if borderColorSwatch then
        borderColorSwatch:SetBackdropColor(r, g, b, 1)
    end

    if borderColorPicker then
        if borderColorPicker.previewNew then
            borderColorPicker.previewNew:SetBackdropColor(r, g, b, 1)
            borderColorPicker.previewNew:SetBackdropBorderColor(1, 1, 1, 0.14)
        end
        if borderColorPicker.hexText then
            borderColorPicker.hexText:SetText(HexFromColor(r, g, b))
        end
        if borderColorPicker.hexBackdrop then
            borderColorPicker.hexBackdrop:SetBackdropBorderColor(r, g, b, 0.55)
        end
    end

    if triggerApply then
        Apply()
    end
end

local function CreatePickerSlider(parent, label, x, y, width, onChanged)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", x, y)
    row:SetSize(width, 31)

    local title = Text(row, label, "GameFontNormalSmall")
    title:SetPoint("TOPLEFT", 0, 0)
    title:SetTextColor(1, 1, 1, 0.84)

    local valueText = Text(row, "0", "GameFontHighlightSmall")
    valueText:SetPoint("TOPRIGHT", 0, 0)
    valueText:SetJustifyH("RIGHT")
    valueText:SetTextColor(1, 1, 1, 0.66)

    local slider = CreateFrame("Slider", nil, row)
    slider:SetOrientation("HORIZONTAL")
    slider:SetPoint("TOPLEFT", 0, -15)
    slider:SetSize(width, 15)
    slider:SetMinMaxValues(0, 255)
    slider:SetValueStep(1)
    slider:SetObeyStepOnDrag(true)

    local track = slider:CreateTexture(nil, "BACKGROUND")
    track:SetHeight(4)
    track:SetPoint("LEFT", slider, "LEFT", 7, 0)
    track:SetPoint("RIGHT", slider, "RIGHT", -7, 0)
    track:SetColorTexture(1, 1, 1, 0.13)

    local fill = slider:CreateTexture(nil, "ARTWORK")
    fill:SetHeight(4)
    fill:SetPoint("LEFT", track, "LEFT", 0, 0)
    fill:SetWidth(1)

    local knob = CreateFrame("Frame", nil, slider, "BackdropTemplate")
    knob:SetSize(8, 12)
    knob:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    knob:EnableMouse(false)

    slider:SetThumbTexture("Interface\\Buttons\\WHITE8X8")
    local nativeThumb = slider:GetThumbTexture()
    if nativeThumb then
        nativeThumb:SetSize(14, 18)
        nativeThumb:SetAlpha(0)
    end

    local function UpdateVisual(current)
        current = math.floor((current or 0) + 0.5)
        if current < 0 then current = 0 end
        if current > 255 then current = 255 end

        local pct = current / 255
        local trackWidth = math.max(1, width - 14)
        local xPos = pct * trackWidth
        local r, g, b = ClassColor()

        fill:SetColorTexture(r, g, b, 0.88)
        fill:SetWidth(math.max(1, xPos))

        knob:ClearAllPoints()
        knob:SetPoint("CENTER", track, "LEFT", xPos, 0)
        knob:SetBackdropColor(r, g, b, 0.95)
        knob:SetBackdropBorderColor(1, 1, 1, 0.18)
        valueText:SetText(tostring(current))
    end

    slider:SetScript("OnValueChanged", function(self, current)
        current = math.floor((current or 0) + 0.5)
        if current < 0 then current = 0 end
        if current > 255 then current = 255 end
        UpdateVisual(current)
        if parent._refreshing then return end
        if onChanged then
            onChanged(current)
        end
    end)

    slider:SetScript("OnEnter", function()
        track:SetColorTexture(1, 1, 1, 0.19)
    end)
    slider:SetScript("OnLeave", function()
        track:SetColorTexture(1, 1, 1, 0.13)
    end)

    function slider:SetNumber(value)
        parent._refreshing = true
        self:SetValue(value)
        UpdateVisual(value)
        parent._refreshing = false
    end

    slider._UpdateVisual = UpdateVisual
    return slider
end

local function CreateColorPresetButton(parent, x, y, r, g, b)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetPoint("TOPLEFT", x, y)
    btn:SetSize(18, 18)
    btn:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    btn:SetBackdropColor(r, g, b, 1)
    btn:SetBackdropBorderColor(1, 1, 1, 0.12)

    btn:SetScript("OnEnter", function(self)
        self:SetBackdropBorderColor(1, 1, 1, 0.40)
    end)
    btn:SetScript("OnLeave", function(self)
        self:SetBackdropBorderColor(1, 1, 1, 0.12)
    end)
    btn:SetScript("OnClick", function()
        if borderColorPicker and borderColorPicker.SetRGB then
            borderColorPicker:SetRGB(r, g, b, true)
        end
    end)

    return btn
end

local function EnsureBorderColorPicker()
    if borderColorPicker then return borderColorPicker end

    local picker = CreateFrame("Frame", "ChatFloaterBorderColorPicker", UIParent, "BackdropTemplate")
    picker:SetSize(318, 282)
    picker:SetFrameStrata("DIALOG")
    picker:SetFrameLevel((frame and frame:GetFrameLevel() or 1) + 35)
    picker:SetClampedToScreen(true)
    picker:SetToplevel(true)
    picker:Hide()
    ApplyMainTheme(picker)
    MakePopupMovable(picker)

    local title = Text(picker, T("CF_BORDER_COLOR", "Border Color"), "GameFontNormal")
    title:SetPoint("TOPLEFT", 11, -9)
    title:SetTextColor(0.36, 0.78, 1.0, 0.96)

    local line = picker:CreateTexture(nil, "ARTWORK")
    line:SetPoint("TOPLEFT", 10, -29)
    line:SetPoint("TOPRIGHT", -10, -29)
    line:SetHeight(1)
    line:SetColorTexture(0.36, 0.78, 1.0, 0.18)

    CreateCloseButton(picker, -6, -4, 20)

    local oldLabel = Text(picker, T("CF_OLD", "Old"), "GameFontHighlightSmall")
    oldLabel:SetPoint("TOPLEFT", 16, -40)
    oldLabel:SetTextColor(1, 1, 1, 0.50)

    local newLabel = Text(picker, T("CF_NEW", "New"), "GameFontHighlightSmall")
    newLabel:SetPoint("TOPLEFT", 84, -40)
    newLabel:SetTextColor(1, 1, 1, 0.50)

    picker.previewOld = CreateFrame("Frame", nil, picker, "BackdropTemplate")
    picker.previewOld:SetPoint("TOPLEFT", 16, -56)
    picker.previewOld:SetSize(52, 24)
    picker.previewOld:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    picker.previewOld:SetBackdropBorderColor(1, 1, 1, 0.12)

    picker.previewNew = CreateFrame("Frame", nil, picker, "BackdropTemplate")
    picker.previewNew:SetPoint("TOPLEFT", 84, -56)
    picker.previewNew:SetSize(52, 24)
    picker.previewNew:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    picker.previewNew:SetBackdropBorderColor(1, 1, 1, 0.12)

    picker.hexBackdrop = CreateFrame("Frame", nil, picker, "BackdropTemplate")
    picker.hexBackdrop:SetPoint("TOPLEFT", 156, -56)
    picker.hexBackdrop:SetSize(108, 24)
    picker.hexBackdrop:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    picker.hexBackdrop:SetBackdropColor(1, 1, 1, 0.025)
    picker.hexBackdrop:SetBackdropBorderColor(1, 1, 1, 0.10)

    picker.hexText = Text(picker.hexBackdrop, "#000000", "GameFontHighlightSmall")
    picker.hexText:SetPoint("CENTER", 0, 0)
    picker.hexText:SetTextColor(1, 1, 1, 0.80)

    picker.rSlider = CreatePickerSlider(picker, "Red", 16, -92, 286, function(value)
        local _, g, b = picker:GetRGB()
        picker:SetRGB(value / 255, g, b, true)
    end)
    picker.gSlider = CreatePickerSlider(picker, "Green", 16, -124, 286, function(value)
        local r, _, b = picker:GetRGB()
        picker:SetRGB(r, value / 255, b, true)
    end)
    picker.bSlider = CreatePickerSlider(picker, "Blue", 16, -156, 286, function(value)
        local r, g, _ = picker:GetRGB()
        picker:SetRGB(r, g, value / 255, true)
    end)

    local presetLabel = Text(picker, T("CF_PRESETS", "Presets"), "GameFontHighlightSmall")
    presetLabel:SetPoint("TOPLEFT", 16, -198)
    presetLabel:SetTextColor(1, 1, 1, 0.50)

    local classR, classG, classB = ClassColor()
    local presets = {
        { classR, classG, classB },
        { 1.00, 0.55, 0.02 },
        { 0.38, 0.44, 0.52 },
        { 1.00, 1.00, 1.00 },
        { 0.15, 0.15, 0.15 },
        { 0.80, 0.25, 0.25 },
        { 0.25, 0.70, 0.35 },
        { 0.25, 0.50, 0.90 },
        { 0.76, 0.56, 0.10 },
    }
    local px = 16
    for _, c in ipairs(presets) do
        CreateColorPresetButton(picker, px, -216, c[1], c[2], c[3])
        px = px + 24
    end

    picker.okayButton = CreateSoftButton(picker, OKAY or "Okay", 16, -248, 90, 20, function()
        picker:Hide()
    end)
    picker.cancelButton = CreateSoftButton(picker, CANCEL or "Cancel", 114, -248, 90, 20, function()
        local old = picker.oldColor or { r = 0.38, g = 0.44, b = 0.52 }
        picker:SetRGB(old.r, old.g, old.b, true)
        picker:Hide()
    end)
    picker.resetButton = CreateSoftButton(picker, T("CF_RESET", "Reset"), 212, -248, 90, 20, function()
        picker:SetRGB(0.38, 0.44, 0.52, true)
    end)

    function picker:GetRGB()
        local c = self.currentColor or { r = 0.38, g = 0.44, b = 0.52 }
        return c.r or 0.38, c.g or 0.44, c.b or 0.52
    end

    function picker:SetRGB(r, g, b, triggerApply)
        if r < 0 then r = 0 elseif r > 1 then r = 1 end
        if g < 0 then g = 0 elseif g > 1 then g = 1 end
        if b < 0 then b = 0 elseif b > 1 then b = 1 end

        self.currentColor = self.currentColor or {}
        self.currentColor.r = r
        self.currentColor.g = g
        self.currentColor.b = b

        self._refreshing = true
        if self.rSlider then self.rSlider:SetNumber(r * 255) end
        if self.gSlider then self.gSlider:SetNumber(g * 255) end
        if self.bSlider then self.bSlider:SetNumber(b * 255) end
        self._refreshing = false

        ApplyBorderColorValue(r, g, b, triggerApply)
    end

    borderColorPicker = picker
    return picker
end

local function OpenColorPicker()
    local db = DB()
    db.chatPanelBorderColor = db.chatPanelBorderColor or { r = 0.38, g = 0.44, b = 0.52 }

    local picker = EnsureBorderColorPicker()
    local old = {
        r = db.chatPanelBorderColor.r or 0.38,
        g = db.chatPanelBorderColor.g or 0.44,
        b = db.chatPanelBorderColor.b or 0.52,
    }
    picker.oldColor = old

    picker.previewOld:SetBackdropColor(old.r, old.g, old.b, 1)
    picker.previewOld:SetBackdropBorderColor(1, 1, 1, 0.14)

    picker:SetRGB(old.r, old.g, old.b, false)

    if not picker:IsShown() and not picker._hasCustomPosition then
        picker:ClearAllPoints()
        if borderColorSwatch then
            picker:SetPoint("TOPLEFT", borderColorSwatch, "TOPRIGHT", 14, 10)
        else
            picker:SetPoint("CENTER")
        end
    end
    picker:Show()
end

local function BorderTextureLabel(value)
    value = type(value) == "string" and value or "BLIZZARD_TOOLTIP"
    if value == "BLIZZARD_TOOLTIP" then
        return T("CF_BORDER_BLIZZARD", "Blizzard Tooltip")
    elseif value == "SOLID" then
        return T("CF_BORDER_SOLID", "Solid")
    elseif value == "NONE" then
        return T("CF_BORDER_NONE", "None")
    end

    local media = value:match("^LSM:(.+)$")
    if media and media ~= "" then
        return media
    end
    return T("CF_BORDER_BLIZZARD", "Blizzard Tooltip")
end

local function EnsureBorderTexturePicker()
    if borderTexturePicker then return borderTexturePicker end

    local picker = CreateFrame("Frame", "ChatFloaterBorderTexturePicker", UIParent, "BackdropTemplate")
    picker:SetSize(304, 276)
    picker:SetFrameStrata("DIALOG")
    picker:SetFrameLevel((frame and frame:GetFrameLevel() or 1) + 30)
    picker:SetClampedToScreen(true)
    picker:SetToplevel(true)
    picker:Hide()
    ApplyMainTheme(picker)
    MakePopupMovable(picker)

    local title = Text(picker, T("CF_BORDER_TEXTURE", "Border Texture"), "GameFontNormal")
    title:SetPoint("TOPLEFT", 11, -9)
    title:SetTextColor(0.36, 0.78, 1.0, 0.96)

    local line = picker:CreateTexture(nil, "ARTWORK")
    line:SetPoint("TOPLEFT", 10, -29)
    line:SetPoint("TOPRIGHT", -10, -29)
    line:SetHeight(1)
    line:SetColorTexture(0.36, 0.78, 1.0, 0.18)

    CreateCloseButton(picker, -6, -4, 20)

    local scroll = CreateFrame("ScrollFrame", nil, picker)
    scroll:SetPoint("TOPLEFT", 9, -36)
    scroll:SetPoint("BOTTOMRIGHT", -18, 9)
    scroll:EnableMouseWheel(true)

    local child = CreateFrame("Frame", nil, scroll)
    child:SetSize(272, 1)
    scroll:SetScrollChild(child)

    local scrollTrack = CreateFrame("Frame", nil, picker, "BackdropTemplate")
    scrollTrack:SetPoint("TOPRIGHT", -8, -38)
    scrollTrack:SetPoint("BOTTOMRIGHT", -8, 10)
    scrollTrack:SetWidth(5)
    scrollTrack:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
    scrollTrack:SetBackdropColor(1, 1, 1, 0.05)

    local scrollBar = CreateFrame("Slider", nil, picker)
    scrollBar:SetOrientation("VERTICAL")
    scrollBar:SetPoint("TOPRIGHT", -8, -38)
    scrollBar:SetPoint("BOTTOMRIGHT", -8, 10)
    scrollBar:SetWidth(5)
    scrollBar:SetMinMaxValues(0, 0)
    scrollBar:SetValue(0)
    scrollBar:SetValueStep(1)

    local thumb = scrollBar:CreateTexture(nil, "ARTWORK")
    thumb:SetTexture("Interface\\Buttons\\WHITE8X8")
    local r, g, b = ClassColor()
    thumb:SetVertexColor(r, g, b, 0.86)
    thumb:SetSize(5, 38)
    scrollBar:SetThumbTexture(thumb)

    scrollBar:SetScript("OnValueChanged", function(_, value)
        scroll:SetVerticalScroll(value)
    end)

    scroll:SetScript("OnMouseWheel", function(_, delta)
        local minV, maxV = scrollBar:GetMinMaxValues()
        local value = scrollBar:GetValue() - (delta * 42)
        if value < minV then value = minV end
        if value > maxV then value = maxV end
        scrollBar:SetValue(value)
    end)

    picker.child = child
    picker.scroll = scroll
    picker.scrollBar = scrollBar
    picker.scrollTrack = scrollTrack
    borderTexturePicker = picker
    return picker
end

local function OpenBorderTexturePicker(anchor)
    local picker = EnsureBorderTexturePicker()
    local child = picker.child

    picker:ClearAllPoints()
    picker:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, 6)

    if child.rows then
        for _, row in ipairs(child.rows) do
            row:Hide()
        end
    end
    child.rows = {}

    local items = {
        { value = "BLIZZARD_TOOLTIP", label = T("CF_BORDER_BLIZZARD", "Blizzard Tooltip") },
        { value = "SOLID", label = T("CF_BORDER_SOLID", "Solid") },
        { value = "NONE", label = T("CF_BORDER_NONE", "None") },
    }

    local borders = ns.GetSharedMediaBorders and ns.GetSharedMediaBorders() or {}
    for _, name in ipairs(borders) do
        items[#items + 1] = { value = "LSM:" .. name, label = name }
    end

    local selected = DB().chatPanelBorderTexture or "BLIZZARD_TOOLTIP"
    local y = -1
    for _, info in ipairs(items) do
        local row = CreateSoftButton(child, info.label, 0, y, 268, 20, nil)
        row._text:ClearAllPoints()
        row._text:SetPoint("LEFT", 8, 0)
        row._text:SetJustifyH("LEFT")
        row:SetActive(info.value == selected)
        row:SetScript("OnClick", function()
            DB().chatPanelBorderTexture = info.value
            picker:Hide()
            Apply()
            if ns.RefreshGUI then ns.RefreshGUI() end
        end)
        child.rows[#child.rows + 1] = row
        y = y - 22
    end

    local childHeight = math.max(220, -y + 2)
    child:SetHeight(childHeight)

    local viewport = 231
    local maxScroll = math.max(0, childHeight - viewport)
    picker.scrollBar:SetMinMaxValues(0, maxScroll)
    picker.scrollBar:SetValue(0)
    picker.scrollTrack:SetShown(maxScroll > 0)
    picker.scrollBar:SetShown(maxScroll > 0)

    local thumb = picker.scrollBar:GetThumbTexture()
    if thumb then
        local r, g, b = ClassColor()
        thumb:SetVertexColor(r, g, b, 0.86)
    end

    picker:Show()
end

local function Build()
    if frame then return frame end

    frame = CreateFrame("Frame", "ChatFloaterConfigFrame", UIParent, "BackdropTemplate")
    frame:SetSize(700, 632)
    frame:SetPoint("CENTER")
    frame:SetFrameStrata("DIALOG")
    frame:SetToplevel(true)
    frame:SetClampedToScreen(true)
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", function(self) self:StartMoving() end)
    frame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
    frame:Hide()
    ApplyMainTheme(frame)

    local title = Text(frame, "ChatFloater", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", 16, -13)
    title:SetTextColor(0.36, 0.78, 1.0, 1)

    local subtitle = Text(frame, T("CF_SUBTITLE", "Compact floating chat bar") .. " • /chatfloat /cf /chf", "GameFontHighlightSmall")
    subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -1)
    subtitle:SetTextColor(1, 1, 1, 0.50)

    CreateCloseButton(frame, -7, -5, 20)

    local leftX, rightX = 14, 360
    local colW = 326

    local channels = Section(frame, T("CF_CHANNELS", "Channels and buttons"), leftX, -54, colW, 166)
    local channelRows = {
        {"Say", "isSay", 12, -36}, {"Yell", "isYell", 112, -36}, {"Whisper", "isWhisper", 212, -36},
        {"Party", "isParty", 12, -61}, {"Raid", "isRaid", 112, -61}, {"Instance", "isInstance", 212, -61},
        {"Guild", "isGuild", 12, -86}, {"Roll", "isDiceButton", 112, -86}, {"Macro", "isMacroButton", 212, -86},
        {"Ready", "isReadyCheck", 12, -111}, {"Leave", "isLeaveReset", 112, -111}, {"Reload", "isReload", 212, -111},
    }
    for _, item in ipairs(channelRows) do
        CreateFlatCheckbox(channels, item[1], item[2], item[3], item[4])
    end

    local keyHint = Text(channels, T("CF_KEYBIND_HINT", "Assign the channel-cycle key in WoW Key Bindings."), "GameFontHighlightSmall")
    keyHint:SetPoint("TOPLEFT", 12, -141)
    keyHint:SetWidth(300)
    keyHint:SetJustifyH("LEFT")
    keyHint:SetTextColor(1, 1, 1, 0.52)

    local appearance = Section(frame, T("CF_APPEARANCE", "Appearance"), rightX, -54, colW, 166)
    CreateModernSlider(appearance, T("CF_SCALE", "Scale"), "chatUIScale", 0.65, 1.60, 0.05, 12, -36, 302)
    CreateModernSlider(appearance, T("CF_OPACITY", "Panel opacity"), "chatPanelOpacity", 0, 1, 0.05, 12, -67, 302)
    CreateModernSlider(appearance, T("CF_GLOW", "Glow strength"), "chatGlowStrength", 0, 1, 0.05, 12, -98, 302)
    CreateModernSlider(appearance, T("CF_SPACING", "Button spacing"), "chatButtonSpacing", 0, 12, 1, 12, -129, 302)

    local position = Section(frame, T("CF_POSITION", "Position"), leftX, -230, colW, 112)
    CreateFlatCheckbox(position, T("CF_MOVE_MODE", "Move mode: normal Left Drag"), "barUnlocked", 12, -36, function(value)
        if ns.SetBarUnlocked then ns.SetBarUnlocked(value) end
    end, 285)

    local moveHint = Text(position, T("CF_MOVE_HINT", "Shift + Left Drag always moves the bar."), "GameFontHighlightSmall")
    moveHint:SetPoint("TOPLEFT", 12, -58)
    moveHint:SetWidth(300)
    moveHint:SetJustifyH("LEFT")
    moveHint:SetTextColor(1, 1, 1, 0.50)

    CreateSoftButton(position, T("CF_RESET_POSITION", "Reset position"), 12, -83, 126, 20, function()
        if ns.ResetBarPosition then ns.ResetBarPosition() end
    end)
    CreateSoftButton(position, T("CF_ANCHOR_CHAT", "Anchor to chat"), 145, -83, 104, 20, function()
        DB().chatBarAnchorMode = "ANCHOR"
        if ns.OnChatBarPositionChanged then ns.OnChatBarPositionChanged() end
    end)
    CreateSoftButton(position, T("CF_FREE", "Free"), 256, -83, 58, 20, function()
        DB().chatBarAnchorMode = "FREE"
        if ns.OnChatBarPositionChanged then ns.OnChatBarPositionChanged() end
    end)

    local behavior = Section(frame, T("CF_BEHAVIOR", "Behavior"), rightX, -230, colW, 112)
    CreateFlatCheckbox(behavior, T("CF_ACTIVE", "Highlight active channel"), "chatShowActiveChannel", 12, -36, nil, 182)
    CreateFlatCheckbox(behavior, T("CF_BACKGROUND", "Panel background"), "chatPanelBackground", 12, -61, nil, 182)
    CreateFlatCheckbox(behavior, T("CF_ENABLED", "Enable ChatFloater"), "isChatBar", 12, -86, function(value)
        if ns.OnChatBarToggle then ns.OnChatBarToggle(value) end
    end, 182)

    local function UpdateLayoutButtons(dir)
        if horizontalButton then horizontalButton:SetActive(dir ~= "VERTICAL") end
        if verticalButton then verticalButton:SetActive(dir == "VERTICAL") end
    end

    horizontalButton = CreateSoftButton(behavior, T("CF_HORIZONTAL", "Horizontal"), 202, -34, 112, 20, function()
        DB().chatLayoutDirection = "HORIZONTAL"
        UpdateLayoutButtons("HORIZONTAL")
        Apply()
        ns.RefreshGUI()
    end)
    verticalButton = CreateSoftButton(behavior, T("CF_VERTICAL", "Vertical"), 202, -59, 112, 20, function()
        DB().chatLayoutDirection = "VERTICAL"
        UpdateLayoutButtons("VERTICAL")
        Apply()
        ns.RefreshGUI()
    end)
    UpdateLayoutButtons(DB().chatLayoutDirection)

    local border = Section(frame, T("CF_BORDER_TITLE", "Border"), 14, -352, 672, 124)
    CreateModernSlider(border, T("CF_BORDER_SIZE", "Border Size"), "chatPanelBorderSize", 1, 32, 1, 12, -36, 205)
    CreateModernSlider(border, T("CF_BORDER_OFFSET", "Border Offset"), "chatPanelBorderOffset", 0, 16, 1, 233, -36, 205)
    CreateModernSlider(border, T("CF_BORDER_OPACITY", "Border Opacity"), "chatPanelBorderOpacity", 0, 1, 0.05, 454, -36, 205)

    local textureTitle = Text(border, T("CF_BORDER_TEXTURE", "Border Texture"), "GameFontNormalSmall")
    textureTitle:SetPoint("TOPLEFT", 12, -78)
    textureTitle:SetTextColor(1, 1, 1, 0.84)

    borderTextureButton = CreateSoftButton(border, "", 12, -95, 300, 20, function(self)
        if borderTexturePicker and borderTexturePicker:IsShown() then
            borderTexturePicker:Hide()
        else
            OpenBorderTexturePicker(self)
        end
    end)
    borderTextureButton._text:ClearAllPoints()
    borderTextureButton._text:SetPoint("LEFT", 8, 0)
    borderTextureButton._text:SetPoint("RIGHT", -28, 0)
    borderTextureButton._text:SetJustifyH("LEFT")

    local arrowDivider = borderTextureButton:CreateTexture(nil, "ARTWORK")
    arrowDivider:SetPoint("TOPRIGHT", -22, -3)
    arrowDivider:SetPoint("BOTTOMRIGHT", -22, 3)
    arrowDivider:SetWidth(1)
    arrowDivider:SetColorTexture(1, 1, 1, 0.08)

    local arrow = Text(borderTextureButton, "v", "GameFontNormalSmall")
    arrow:SetPoint("RIGHT", -7, 1)
    arrow:SetTextColor(1, 1, 1, 0.52)

    local colorTitle = Text(border, T("CF_BORDER_COLOR", "Border Color"), "GameFontNormalSmall")
    colorTitle:SetPoint("TOPLEFT", 350, -78)
    colorTitle:SetTextColor(1, 1, 1, 0.84)

    borderColorSwatch = CreateFrame("Button", nil, border, "BackdropTemplate")
    borderColorSwatch:SetPoint("TOPLEFT", 350, -95)
    borderColorSwatch:SetSize(76, 20)
    borderColorSwatch:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    borderColorSwatch:SetBackdropBorderColor(1, 1, 1, 0.14)
    borderColorSwatch:SetScript("OnEnter", function(self)
        local r, g, b = ClassColor()
        self:SetBackdropBorderColor(r, g, b, 0.65)
    end)
    borderColorSwatch:SetScript("OnLeave", function(self)
        self:SetBackdropBorderColor(1, 1, 1, 0.14)
    end)
    borderColorSwatch:SetScript("OnClick", OpenColorPicker)

    local colorHint = Text(border, T("CF_BORDER_COLOR_HINT", "Click to choose"), "GameFontHighlightSmall")
    colorHint:SetPoint("LEFT", borderColorSwatch, "RIGHT", 8, 0)
    colorHint:SetTextColor(1, 1, 1, 0.48)

    local bcb = Section(frame, "BigChatBox", 14, -486, 672, 102)
    CreateFlatCheckbox(bcb, T("CF_BCB_INTEGRATION", "Adaptive BigChatBox integration"), "bigChatBoxIntegration", 12, -36, function()
        if ns.OnChatBarPositionChanged then ns.OnChatBarPositionChanged() end
        ns.RefreshGUI()
    end, 330)

    statusText = Text(bcb, "", "GameFontHighlightSmall")
    statusText:SetPoint("TOPRIGHT", bcb, "TOPRIGHT", -12, -37)
    statusText:SetWidth(190)
    statusText:SetJustifyH("RIGHT")

    local bcbHint = Text(bcb, T("CF_BCB_HINT",
        "When BigChatBox is visible, ChatFloater uses it as the chat anchor. Combat/protected states automatically fall back to Blizzard chat."),
        "GameFontHighlightSmall")
    bcbHint:SetPoint("TOPLEFT", 12, -62)
    bcbHint:SetWidth(646)
    bcbHint:SetJustifyH("LEFT")
    bcbHint:SetTextColor(1, 1, 1, 0.48)

    local footer = Text(frame, T("CF_SAVED", "Settings are stored in ChatFloaterDB (SavedVariables)."), "GameFontDisableSmall")
    footer:SetPoint("BOTTOMLEFT", 16, 18)
    footer:SetTextColor(1, 1, 1, 0.40)

    frame:SetScript("OnShow", function()
        ns.RefreshGUI()
    end)
    frame:SetScript("OnHide", function()
        if borderTexturePicker then borderTexturePicker:Hide() end
        if borderColorPicker then borderColorPicker:Hide() end
    end)

    return frame
end

function ns.RefreshGUI()
    if not frame or not ChatFloaterDB then return end

    refreshing = true

    for _, control in ipairs(controls) do
        if control.key then
            if control.kind == "check" and control.SetValue then
                control:SetValue(ChatFloaterDB[control.key] == true)
            elseif control.kind == "slider" then
                local value = tonumber(ChatFloaterDB[control.key]) or 0
                control:SetValue(value)
                if control._UpdateVisual then control._UpdateVisual(value) end
            end
        end
    end

    if horizontalButton then
        horizontalButton:SetActive(ChatFloaterDB.chatLayoutDirection ~= "VERTICAL")
    end
    if verticalButton then
        verticalButton:SetActive(ChatFloaterDB.chatLayoutDirection == "VERTICAL")
    end

    if borderTextureButton and borderTextureButton._text then
        borderTextureButton._text:SetText(BorderTextureLabel(ChatFloaterDB.chatPanelBorderTexture))
    end

    if borderColorSwatch then
        local color = ChatFloaterDB.chatPanelBorderColor or { r = 0.38, g = 0.44, b = 0.52 }
        borderColorSwatch:SetBackdropColor(color.r or 0.38, color.g or 0.44, color.b or 0.52, 1)
        borderColorSwatch:SetBackdropBorderColor(1, 1, 1, 0.14)
    end

    if statusText then
        local found, version, visible = false, nil, false
        if ns.GetBigChatBoxStatus then
            found, version, visible = ns.GetBigChatBoxStatus()
        end

        if found then
            local state = visible and T("CF_BCB_ACTIVE", "active") or T("CF_BCB_FALLBACK", "Blizzard fallback")
            statusText:SetText("BigChatBox " .. tostring(version or "") .. " • " .. state)
            statusText:SetTextColor(visible and 0.35 or 0.78, visible and 0.90 or 0.65, visible and 0.50 or 0.35)
        else
            statusText:SetText(T("CF_BCB_NOT_FOUND", "BigChatBox not detected"))
            statusText:SetTextColor(0.72, 0.50, 0.35)
        end
    end

    refreshing = false
end

function ns.ToggleGUI()
    local f = Build()
    if f:IsShown() then
        f:Hide()
    else
        f:Show()
    end
end
