---@type Orbit
local Orbit = Orbit
local OrbitEngine = Orbit.Engine
local Constants = Orbit.Constants

-- [ COOLDOWN UTILS ] --------------------------------------------------------------------------------
local CooldownUtils = {}
local HORIZONTAL_EDGES = { LEFT = true, RIGHT = true }

-- [ INHERITED PARENT RESOLVER ] ---------------------------------------------------------------------
function CooldownUtils:GetInheritedParentIndex(anchorFrame, viewerMap)
    local anchors = OrbitEngine.FrameAnchor and OrbitEngine.FrameAnchor.anchors
    if not anchors then
        return nil
    end
    local current, rootIndex = anchorFrame, nil
    while true do
        local info = anchors[current]
        if not info or not HORIZONTAL_EDGES[info.edge] then
            break
        end
        local parent = info.parent
        local entry = parent and parent.systemIndex and viewerMap[parent.systemIndex]
        if not (entry and entry.anchor == parent) then
            break
        end
        rootIndex = parent.systemIndex
        current = parent
    end
    return rootIndex
end

function CooldownUtils:IsInheritingLayout(plugin, anchorFrame, viewerMap)
    return self:GetInheritedParentIndex(anchorFrame, viewerMap) ~= nil
end

-- [ SKIN SETTINGS BUILDER ] -------------------------------------------------------------------------
function CooldownUtils:BuildSkinSettings(plugin, systemIndex, options)
    options = options or {}
    local inherited = options.inheritOverrides
    return {
        style = options.style or 1,
        aspectRatio = (inherited and inherited.aspectRatio) or plugin:GetSetting(systemIndex, "aspectRatio") or "1:1",
        zoom = options.zoom or 0,
        borderStyle = options.borderStyle or 1,
        borderSize = Orbit.db.GlobalSettings.BorderSize,
        iconBorder = true,
        activeSwipeColor = OrbitEngine.ColorCurve:GetFirstColorFromCurve(plugin:GetSetting(systemIndex, "ActiveSwipeColorCurve"))
            or { r = 1, g = 0.95, b = 0.57, a = 0.7 },
        cooldownSwipeColor = OrbitEngine.ColorCurve:GetFirstColorFromCurve(plugin:GetSetting(systemIndex, "CooldownSwipeColorCurve"))
            or plugin:GetSetting(systemIndex, "SwipeColor")
            or { r = 0, g = 0, b = 0, a = 0.8 },
        orientation = plugin:GetSetting(systemIndex, "Orientation"),
        limit = plugin:GetSetting(systemIndex, "IconLimit"),
        padding = (inherited and inherited.padding) or plugin:GetSetting(systemIndex, "IconPadding"),
        size = (inherited and inherited.size) or plugin:GetSetting(systemIndex, "IconSize"),
        showTimer = plugin:GetSetting(systemIndex, "ShowTimer"),
        showGCDSwipe = plugin:GetSetting(systemIndex, "ShowGCDSwipe"),
        baseIconSize = Constants.Skin.DefaultIconSize,
        backdropColor = plugin:GetSetting(systemIndex, "BackdropColour"),
        showTooltip = options.showTooltip or false,
        verticalGrowth = options.verticalGrowth,
        horizontalGrowth = options.horizontalGrowth,
    }
end

-- [ TEXT COLOR APPLIER ] ----------------------------------------------------------------------------
-- Delegates to OverrideUtils.ApplyTextColor (which handles overrides + global FontColorCurve fallback).
-- remainingPercent: optional 0-1 value for progress-aware curve sampling (1=full, 0=expired)
function CooldownUtils:ApplyTextColor(textElement, overrides, remainingPercent)
    if not textElement or not textElement.SetTextColor then
        return
    end

    local OverrideUtils = OrbitEngine.OverrideUtils
    if OverrideUtils then
        OverrideUtils.ApplyTextColor(textElement, overrides, remainingPercent)
    end
end

