local _, ns = ...
local SlickUI = ns.SlickUI
local Theme = SlickUI.Theme

local Skyriding = {
	elapsed = 0,
	flagElapsed = 0,
	NormalBarActive = false,
	OverrideBarActive = false,
	Active = false,
}
SlickUI.Skyriding = Skyriding

local SURGE_FORWARD_SPELL_ID = 372608

local function SafeNumber(value)
	if canaccessvalue and not canaccessvalue(value) then return nil end
	if issecretvalue and issecretvalue(value) then return nil end
	return type(value) == "number" and value or nil
end

local function IsAccessible(value)
	if canaccessvalue and not canaccessvalue(value) then return false end
	if issecretvalue and issecretvalue(value) then return false end
	return true
end

local function IsSurgeForwardSpell(spellID)
	if not SafeNumber(spellID) then return false end
	if spellID == SURGE_FORWARD_SPELL_ID then return true end
	if not C_Spell or type(C_Spell.GetSpellName) ~= "function" then return false end
	local expected = C_Spell.GetSpellName(SURGE_FORWARD_SPELL_ID)
	local resolved = C_Spell.GetSpellName(spellID)
	return IsAccessible(expected) and IsAccessible(resolved)
		and type(expected) == "string" and expected ~= "" and resolved == expected
end

local function GetButtonActionSpell(button)
	if not button or type(GetActionInfo) ~= "function" then return nil end
	local slot = SafeNumber(button.action)
	if not slot and button.GetAttribute then
		local ok, attribute = pcall(button.GetAttribute, button, "action")
		if ok then slot = SafeNumber(attribute) end
	end
	if not slot then return nil end
	local ok, actionType, actionID = pcall(GetActionInfo, slot)
	if not ok or not IsAccessible(actionType) or not IsAccessible(actionID)
		or actionType ~= "spell" then return nil end
	return SafeNumber(actionID)
end

local function NormalBarHasSurgeForward()
	for index = 1, 12 do
		if GetButtonActionSpell(_G["ActionButton" .. index]) == SURGE_FORWARD_SPELL_ID then
			return true
		end
	end
	return false
end

local function OverrideBarHasSurgeForward()
	local prefixes = { "OverrideActionBarButton", "VehicleMenuBarActionButton", "ActionButton" }
	for _, prefix in ipairs(prefixes) do
		for index = 1, 12 do
			local button = _G[prefix .. index]
			if button and (not button.IsShown or button:IsShown())
				and IsSurgeForwardSpell(GetButtonActionSpell(button)) then
				return true
			end
		end
	end
	return false
end

function Skyriding:RefreshActiveFlag()
	-- These are deliberately independent flags. A false result from one bar
	-- family must never erase a true result from the other.
	self.NormalBarActive = NormalBarHasSurgeForward()
	self.OverrideBarActive = OverrideBarHasSurgeForward()
	self.Active = self.NormalBarActive or self.OverrideBarActive
	-- Hidden frames do not receive OnUpdate. Apply visibility immediately when
	-- an action-bar event changes the flag so the Vigor frame can wake back up.
	if self.Frame then self:Update() end
end

function Skyriding:GetPosition()
	local settings = SlickUI.DB.skyriding
	return settings.position or SlickUI.Defaults.skyriding.position
end

function Skyriding:SavePosition()
	local frame = self.Frame
	local x, y = frame:GetCenter()
	local parentX, parentY = UIParent:GetCenter()
	if not x or not y or not parentX or not parentY then return end
	SlickUI.DB.skyriding.position = { x = x - parentX, y = y - parentY }
end

local function ReadSharedSkyridingCharges(segmentLimit)
	if InCombatLockdown() then return nil end
	local chargeInfo = C_Spell and C_Spell.GetSpellCharges and C_Spell.GetSpellCharges(SURGE_FORWARD_SPELL_ID)
	local charges = chargeInfo and chargeInfo.currentCharges
	local maximum = chargeInfo and chargeInfo.maxCharges
	if charges == nil or not maximum or maximum <= 0 or maximum > segmentLimit then return nil end
	local start = chargeInfo.cooldownStartTime
	local duration = chargeInfo.cooldownDuration
	if charges < maximum and start and duration and duration > 0 then
		charges = charges + math.max(0, math.min(1, (GetTime() - start) / duration))
	end
	return charges, maximum
end

