--[[
    Commander 3.0 - Tab Class

    First-class Tab object that encapsulates:
    - Session reference (with proper lifecycle management)
    - VirtualBuffer (output history)
    - UI button (visual representation)

    Tabs can be transferred between windows cleanly via Detach()/AttachTo().
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Tab Class
-------------------------------------------------------------------------------

local Tab = C3.class()
C3.UI = C3.UI or {}
C3.UI.Tab = Tab

-- Constants
local TAB_HEIGHT = 20

local function resolveTabColors()
    local Theme = C3.UI.Theme
    local tabs = Theme and Theme.colors and Theme.colors.tabs

    local function rgba(color, fallback)
        if type(color) == "table" and #color >= 3 then
            return color
        end
        return fallback
    end

    local function rgbFromHex(hex, fallback)
        if Theme and Theme.HexToRGB and type(hex) == "string" then
            local r, g, b = Theme:HexToRGB(hex)
            return { r, g, b }
        end
        return fallback
    end

    return {
        inactiveBg = rgba(tabs and tabs.inactive, { 0.08, 0.09, 0.10, 0.25 }),
        activeBg = rgba(tabs and tabs.active, { 0.14, 0.15, 0.16, 0.45 }),
        hoverBg = rgba(tabs and tabs.hover, { 0.11, 0.12, 0.13, 0.35 }),
        textInactive = rgbFromHex(tabs and tabs.textInactive, { 0.6, 0.65, 0.7 }),
        textActive = rgbFromHex(tabs and tabs.textActive, { 0.96, 0.97, 0.98 }),
        indicator = rgbFromHex(tabs and tabs.indicator, { 0.53, 0.75, 0.82 }),
    }
end

-------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------

---Create a new Tab
---@param options table { session?, buffer?, name? }
function Tab:init(options)
    options = options or {}

    -- Generate unique ID
    self.id = self:_generateId()

    -- Session management (with reference counting)
    if options.session then
        self.session = options.session
        C3.SessionManager:AddRef(self.session.id)
    else
        self.session = C3.SessionManager:Create({
            name = options.name or "Tab"
        })
        -- New session starts with refcount 1, no need to AddRef
    end

    -- Buffer for output history
    self.buffer = options.buffer or C3.VirtualBuffer:new({ maxLines = 5000 })

    -- UI state
    self.button = nil       -- Created when attached to a TabStrip
    self.tabStrip = nil     -- Parent TabStrip reference
    self.isActive = false

    -- Dynamic title system
    self._number = 1                    -- Position number (1, 2, 3...)
    self._label = nil                   -- Dynamic label (command or cwd), nil = blank
    self.name = self:_formatTitle()     -- "N." or "N. label" for display
end

local MAX_LABEL_LENGTH = 24  -- Truncate labels longer than this

---Format the full tab title as "N." (blank) or "N. label"
---@return string
function Tab:_formatTitle()
    if self._label and self._label ~= "" then
        return string.format("%d. %s", self._number, self._label)
    else
        return string.format("%d.", self._number)
    end
end

---Truncate a label if too long
---@param label string
---@return string
local function truncateLabel(label)
    if not label then return "" end
    if #label <= MAX_LABEL_LENGTH then
        return label
    end
    return label:sub(1, MAX_LABEL_LENGTH - 1) .. "…"
end

function Tab:_generateId()
    return string.format("tab_%x_%x", GetTime() * 1000, math.random(0xFFFF))
end

-------------------------------------------------------------------------------
-- Lifecycle Management
-------------------------------------------------------------------------------

---Attach this tab to a TabStrip (creates UI button)
---@param tabStrip table TabStrip instance
---@param position number? Insert position (nil = append)
function Tab:AttachTo(tabStrip, position)
    if self.tabStrip then
        error("Tab is already attached to a TabStrip. Call Detach() first.")
    end

    self.tabStrip = tabStrip
    self.button = self:_createButton(tabStrip)

    -- TabStrip will handle array insertion and layout
end

---Detach this tab from its current TabStrip (removes UI button)
---@return boolean success
function Tab:Detach()
    if not self.tabStrip then
        return false
    end

    -- Hide and clean up button
    if self.button then
        self:SetDragging(false)
        self.button:Hide()
        self.button:SetParent(nil)
        self.button = nil
    end

    self.tabStrip = nil
    self.isActive = false

    return true
end

---Destroy this tab completely (releases session reference)
function Tab:Destroy()
    -- Detach from TabStrip if attached
    self:Detach()

    -- Release session reference
    if self.session then
        C3.SessionManager:Release(self.session.id)
        self.session = nil
    end

    -- Clear buffer
    if self.buffer then
        self.buffer:Clear()
        self.buffer = nil
    end
end

-------------------------------------------------------------------------------
-- UI Button Creation
-------------------------------------------------------------------------------

function Tab:_createButton(tabStrip)
    local button = CreateFrame("Button", nil, tabStrip.tabContainer)
    button:SetHeight(TAB_HEIGHT)
    button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    button:RegisterForDrag("LeftButton")
    local highlight = button:CreateTexture(nil, "HIGHLIGHT")
    highlight:SetAllPoints()
    highlight:SetColorTexture(1, 1, 1, 0)
    button:SetHighlightTexture(highlight)

    -- Background (inactive state - blue tinted)
    button.bg = button:CreateTexture(nil, "BACKGROUND")
    button.bg:SetAllPoints()
    button._tabColors = resolveTabColors()
    local inactiveBg = button._tabColors.inactiveBg
    button.bg:SetColorTexture(inactiveBg[1], inactiveBg[2], inactiveBg[3], inactiveBg[4] or 1)

    -- Text (fixed size, does not scale with zoom)
    local Theme = C3.UI.Theme
    local font = Theme and Theme.Font or "Fonts\\FRIZQT__.TTF"
    local TAB_FONT_SIZE = 10  -- Fixed size for tabs
    button.text = button:CreateFontString(nil, "OVERLAY")
    button.text:SetFont(font, TAB_FONT_SIZE, "")
    button.text:SetPoint("LEFT", 6, 0)
    button.text:SetPoint("RIGHT", -16, 0)
    button.text:SetJustifyH("LEFT")
    button.text:SetWordWrap(false)
    button.text:SetNonSpaceWrap(false)
    button.text:SetText(self:_formatTitle())
    local textInactive = button._tabColors.textInactive
    button.text:SetTextColor(textInactive[1], textInactive[2], textInactive[3])

    -- Active indicator (subtle underline)
    button.indicator = button:CreateTexture(nil, "ARTWORK")
    button.indicator:SetHeight(1)
    button.indicator:SetPoint("BOTTOMLEFT", 6, 1)
    button.indicator:SetPoint("BOTTOMRIGHT", -6, 1)
    local indicator = button._tabColors.indicator
    button.indicator:SetColorTexture(indicator[1], indicator[2], indicator[3], indicator[4] or 1)
    button.indicator:SetAlpha(0)

    -- Close button (plain text X)
    button.closeBtn = CreateFrame("Button", nil, button)
    button.closeBtn:SetPoint("RIGHT", -2, 0)
    button.closeBtn:SetSize(14, 14)

    local closeText = button.closeBtn:CreateFontString(nil, "ARTWORK")
    closeText:SetFont(font, TAB_FONT_SIZE, "")
    closeText:SetPoint("CENTER")
    closeText:SetText("")
    closeText:SetTextColor(0.5, 0.5, 0.5)
    button.closeText = closeText

    -- Wire up events (delegate to TabStrip)
    local tab = self
    button.closeBtn:SetScript("OnClick", function()
        tabStrip:CloseTab(tab)
    end)

    button.closeBtn:SetScript("OnEnter", function()
        closeText:SetTextColor(1, 0.3, 0.3)  -- Red on hover
    end)

    button.closeBtn:SetScript("OnLeave", function()
        closeText:SetTextColor(0.5, 0.5, 0.5)
    end)

    button:SetScript("OnClick", function(_, mouseButton)
        if mouseButton == "LeftButton" then
            tabStrip:SelectTab(tab)
        elseif mouseButton == "RightButton" then
            tabStrip:ShowTabMenu(tab)
        end
        -- Focus input after any tab click
        if tabStrip.window and tabStrip.window.input then
            tabStrip.window.input:Focus()
        end
    end)

    button:SetScript("OnDragStart", function()
        tabStrip:StartTabDrag(tab)
    end)

    button:SetScript("OnDragStop", function()
        tabStrip:EndTabDrag()
    end)

    button:SetScript("OnEnter", function()
        if not tab.isActive then
            local hoverBg = button._tabColors.hoverBg
            button.bg:SetColorTexture(hoverBg[1], hoverBg[2], hoverBg[3], hoverBg[4] or 1)
            button.text:SetTextColor(0.7, 0.75, 0.8)
            if button.indicator then
                button.indicator:SetAlpha(0.35)
            end
        end
    end)

    button:SetScript("OnLeave", function()
        if not tab.isActive then
            local tabColors = button._tabColors
            local inactiveBg = tabColors.inactiveBg
            local textInactive = tabColors.textInactive
            button.bg:SetColorTexture(inactiveBg[1], inactiveBg[2], inactiveBg[3], inactiveBg[4] or 1)
            button.text:SetTextColor(textInactive[1], textInactive[2], textInactive[3])
            if button.indicator then
                button.indicator:SetAlpha(0)
            end
        end
    end)

    return button
end

-------------------------------------------------------------------------------
-- State Management
-------------------------------------------------------------------------------

---Set this tab as active (updates visual state)
---@param active boolean
function Tab:SetActive(active)
    self.isActive = active

    if self.button then
        if active then
            -- Active: lighter blue background, white text
            local tabColors = self.button._tabColors
            local activeBg = tabColors.activeBg
            local textActive = tabColors.textActive
            self.button.bg:SetColorTexture(activeBg[1], activeBg[2], activeBg[3], activeBg[4] or 1)
            self.button.text:SetTextColor(textActive[1], textActive[2], textActive[3])
            if self.button.indicator then
                self.button.indicator:SetAlpha(1)
            end
        else
            -- Inactive: darker blue background, blue-gray text
            local tabColors = self.button._tabColors
            local inactiveBg = tabColors.inactiveBg
            local textInactive = tabColors.textInactive
            self.button.bg:SetColorTexture(inactiveBg[1], inactiveBg[2], inactiveBg[3], inactiveBg[4] or 1)
            self.button.text:SetTextColor(textInactive[1], textInactive[2], textInactive[3])
            if self.button.indicator then
                self.button.indicator:SetAlpha(0)
            end
        end
    end
end

---Update drag visuals for this tab
---@param dragging boolean
function Tab:SetDragging(dragging)
    self._dragging = dragging and true or false
    if self.button then
        self.button:SetAlpha(self._dragging and 0.4 or 1)
    end
end

---@return boolean
function Tab:IsDragging()
    return self._dragging == true
end

---Set the dynamic label (from command or cwd)
---@param label string
function Tab:SetLabel(label)
    self._label = truncateLabel(label)
    self:_refreshTitle()
end

---Set the position number
---@param number number
function Tab:SetNumber(number)
    self._number = number
    self:_refreshTitle()
end

---Get the current position number
---@return number
function Tab:GetNumber()
    return self._number
end

---Refresh the displayed title
function Tab:_refreshTitle()
    self.name = self:_formatTitle()
    if self.button and self.button.text then
        self.button.text:SetText(self.name)
    end
end

-------------------------------------------------------------------------------
-- Buffer Wiring
-------------------------------------------------------------------------------

---Wire buffer callbacks to an OutputDisplay
---@param outputDisplay table OutputDisplay instance
function Tab:WireBufferTo(outputDisplay)
    if not self.buffer then return end

    -- Set buffer reference
    outputDisplay.buffer = self.buffer

    -- Update renderer
    if outputDisplay.renderer then
        outputDisplay.renderer:SetBuffer(self.buffer)
    end

    -- Wire callbacks
    self.buffer.onLineAdded = function(line)
        outputDisplay:_onLineAdded(line)
    end

    self.buffer.onBufferChanged = function()
        outputDisplay:Refresh()
    end

    -- Initial render
    outputDisplay:Refresh()
end

---Unwire buffer callbacks
function Tab:UnwireBuffer()
    if not self.buffer then return end

    self.buffer.onLineAdded = nil
    self.buffer.onBufferChanged = nil
end

-------------------------------------------------------------------------------
-- Serialization (for persistence)
-------------------------------------------------------------------------------

---Serialize tab state for saving
---@return table
function Tab:Serialize()
    return {
        id = self.id,
        sessionId = self.session and self.session.id,
        label = self._label,
    }
end

---Create tab from serialized state
---@param data table
---@return Tab?
function Tab.Deserialize(data)
    if not data then return nil end

    -- Find existing session
    local session = C3.SessionManager:Get(data.sessionId)
    if not session then
        -- Session was lost, create new one
        session = C3.SessionManager:Create({ name = "Terminal" })
    end

    local tab = Tab:new({ session = session })
    -- Restore the label if saved
    if data.label then
        tab._label = data.label
        tab.name = tab:_formatTitle()
    end
    return tab
end

-------------------------------------------------------------------------------
-- Font Size Updates
-------------------------------------------------------------------------------

---Tabs use fixed font size and do not scale with zoom
function Tab:UpdateFontSize()
    -- No-op: tabs intentionally don't scale with text zoom
end

return Tab