-- [ ICON DIMENSION CALCULATOR ] ---------------------------------------------------------------------
function CooldownUtils:CalculateIconDimensions(plugin, systemIndex, overrides)
    local iconSize = (overrides and overrides.size) or plugin:GetSetting(systemIndex, "IconSize") or Constants.Skin.DefaultIconSize
    local aspectRatio = (overrides and overrides.aspectRatio) or plugin:GetSetting(systemIndex, "aspectRatio") or "1:1"
    local w, h = iconSize, iconSize
    if aspectRatio == "16:9" then h = iconSize * (9 / 16)
    elseif aspectRatio == "4:3" then h = iconSize * (3 / 4)
    elseif aspectRatio == "21:9" then h = iconSize * (9 / 21) end
    local Pixel = OrbitEngine.Pixel
    if Pixel then w = Pixel:Snap(w); h = Pixel:Snap(h); iconSize = Pixel:Snap(iconSize) end
    return w, h, iconSize
end

-- [ SIMPLE TEXT APPLIER ] ---------------------------------------------------------------------------
function CooldownUtils:ApplySimpleTextStyle(plugin, systemIndex, textElement, componentKey, defaultAnchor, defaultOffsetX, defaultOffsetY)
    if not textElement then
        return
    end

    local fontPath = plugin:GetGlobalFont()
    local baseSize = plugin:GetBaseFontSize()
    local positions = plugin:GetComponentPositions(systemIndex)
    local pos = positions[componentKey] or {}
    local overrides = pos.overrides or {}
    local defaultSize = math.max(6, baseSize)

    local OverrideUtils = OrbitEngine.OverrideUtils
    if OverrideUtils then
        OverrideUtils.ApplyOverrides(textElement, overrides, { fontSize = defaultSize, fontPath = fontPath })
    end

    local ApplyTextPosition = OrbitEngine.PositionUtils and OrbitEngine.PositionUtils.ApplyTextPosition
    if ApplyTextPosition then
        ApplyTextPosition(textElement, textElement:GetParent(), pos, defaultAnchor, defaultOffsetX, defaultOffsetY)
    end
end

-- [ CHARGE COMPLETION TRACKING ] --------------------------------------------------------------------
-- All `_charges`/`_maxCharges`/`_knownRechargeDuration` storage sites guard against secret values,
-- but we defend the arithmetic here too: if any field is secret we skip the tick entirely.
function CooldownUtils:OnChargeCast(obj)
    if issecretvalue(obj._charges) or issecretvalue(obj._knownRechargeDuration) then return end
    if not obj._charges or obj._charges <= 0 then
        return
    end
    obj._charges = obj._charges - 1
    if not obj._rechargeEndsAt and obj._knownRechargeDuration then
        obj._rechargeEndsAt = GetTime() + obj._knownRechargeDuration
    end
end

function CooldownUtils:TrackChargeCompletion(obj)
    if issecretvalue(obj._charges) or issecretvalue(obj._maxCharges) or issecretvalue(obj._knownRechargeDuration) then return end
    local duration = obj._knownRechargeDuration
    if not obj._rechargeEndsAt or not obj._charges or not obj._maxCharges or not duration or duration <= 0 then
        return
    end
    if obj._charges >= obj._maxCharges then
        obj._rechargeEndsAt = nil
        return
    end
    while obj._rechargeEndsAt and GetTime() >= obj._rechargeEndsAt do
        obj._charges = obj._charges + 1
        if obj._charges < obj._maxCharges then
            obj._rechargeEndsAt = obj._rechargeEndsAt + duration
        else
            obj._rechargeEndsAt = nil
            break
        end
    end
end

-- [ REUSABLE CHILD BUFFER ] -------------------------------------------------------------------------
local _sharedChildBuf = {}
function CooldownUtils:PackChildren(...)
    wipe(_sharedChildBuf)
    for i = 1, select('#', ...) do _sharedChildBuf[i] = select(i, ...) end
    return _sharedChildBuf
end

-- Export to Orbit Engine
OrbitEngine.CooldownUtils = CooldownUtils