function Skyriding:ReadVigor()
	-- Follow the same resolved ActionButtons that SlickUI displays. Both
	-- skyriding mounts and Druid Travel Form page Surge Forward onto this bar.
	if not self.Active then return nil end

	local charges, chargeMaximum = ReadSharedSkyridingCharges(#self.Segments)
	if charges ~= nil then
		return charges, chargeMaximum
	end

	-- Compatibility fallback for clients that still expose Vigor as mount power.
	local powerType = Enum and Enum.PowerType and Enum.PowerType.AlternateMount
	if not powerType then return nil end
	local current = SafeNumber(UnitPower("player", powerType))
	local maximum = SafeNumber(UnitPowerMax("player", powerType))
	if not current or not maximum or maximum <= 0 then return nil end
	local modifier = type(UnitPowerDisplayMod) == "function" and SafeNumber(UnitPowerDisplayMod(powerType)) or 1
	modifier = modifier and modifier > 0 and modifier or 1
	current, maximum = current / modifier, maximum / modifier
	if maximum <= 0 or maximum > #self.Segments then return nil end
	return current, math.floor(maximum + 0.5)
end

function Skyriding:Update()
	local current, maximum = self:ReadVigor()
	local editing = SlickUI.DB and SlickUI.DB.locked == false
	local visible = current ~= nil and maximum ~= nil
	self.Frame:SetShown(visible or editing)
	self.Frame:EnableMouse(editing)
	self.Preview:SetShown(editing and not visible)
	maximum = maximum or 6
	current = current or maximum

	local spacing = 4
	local innerWidth = self.Bar:GetWidth()
	if not innerWidth or innerWidth <= 0 then innerWidth = self.Frame:GetWidth() - 8 end
	local width = (innerWidth - spacing * (maximum - 1)) / maximum
	for index, segment in ipairs(self.Segments) do
		segment:ClearAllPoints()
		if index <= maximum then
			segment:SetWidth(width)
			segment:SetPoint("LEFT", self.Bar, "LEFT", (index - 1) * (width + spacing), 0)
			segment:SetValue(math.max(0, math.min(1, current - index + 1)))
			segment:Show()
		else
			segment:Hide()
		end
	end
end

function Skyriding:Create()
	local frame = CreateFrame("Frame", "SlickUISkyridingVigor", UIParent, "BackdropTemplate")
	frame:SetSize(300, 30)
	frame:SetFrameStrata("MEDIUM")
	frame:SetClampedToScreen(true)
	frame:SetMovable(true)
	frame:RegisterForDrag("LeftButton")
	local position = self:GetPosition()
	frame:SetPoint("CENTER", UIParent, "CENTER", position.x, position.y)
	Theme:CreatePanel(frame, 0.86, true)
	frame:SetScript("OnDragStart", function(self)
		if SlickUI.DB.locked == false and not InCombatLockdown() then self:StartMoving() end
	end)
	frame:SetScript("OnDragStop", function(self)
		self:StopMovingOrSizing()
		Skyriding:SavePosition()
	end)
	frame:SetScript("OnUpdate", function(_, elapsed)
		Skyriding.elapsed = Skyriding.elapsed + elapsed
		Skyriding.flagElapsed = Skyriding.flagElapsed + elapsed
		if Skyriding.flagElapsed >= 0.50 then
			Skyriding.flagElapsed = 0
			Skyriding:RefreshActiveFlag()
		end
		if Skyriding.elapsed >= 0.10 then
			Skyriding.elapsed = 0
			Skyriding:Update()
		end
	end)

	local label = Theme:CreateText(frame, 10, "OVERLAY")
	label:SetPoint("BOTTOM", frame, "TOP", 0, 3)
	label:SetText("SKYRIDING VIGOR")
	label:SetTextColor(0.25, 0.80, 1.00)
	self.Preview = label

	local bar = CreateFrame("Frame", nil, frame)
	bar:SetPoint("TOPLEFT", 4, -4)
	bar:SetPoint("BOTTOMRIGHT", -4, 4)
	self.Bar = bar
	self.Segments = {}
	for index = 1, 10 do
		local segment = CreateFrame("StatusBar", nil, bar)
		segment:SetHeight(22)
		segment:SetStatusBarTexture(Theme.Texture)
		segment:SetStatusBarColor(0.12, 0.67, 1.00, 1)
		segment:SetMinMaxValues(0, 1)
		segment:SetValue(1)
		Theme:CreateBarBackground(segment)
		self.Segments[index] = segment
	end

	self.Frame = frame
	self.EventFrame = CreateFrame("Frame")
	for _, event in ipairs({
		"PLAYER_ENTERING_WORLD", "ACTIONBAR_PAGE_CHANGED", "ACTIONBAR_SLOT_CHANGED",
		"UPDATE_BONUS_ACTIONBAR", "UPDATE_OVERRIDE_ACTIONBAR",
		"PLAYER_MOUNT_DISPLAY_CHANGED", "UPDATE_SHAPESHIFT_FORM",
	}) do
		self.EventFrame:RegisterEvent(event)
	end
	self.EventFrame:SetScript("OnEvent", function()
		Skyriding:RefreshActiveFlag()
		C_Timer.After(0.25, function() Skyriding:RefreshActiveFlag() end)
		C_Timer.After(0.75, function() Skyriding:RefreshActiveFlag() end)
	end)
	self:RefreshActiveFlag()
end

C_Timer.After(0, function() Skyriding:Create() end)
