--[[
    Commander 3.0 - TUI App System

    Apps are full-screen TUI applications that take over the terminal.
    They own state and handle input/rendering lifecycle.

    Lifecycle:
        1. App:new(terminal) - Constructor
        2. app:Start() - Enter TUI mode, start render loop
        3. app:draw(buf) - Called each frame to render (override this)
        4. app:handleInput(action, key) - Called on input (override this)
        5. app:Stop() - Exit TUI mode, cleanup

    Usage:
        local MyApp = C3.TUI.App:extend()

        function MyApp:init(terminal)
            self:super("init", terminal)
            self.state = {}
        end

        function MyApp:draw(buf)
            -- Render widgets to buf
        end

        function MyApp:handleInput(action, key)
            if action == "cancel" then self:Stop() return true end
            return false
        end

        -- Launch
        MyApp:new(terminal):Start()
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- App Base Class
-------------------------------------------------------------------------------

local App = C3.class()
C3.TUI = C3.TUI or {}
C3.TUI.App = App

-- Default tick interval (seconds)
App.DEFAULT_TICK_INTERVAL = 0.1  -- 10 FPS

---Create a new TUI App
---@param terminal table TerminalWindow instance
function App:init(terminal)
    self.terminal = terminal
    self.running = false
    self.tickInterval = App.DEFAULT_TICK_INTERVAL
    self._ticker = nil
    self._buffer = nil
    self._dirty = true  -- Needs redraw
end

---Reassign app to a new terminal (used when tab is transferred to another window)
---@param newTerminal table TerminalWindow instance
function App:SetTerminal(newTerminal)
    self.terminal = newTerminal
    self._dirty = true  -- Force redraw on new terminal
end

---Start the app (enter TUI mode)
function App:Start()
    if self.running then return end

    if not self.terminal then
        C3:Debug("App:Start - No terminal")
        return
    end

    self.running = true

    -- Get buffer dimensions
    local cols, rows = self:GetSize()

    -- Create buffer
    self._buffer = C3.TUI.Buffer:new(cols, rows)

    -- Enter TUI mode
    self.terminal:EnterTuiMode(self)

    -- Call onStart hook if defined
    if self.onStart then
        self:onStart()
    end

    -- Initial render
    self:Render()

    -- Start tick loop
    self:_startTicker()

    C3:Debug("App started: " .. (self.name or "unnamed"))
end

---Stop the app (exit TUI mode)
function App:Stop()
    if not self.running then return end

    self.running = false

    -- Stop ticker
    self:_stopTicker()

    -- Call onStop hook if defined
    if self.onStop then
        self:onStop()
    end

    -- Cleanup buffer
    if self._buffer then
        self._buffer:Destroy()
        self._buffer = nil
    end

    -- Exit TUI mode
    if self.terminal then
        self.terminal:ExitTuiMode()
    end

    C3:Debug("App stopped: " .. (self.name or "unnamed"))
end

---Mark app as needing redraw
function App:MarkDirty()
    self._dirty = true
end

---Render a frame
function App:Render()
    if not self.running or not self._buffer then return end

    -- Skip if not dirty and using dirty-flag optimization
    if not self._dirty then return end
    self._dirty = false

    -- Clear buffer
    self._buffer:Clear()

    -- Call app's draw method
    if self.draw then
        self:draw(self._buffer)
    end

    -- Push to terminal
    if self.terminal and self.terminal.RenderTuiBuffer then
        self.terminal:RenderTuiBuffer(self._buffer)
    end
end

---Handle an input action
---@param action string Action name (up, down, select, cancel, etc.)
---@param key string Raw key that triggered action
---@return boolean handled
function App:HandleInput(action, key)
    if not self.running then return false end

    local handled = false

    if self.handleInput then
        handled = self:handleInput(action, key)
    end

    -- If input was handled, mark dirty for redraw
    if handled then
        self:MarkDirty()
    end

    return handled
end

---Handle raw key (for games)
---@param key string
---@param isDown boolean
function App:HandleRawKey(key, isDown)
    if not self.running then return end

    if self.handleRawKey then
        self:handleRawKey(key, isDown)
        self:MarkDirty()
    end
end

---Get terminal size in characters
---@return number cols, number rows
function App:GetSize()
    if self.terminal and self.terminal.GetTuiSize then
        return self.terminal:GetTuiSize()
    end
    return 80, 24
end

---Get the buffer
---@return table|nil TuiBuffer
function App:GetBuffer()
    return self._buffer
end

---Set tick interval (render refresh rate)
---@param interval number Seconds between ticks
function App:SetTickInterval(interval)
    self.tickInterval = interval
    if self._ticker then
        self:_stopTicker()
        self:_startTicker()
    end
end

---Pause rendering (stop ticker but stay in TUI mode)
function App:Pause()
    self:_stopTicker()
end

---Resume rendering
function App:Resume()
    if self.running then
        self:_startTicker()
    end
end

-------------------------------------------------------------------------------
-- Internal Methods
-------------------------------------------------------------------------------

---Start the render ticker
function App:_startTicker()
    if self._ticker then return end
    if self.tickInterval <= 0 then return end

    self._ticker = C_Timer.NewTicker(self.tickInterval, function()
        if not self.running then return end

        -- Call tick hook if defined
        if self.tick then
            self:tick()
        end

        -- Render (respects dirty flag)
        self:Render()
    end)
end

---Stop the render ticker
function App:_stopTicker()
    if self._ticker then
        self._ticker:Cancel()
        self._ticker = nil
    end
end

-------------------------------------------------------------------------------
-- Helper Methods for Subclasses
-------------------------------------------------------------------------------

---Get theme color
---@param key string Color key like "accent.primary"
---@return string|nil Hex color
function App:GetColor(key)
    return C3.Colors and C3.Colors.GetColor(key) or nil
end

---Check if an action was triggered
---@param key string
---@param action string
---@param modifiers? table
---@return boolean
function App:IsAction(key, action, modifiers)
    return C3.TUI.Input.IsAction(key, action, modifiers)
end

---Get Layout helper
---@return table Layout class
function App:Layout()
    return C3.Layout
end

---Get Constraint helper
---@return table Constraint class
function App:Constraint()
    return C3.Constraint
end

---Get Rect helper
---@return table Rect class
function App:Rect()
    return C3.Rect
end

-------------------------------------------------------------------------------
-- Event-Driven Mode (Alternative to Ticker)
-------------------------------------------------------------------------------

---Use event-driven rendering (no automatic ticker)
---Call app:Render() manually when state changes
function App:UseEventDriven()
    self.tickInterval = 0  -- Disable ticker
end

---Force immediate render (bypasses dirty check)
function App:ForceRender()
    self._dirty = true
    self:Render()
end

return App
