local _, Orbit = ...
local Engine = Orbit.Engine
local Constants = Orbit.Constants
local Layout = Engine.Layout
local L = Orbit.L

-- [ VALUE-COLUMN CONTROLS ]--------------------------------------------------------------------------
-- Both right-align off Constants.Widget.ValueInset so all value-column controls line up regardless of host widget.
local WHITE8x8 = "Interface\\Buttons\\WHITE8x8"
local CHECKER = "Interface\\AddOns\\Orbit\\Core\\assets\\Other\\Orbit_Checkerboard"  -- "no tint" (none) swatch fill

local function ResolvePreviewColor(curveMode, value)
    if not value then return 1, 1, 1, 1 end
    if curveMode then
        local pins = value.pins
        if not pins or #pins == 0 then return 0.5, 0.5, 0.5, 1 end
        local first = pins[1]
        for i = 2, #pins do
            if pins[i].position < first.position then first = pins[i] end
        end
        if first.type == "class" then
            local r, g, b = Engine.ClassColor:GetCurrentClassColorUnpacked()
            return r, g, b, (first.color and first.color.a) or 1
        end
        local c = first.color or {}
        return c.r or 1, c.g or 1, c.b or 1, c.a or 1
    end
    return Engine.ClassColor:ResolveValueUnpacked(value)
end

-- Paints the swatch fill: a colour preview, or a checker tile when the value is the "no tint" (none) state.
local function RenderSwatchColor(swatch, allowNone)
    if allowNone and swatch.value and swatch.value.none then
        swatch.Color:SetTexture(CHECKER)
        swatch.Color:SetVertexColor(1, 1, 1, 1)
    else
        swatch.Color:SetColorTexture(1, 1, 1, 1)
        swatch.Color:SetVertexColor(ResolvePreviewColor(swatch.curveMode, swatch.value))
    end
end

-- initialValue + enabled accept a getter; resolved on every render so the swatch tracks current SavedVariables.
function Layout:ApplyValueColorSwatch(frame, cfg, anchorX)
    local enabled = cfg.enabled
    if type(enabled) == "function" then enabled = enabled() end
    if enabled == false then
        if frame.ValueColorSwatch then frame.ValueColorSwatch:Hide() end
        return
    end

    if not frame.ValueColorSwatch then
        local swatch = CreateFrame("Button", nil, frame, "BackdropTemplate")
        swatch:SetSize(Constants.Widget.ValueSwatchSize, Constants.Widget.ValueSwatchSize)
        swatch:SetBackdrop({ bgFile = WHITE8x8, edgeFile = WHITE8x8, edgeSize = 1 })
        swatch:SetBackdropBorderColor(0, 0, 0, 1)
        swatch.Color = swatch:CreateTexture(nil, "OVERLAY")
        swatch.Color:SetPoint("TOPLEFT", 1, -1)
        swatch.Color:SetPoint("BOTTOMRIGHT", -1, 1)
        swatch.Color:SetColorTexture(1, 1, 1, 1)
        frame.ValueColorSwatch = swatch
    end

    local C = Constants
    local swatch = frame.ValueColorSwatch
    swatch.curveMode = cfg.curve and true or false
    local initial = cfg.initialValue
    if type(initial) == "function" then initial = initial() end
    swatch.value = initial

    swatch:ClearAllPoints()
    -- value-column controls are right-aligned; a lone swatch flushes to the column's right edge
    local ax = anchorX or (C.Widget.ValueSwatchSize / 2 + C.Widget.ValueInset)
    swatch:SetPoint("CENTER", frame, "RIGHT", -Engine.Pixel:Snap(ax, frame:GetEffectiveScale()), 0)
    if cfg.allowNone then swatch:RegisterForClicks("LeftButtonUp", "RightButtonUp") else swatch:RegisterForClicks("LeftButtonUp") end
    RenderSwatchColor(swatch, cfg.allowNone)

    swatch:SetScript("OnClick", function(_, button)
        if cfg.allowNone and button == "RightButton" then
            swatch.value = { none = true }
            RenderSwatchColor(swatch, true)
            if cfg.callback then cfg.callback(swatch.value) end
            return
        end
        local lib = LibStub and LibStub("LibOrbitColorPicker-1.0", true)
        if not lib then return end
        local as = Orbit.db and Orbit.db.AccountSettings
        if as and not as.RecentColors then as.RecentColors = {} end

        local initialData
        if swatch.curveMode then
            initialData = swatch.value
        else
            local v = swatch.value or {}
            initialData = { r = v.r or 1, g = v.g or 1, b = v.b or 1, a = v.a or 1 }
            if v.type then initialData.type = v.type end
        end

        lib:Open({
            initialData = initialData,
            forceSingleColor = not swatch.curveMode,
            recentColorsDb = as and as.RecentColors,
            anchor = Layout.GetPickerAnchor(swatch),
            onOpen = function(picker)
                if as and not as.ColorPickerTourComplete then
                    as.ColorPickerTourComplete = true
                    C_Timer.After(0.1, function() if picker:IsOpen() then picker:StartTour() end end)
                end
            end,
            callback = function(result, wasCancelled)
                if wasCancelled or not result then return end
                local newValue
                if swatch.curveMode then
                    if result.pins and #result.pins > 0 then
                        newValue = { pins = result.pins }
                        if result.desaturated ~= nil then newValue.desaturated = result.desaturated end
                    end
                else
                    local pin = result.pins and result.pins[1]
                    if pin and pin.color then
                        newValue = { r = pin.color.r, g = pin.color.g, b = pin.color.b, a = pin.color.a or 1 }
                        if pin.type then newValue.type = pin.type end
                    end
                end
                if not newValue then return end
                swatch.value = newValue
                RenderSwatchColor(swatch, cfg.allowNone)
                if cfg.callback then cfg.callback(newValue) end
            end,
        })
    end)

    if cfg.tooltip then
        swatch:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetText(cfg.tooltip, 1, 1, 1, 1, true)
            if cfg.allowNone then
                GameTooltip:AddLine(L.CFG_REMOVE_COLOR_TT, 0.6, 0.6, 0.6, true)
            end
            GameTooltip:Show()
        end)
        swatch:SetScript("OnLeave", GameTooltip_Hide)
    else
        swatch:SetScript("OnEnter", nil)
        swatch:SetScript("OnLeave", nil)
    end

    swatch:Show()
    return swatch
