--[[
    Commander 3.0 - CLI Spinner

    Animated loading indicator for async operations.
    Updates a line in-place using the virtual buffer system.
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Spinner Module
-------------------------------------------------------------------------------

local Spinner = {}
C3.Spinner = Spinner

-- Use shared utilities
local GetThemeColor = function(...) return C3.Colors.GetColor(...) end

-- Spinner animation frames (using WoW-safe characters)
Spinner.STYLES = {
    dots = { ".  ", ".. ", "...", " ..", "  .", " .." },
    fade = { "o", "O", "o", "." },
    pulse = { "*", "+", "*", "+" },
    spin = { "-", "\\", "-", "/" },  -- Note: avoided | due to WoW escaping
    grow = { ".", "o", "O", "o" },
}

-- Default style
Spinner.DEFAULT_STYLE = "dots"

-- Active spinners
local activeSpinners = {}
local spinnerFrame = nil

-------------------------------------------------------------------------------
-- Spinner Instance
-------------------------------------------------------------------------------

local SpinnerInstance = {}
SpinnerInstance.__index = SpinnerInstance

---Create a new spinner
---@param options table { message, style, color }
---@return table spinner instance
function Spinner:Create(options)
    options = options or {}

    local instance = setmetatable({
        message = options.message or "Loading",
        style = options.style or self.DEFAULT_STYLE,
        color = options.color or GetThemeColor("status.dim") or "4C566A",
        frames = self.STYLES[options.style or self.DEFAULT_STYLE] or self.STYLES.line,
        frameIndex = 1,
        line = nil,
        lineId = nil,
        startTime = GetTime(),
        interval = options.interval or 0.1,
        lastUpdate = 0,
        running = false,
    }, SpinnerInstance)

    return instance
end

---Start the spinner and add it to the terminal
---@param terminal table Optional terminal reference (defaults to C3.Terminal)
---@return table self
function SpinnerInstance:Start(terminal)
    terminal = terminal or C3.Terminal
    if not terminal or not terminal.output then
        return self
    end

    self.terminal = terminal
    self.running = true

    -- Add initial line to buffer
    local content = self:_formatLine()
    self.line = terminal.output:AddLine(content)
    if self.line then
        self.lineId = self.line.id
    end

    -- Register for updates
    activeSpinners[self] = true
    Spinner:_ensureUpdateFrame()

    -- Scroll to bottom
    terminal.output:ScrollToBottom()

    return self
end

---Stop the spinner and optionally replace with final message
---@param finalMessage string|nil Final message to display (nil to keep spinner text)
---@param finalColor string|nil Color for final message
function SpinnerInstance:Stop(finalMessage, finalColor)
    self.running = false
    activeSpinners[self] = nil

    -- Update line with final message if provided
    if finalMessage and self.lineId and self.terminal then
        local color = finalColor or GetThemeColor("accent.success") or "A3BE8C"
        local content = string.format("|cff%s%s|r", color, finalMessage)
        self.terminal.output.buffer:UpdateLine(self.lineId, content)
        self.terminal.output:Refresh()
    end
end

---Update the spinner animation
function SpinnerInstance:Update()
    if not self.running then return end

    local now = GetTime()
    if now - self.lastUpdate < self.interval then
        return
    end
    self.lastUpdate = now

    -- Advance frame
    self.frameIndex = self.frameIndex + 1
    if self.frameIndex > #self.frames then
        self.frameIndex = 1
    end

    -- Update the line content
    if self.lineId and self.terminal and self.terminal.output then
        local content = self:_formatLine()
        self.terminal.output.buffer:UpdateLine(self.lineId, content)
        self.terminal.output:Refresh()
    end
end

---Format the spinner line
---@return string
function SpinnerInstance:_formatLine()
    local frame = self.frames[self.frameIndex]
    local elapsed = GetTime() - self.startTime
    local elapsedStr = string.format("%.1fs", elapsed)

    local dimColor = GetThemeColor("status.dim") or "4C566A"
    return string.format("|cff%s%s %s|r |cff%s(%s)|r",
        self.color, frame, self.message, dimColor, elapsedStr)
end

-------------------------------------------------------------------------------
-- Update Frame Management
-------------------------------------------------------------------------------

function Spinner:_ensureUpdateFrame()
    if spinnerFrame then return end

    spinnerFrame = CreateFrame("Frame")
    spinnerFrame:SetScript("OnUpdate", function(self, elapsed)
        for spinner in pairs(activeSpinners) do
            spinner:Update()
        end

        -- Clean up if no active spinners
        if not next(activeSpinners) then
            spinnerFrame:SetScript("OnUpdate", nil)
            spinnerFrame = nil
        end
    end)
end

-------------------------------------------------------------------------------
-- Convenience Functions
-------------------------------------------------------------------------------

---Quick spinner creation and start
---@param message string
---@param options table|nil
---@return table spinner
function Spinner:Show(message, options)
    options = options or {}
    options.message = message
    local spinner = self:Create(options)
    return spinner:Start()
end

return Spinner
