--[[
    Commander 3.0 - Theme System

    Minimal dark theme definitions for the terminal UI.
]]

local ADDON_NAME, C3 = ...

-------------------------------------------------------------------------------
-- Theme Module
-------------------------------------------------------------------------------

local Theme = {}
C3.UI = C3.UI or {}
C3.UI.Theme = Theme

-- Font path for Commander3 (Cascadia Mono Nerd Font)
local FONT_PATH = "Interface\\AddOns\\Commander3\\fonts\\CaskaydiaMonoNerdFontMono-Regular.ttf"

-------------------------------------------------------------------------------
-- Minimal Dark Theme
-------------------------------------------------------------------------------

-- Nord-inspired color palette
-- Polar Night: #2E3440, #3B4252, #434C5E, #4C566A (backgrounds)
-- Snow Storm: #D8DEE9, #E5E9F0, #ECEFF4 (text)
-- Frost: #8FBCBB, #88C0D0, #81A1C1, #5E81AC (accents)
-- Aurora: #BF616A, #D08770, #EBCB8B, #A3BE8C, #B48EAD (semantic)

Theme.colors = {
    -- Window chrome (#1E1E1E base, semi-transparent)
    window = {
        bg = { 0.12, 0.12, 0.12, 0.85 },       -- #1E1E1E at 85%
        border = { 0.30, 0.32, 0.36, 0.85 },   -- subtle light border
    },

    -- External tab bar (above window when multiple tabs)
    tabBar = {
        bg = { 0.06, 0.06, 0.07, 0.82 },       -- deeper, flatter bar           
        border = { 0.22, 0.23, 0.25, 0.70 },   -- subtle edge
    },

    -- Content area (where terminal output appears)
    content = {
        bg = { 0.10, 0.10, 0.10, 0.87 },       -- #1A1A1A, near window bg
        fg = "D8DEE9",                          -- nord4 (primary text)
    },

    -- Input line
    input = {
        bg = { 0.15, 0.15, 0.15, 0.83 },       -- #262626, slightly lighter
        fg = "ECEFF4",                          -- nord6 (bright text)
        prompt = "8FBCBB",                      -- nord7 (cyan/teal)
        cursor = "ECEFF4",                      -- nord6
        border = { 0.30, 0.32, 0.36, 0.80 },   -- match window border
    },

    -- Tab strip
    tabs = {
        bg = { 0.08, 0.08, 0.09, 0.40 },       -- minimal surface
        inactive = { 0.08, 0.09, 0.10, 0.25 }, -- low-contrast inactive
        active = { 0.14, 0.15, 0.16, 0.45 },   -- subtle lift for active
        textInactive = "9AA3AD",                -- soft gray
        textActive = "F5F7FA",                  -- near-white
        hover = { 0.11, 0.12, 0.13, 0.35 },    -- gentle hover
        indicator = "88C0D0",                   -- accent underline
    },

    -- Window control buttons [e][□][x]
    button = {
        normal = { 0.14, 0.14, 0.14, 0.75 },   -- match inactive tab
        hover = { 0.22, 0.22, 0.22, 0.90 },    -- #383838
        textNormal = { 0.56, 0.74, 0.73, 1 },  -- nord7 RGB
        textHover = { 1, 1, 1, 1 },
    },

    -- Accent colors (Aurora palette)
    accent = {
        primary = "88C0D0",                     -- nord8 (light blue)
        success = "A3BE8C",                     -- nord14 (green)
        warning = "EBCB8B",                     -- nord13 (yellow)
        error = "BF616A",                       -- nord11 (red)
        info = "81A1C1",                        -- nord9 (blue)
        special = "B48EAD",                     -- nord15 (purple)
    },

    -- Syntax highlighting for terminal output
    syntax = {
        command = "A3BE8C",                     -- nord14 (green)
        string = "D08770",                      -- nord12 (orange)
        number = "88C0D0",                      -- nord8 (light blue)
        keyword = "B48EAD",                     -- nord15 (purple)
        comment = "4C566A",                     -- nord3 (gray)
        error = "BF616A",                       -- nord11 (red)
        link = "88C0D0",                        -- nord8 (light blue)
    },

    -- Semantic status colors for datasources/commands
    status = {
        enabled = "A3BE8C",                     -- nord14 (green)
        disabled = "BF616A",                    -- nord11 (red)
        pending = "EBCB8B",                     -- nord13 (yellow)
        dim = "4C566A",                         -- nord3 (gray)
        highlight = "88C0D0",                   -- nord8 (light blue)
        gold = "EBCB8B",                        -- nord13 (gold/currency)
        label = "81A1C1",                       -- nord9 (blue labels)
    },

    -- Item quality colors (WoW standard - unchanged)
    quality = {
        [0] = "9D9D9D",  -- Poor (gray)
        [1] = "FFFFFF",  -- Common (white)
        [2] = "1EFF00",  -- Uncommon (green)
        [3] = "0070FF",  -- Rare (blue)
        [4] = "A335EE",  -- Epic (purple)
        [5] = "FF8000",  -- Legendary (orange)
        [6] = "E6CC80",  -- Artifact (light gold)
        [7] = "00CCFF",  -- Heirloom (light blue)
    },
}

-------------------------------------------------------------------------------
-- Dimensions
-------------------------------------------------------------------------------

Theme.dimensions = {
    -- Window
    windowMinWidth = 500,
    windowMinHeight = 300,
    windowDefaultWidth = 700,
    windowDefaultHeight = 450,

    -- Border
    borderSize = 1,

    -- Padding
    padding = 8,
    paddingSmall = 4,

    -- Input
    inputHeight = 24,

    -- Tab strip
    tabHeight = 24,
    tabMinWidth = 80,
    tabMaxWidth = 150,
    tabPadding = 12,

    -- Font sizes
    fontSizeNormal = 12,
    fontSizeSmall = 10,
    fontSizeLarge = 14,

    -- Terminal
    lineHeight = 14,
}

-------------------------------------------------------------------------------
-- Fonts
-------------------------------------------------------------------------------

-- Font path (set after init)
Theme.Font = FONT_PATH
Theme.FontSize = 12

-------------------------------------------------------------------------------
-- Tree Display Settings
-------------------------------------------------------------------------------

-- Enable box-drawing characters for tree views (├── └── │)
-- Requires a font with Unicode box-drawing support (JetBrains Mono, Fira Code)
-- Set to false to use simple indentation instead
Theme.useTreeCharacters = true

-------------------------------------------------------------------------------
-- Font Zoom System
-------------------------------------------------------------------------------

-- Zoom state (persisted to VFS)
Theme.FontScale = 1.0          -- Current zoom multiplier
Theme.DefaultFontScale = 1.0   -- Default zoom (Ctrl+0 resets to this)
Theme.MinFontScale = 0.5       -- Minimum (6pt effective)
Theme.MaxFontScale = 2.5       -- Maximum (30pt effective)
Theme.FontScaleStep = 0.1      -- Each step is 10%

-------------------------------------------------------------------------------
-- Line Spacing (Development Setting)
-------------------------------------------------------------------------------

-- Line spacing added to font size to get line height
-- Default is 3 pixels. Adjust this during development to dial in the look.
Theme.LineSpacing = 3          -- Pixels added to font size for line height
Theme.MinLineSpacing = 0       -- Minimum (tight lines)
Theme.MaxLineSpacing = 10      -- Maximum (loose lines)

-- Zoom change callbacks
Theme._zoomCallbacks = {}

---Get effective font size after zoom
---@return number
function Theme:GetEffectiveFontSize()
    return math.floor(self.FontSize * (self.FontScale or 1.0))
end

---Get effective line height
---@return number
function Theme:GetEffectiveLineHeight()
    return self:GetEffectiveFontSize() + (self.LineSpacing or 3)
end

---Get current line spacing
---@return number
function Theme:GetLineSpacing()
    return self.LineSpacing or 3
end

---Set line spacing (pixels added to font size)
---@param spacing number
---@return boolean changed Whether the spacing changed
function Theme:SetLineSpacing(spacing)
    spacing = math.max(self.MinLineSpacing, math.min(self.MaxLineSpacing, spacing))
    if spacing ~= self.LineSpacing then
        self.LineSpacing = spacing
        self:_notifyZoomChanged()  -- Reuse zoom callbacks since line height affects layout
        return true
    end
    return false
end

---Get effective input height (for EditBox)
---@return number
function Theme:GetEffectiveInputHeight()
    return self:GetEffectiveFontSize() + 3 + 8
end

---Get effective bottom reserve (space for input area)
---@return number
function Theme:GetEffectiveBottomReserve()
    return self:GetEffectiveInputHeight() + 6
end

---Zoom in (increase font scale)
---@return boolean changed Whether the scale changed
function Theme:ZoomIn()
    local newScale = math.min(self.MaxFontScale, (self.FontScale or 1.0) + self.FontScaleStep)
    -- Round to avoid floating point errors
    newScale = math.floor(newScale * 10 + 0.5) / 10
    if newScale ~= self.FontScale then
        self.FontScale = newScale
        self:_notifyZoomChanged()
        return true
    end
    return false
end

---Zoom out (decrease font scale)
---@return boolean changed Whether the scale changed
function Theme:ZoomOut()
    local newScale = math.max(self.MinFontScale, (self.FontScale or 1.0) - self.FontScaleStep)
    -- Round to avoid floating point errors
    newScale = math.floor(newScale * 10 + 0.5) / 10
    if newScale ~= self.FontScale then
        self.FontScale = newScale
        self:_notifyZoomChanged()
        return true
    end
    return false
end

---Reset zoom to default (configurable via SetDefaultZoom)
---@return boolean changed Whether the scale changed
function Theme:ZoomReset()
    local defaultScale = self.DefaultFontScale or 1.0
    if self.FontScale ~= defaultScale then
        self.FontScale = defaultScale
        self:_notifyZoomChanged()
        return true
    end
    return false
end

---Set the default zoom level (what Ctrl+0 resets to)
---@param scale number Scale value (e.g., 1.0 for 100%, 1.2 for 120%)
function Theme:SetDefaultZoom(scale)
    -- Clamp to valid range
    scale = math.max(self.MinFontScale, math.min(self.MaxFontScale, scale))
    -- Round to avoid floating point errors
    scale = math.floor(scale * 10 + 0.5) / 10
    self.DefaultFontScale = scale
    self:SaveZoomSettings()
end

---Get the default zoom percentage
---@return number percentage (e.g., 100 for 1.0 scale)
function Theme:GetDefaultZoomPercent()
    return math.floor((self.DefaultFontScale or 1.0) * 100)
end

---Get current zoom percentage
---@return number percentage (e.g., 100 for 1.0 scale)
function Theme:GetZoomPercent()
    return math.floor((self.FontScale or 1.0) * 100)
end

---Register callback for zoom changes
---@param callback function(scale, effectiveSize)
function Theme:OnZoomChanged(callback)
    table.insert(self._zoomCallbacks, callback)
end

---Notify all registered callbacks of zoom change
function Theme:_notifyZoomChanged()
    local scale = self.FontScale
    local size = self:GetEffectiveFontSize()
    for _, callback in ipairs(self._zoomCallbacks) do
        pcall(callback, scale, size)
    end
    -- Save settings after zoom change
    self:SaveZoomSettings()
end

---Save zoom settings to VFS
function Theme:SaveZoomSettings()
    local vfs = C3:GetVFS()
    if not vfs then return end

    -- Ensure config directory exists
    vfs:MakeDirP("/sys/config")

    local content = string.format(
        "return { fontScale = %.1f, defaultFontScale = %.1f, lineSpacing = %d }",
        self.FontScale or 1.0,
        self.DefaultFontScale or 1.0,
        self.LineSpacing or 3
    )
    vfs:Write("/sys/config/theme.lua", content)
end

---Load zoom settings from VFS
function Theme:LoadZoomSettings()
    local vfs = C3:GetVFS()
    if not vfs then return end

    local content = vfs:Read("/sys/config/theme.lua")
    if content and content ~= "" then
        local fn = loadstring(content)
        if fn then
            setfenv(fn, {})
            local ok, settings = pcall(fn)
            if ok and settings then
                if settings.fontScale then
                    self.FontScale = settings.fontScale
                    -- Clamp to valid range
                    self.FontScale = math.max(self.MinFontScale,
                                     math.min(self.MaxFontScale, self.FontScale))
                end
                if settings.defaultFontScale then
                    self.DefaultFontScale = settings.defaultFontScale
                    -- Clamp to valid range
                    self.DefaultFontScale = math.max(self.MinFontScale,
                                            math.min(self.MaxFontScale, self.DefaultFontScale))
                end
                if settings.lineSpacing then
                    self.LineSpacing = settings.lineSpacing
                    -- Clamp to valid range
                    self.LineSpacing = math.max(self.MinLineSpacing,
                                       math.min(self.MaxLineSpacing, self.LineSpacing))
                end
            end
        end
    end
end

Theme.fonts = {
    -- Monospace font for terminal (in order of preference)
    mono = {
        FONT_PATH,
        "Interface\\AddOns\\Commander3\\fonts\\CaskaydiaMonoNerdFontMono-Regular.ttf",
        "Interface\\AddOns\\Commander3\\fonts\\JetBrainsMono-Regular.ttf",
        "Fonts\\ARIALN.TTF",
        "Fonts\\FRIZQT__.TTF",
    },

    -- UI font for labels, tabs
    ui = "Fonts\\FRIZQT__.TTF",
}

---Initialize the font (call this after addon loads)
function Theme:InitFont()
    -- Just use the font directly like Commander does (no validation)
    -- SetFont validation seems unreliable, but actual usage works
    self.Font = FONT_PATH

    -- Load persisted zoom settings
    self:LoadZoomSettings()

    return self.Font
end

-------------------------------------------------------------------------------
-- Helper Functions
-------------------------------------------------------------------------------

---Apply background color to a texture
---@param texture texture
---@param colorKey string Key in Theme.colors (e.g., "window.bg")
function Theme:ApplyBackground(texture, colorKey)
    local parts = { strsplit(".", colorKey) }
    local color = self.colors

    for _, part in ipairs(parts) do
        color = color[part]
        if not color then return end
    end

    if type(color) == "table" and #color >= 3 then
        texture:SetColorTexture(color[1], color[2], color[3], color[4] or 1)
    end
end

---Get a color value
---@param colorKey string Key path (e.g., "accent.primary")
---@return any color Color value (table for RGBA, string for hex)
function Theme:GetColor(colorKey)
    local parts = { strsplit(".", colorKey) }
    local color = self.colors

    for _, part in ipairs(parts) do
        color = color[part]
        if not color then return nil end
    end

    return color
end

---Get RGB values from hex color
---@param hex string Hex color (e.g., "FF0000")
---@return number r, number g, number b
function Theme:HexToRGB(hex)
    if not hex or #hex < 6 then return 1, 1, 1 end

    local r = tonumber(hex:sub(1, 2), 16) / 255
    local g = tonumber(hex:sub(3, 4), 16) / 255
    local b = tonumber(hex:sub(5, 6), 16) / 255

    return r, g, b
end

---Get WoW color code from hex
---@param hex string Hex color
---@return string WoW color code (e.g., "|cFFFF0000")
function Theme:ColorCode(hex)
    return string.format("|cFF%s", hex)
end

---Wrap text in color code
---@param text string
---@param hex string Hex color
---@return string Colored text
function Theme:Colorize(text, hex)
    return string.format("|cFF%s%s|r", hex, text)
end

---Apply window styling to a frame
---@param frame frame
function Theme:ApplyWindowStyle(frame)
    -- Create background if needed
    if not frame._themeBg then
        frame._themeBg = frame:CreateTexture(nil, "BACKGROUND")
        frame._themeBg:SetAllPoints()
    end
    self:ApplyBackground(frame._themeBg, "window.bg")

    -- Create border frame if needed
    if not frame._themeBorder then
        frame._themeBorder = CreateFrame("Frame", nil, frame, "BackdropTemplate")
        frame._themeBorder:SetPoint("TOPLEFT", -1, 1)
        frame._themeBorder:SetPoint("BOTTOMRIGHT", 1, -1)
        frame._themeBorder:SetBackdrop({
            edgeFile = "Interface\\Buttons\\WHITE8x8",
            edgeSize = self.dimensions.borderSize,
        })
    end

    local borderColor = self.colors.window.border
    frame._themeBorder:SetBackdropBorderColor(borderColor[1], borderColor[2], borderColor[3], borderColor[4])
end

---Apply input styling to an EditBox
---@param editBox editbox
function Theme:ApplyInputStyle(editBox)
    -- Background
    if not editBox._themeBg then
        editBox._themeBg = editBox:CreateTexture(nil, "BACKGROUND")
        editBox._themeBg:SetAllPoints()
    end
    self:ApplyBackground(editBox._themeBg, "input.bg")

    -- Text color
    local r, g, b = self:HexToRGB(self.colors.input.fg)
    editBox:SetTextColor(r, g, b)

    -- Font
    editBox:SetFont(self.fonts.mono, self.dimensions.fontSizeNormal, "")
end

return Theme