end

-- [ VALUE-COLUMN CHECKBOX ]--------------------------------------------------------------------------
-- tooltip is a string OR `function(checked) → title, subtext` that re-resolves live as the box is toggled.
function Layout:ApplyValueCheckbox(frame, cfg, anchorX)
    if not frame.ValueCheckbox then
        frame.ValueCheckbox = CreateFrame("CheckButton", nil, frame, "UICheckButtonTemplate")
        frame.ValueCheckbox:SetSize(Constants.Widget.ValueCheckboxSize, Constants.Widget.ValueCheckboxSize)
    end

    local C = Constants
    local vcb = frame.ValueCheckbox
    vcb:ClearAllPoints()
    local ax = anchorX or (C.Widget.ValueSwatchSize / 2 + C.Widget.ValueInset)
    vcb:SetPoint("CENTER", frame, "RIGHT", -Engine.Pixel:Snap(ax, frame:GetEffectiveScale()), 0)

    local initial = cfg.initialValue
    if type(initial) == "function" then initial = initial() end
    vcb:SetChecked(initial or false)

    -- Render from OnClick too so a function-tooltip re-resolves against the live checked state while the cursor lingers.
    local function RenderTooltip(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        local tt = cfg.tooltip
        if type(tt) == "function" then
            local title, subtext = tt(self:GetChecked())
            GameTooltip:SetText(title, 1, 1, 1, 1, true)
            if subtext then GameTooltip:AddLine(subtext, 0.6, 0.6, 0.6, true) end
        else
            GameTooltip:SetText(tt, 1, 1, 1, 1, true)
        end
        GameTooltip:Show()
    end

    vcb:SetScript("OnClick", function(self)
        if cfg.callback then cfg.callback(self:GetChecked()) end
        if cfg.tooltip and GameTooltip:GetOwner() == self then RenderTooltip(self) end
    end)

    if cfg.tooltip then
        vcb:SetScript("OnEnter", RenderTooltip)
        vcb:SetScript("OnLeave", GameTooltip_Hide)
    else
        vcb:SetScript("OnEnter", nil)
        vcb:SetScript("OnLeave", nil)
    end

    vcb:Show()
    return vcb
end
