-- ===================== Main frame =====================
local optionsFrame = CreateFrame("Frame", "BPSOptionsFrame", UIParent, "BackdropTemplate")
optionsFrame:SetSize(580, 780)
optionsFrame:SetPoint("CENTER")
optionsFrame:SetFrameStrata("DIALOG")
optionsFrame:SetBackdrop({
    bgFile   = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Buttons\\WHITE8x8",
    edgeSize = 1,
})
optionsFrame:SetBackdropColor(0.06, 0.06, 0.07, 0.97)
optionsFrame:SetBackdropBorderColor(0, 0, 0, 1)
optionsFrame:SetMovable(true)
optionsFrame:EnableMouse(true)
optionsFrame:RegisterForDrag("LeftButton")
optionsFrame:SetScript("OnDragStart", function(s) s:StartMoving() end)
optionsFrame:SetScript("OnDragStop",  function(s) s:StopMovingOrSizing() end)
optionsFrame:SetClampedToScreen(true)
optionsFrame:Hide()

if UISpecialFrames then table.insert(UISpecialFrames, "BPSOptionsFrame") end

-- Accent header strip
local headerBar = CreateFrame("Frame", nil, optionsFrame, "BackdropTemplate")
headerBar:SetPoint("TOPLEFT", 1, -1)
headerBar:SetPoint("TOPRIGHT", -1, -1)
headerBar:SetHeight(78)
headerBar:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" })
headerBar:SetBackdropColor(0.10, 0.10, 0.12, 1)

local accentLine = headerBar:CreateTexture(nil, "ARTWORK")
accentLine:SetColorTexture(0.16, 0.55, 0.95, 1)
accentLine:SetPoint("BOTTOMLEFT", 0, 0)
accentLine:SetPoint("BOTTOMRIGHT", 0, 0)
accentLine:SetHeight(2)

local closeBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", -4, -4)

local mainTitle = headerBar:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
mainTitle:SetPoint("TOPLEFT", 18, -14)
mainTitle:SetText("Battle Pet Statistics")
mainTitle:SetTextColor(1, 1, 1)

-- ===================== Tab system (top, button-style) =====================
local TAB_OPTIONS   = 1
local TAB_STATS     = 2
local TAB_INTERFACE = 3
local currentTab    = TAB_OPTIONS

local tabBtns   = {}
local tabPanels = {}
local TAB_LABELS_FALLBACK = { "Options", "Statistics", "Interface" }

local function StyleTabButton(btn, isActive)
    if isActive then
        btn:SetBackdropColor(0.16, 0.55, 0.95, 1)
        btn.label:SetTextColor(1, 1, 1)
    else
        btn:SetBackdropColor(0.16, 0.16, 0.18, 1)
        btn.label:SetTextColor(0.7, 0.7, 0.72)
    end
end

for i = 1, 3 do
    local btn = CreateFrame("Button", "BPSTab" .. i, headerBar, "BackdropTemplate")
    btn:SetSize(140, 26)
    btn:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" })
    btn:SetID(i)

    if i == 1 then
        btn:SetPoint("BOTTOMLEFT", 18, 12)
    else
        btn:SetPoint("LEFT", tabBtns[i - 1], "RIGHT", 8, 0)
    end

    local label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label:SetPoint("CENTER")
    btn.label = label

    btn:SetScript("OnClick", function(self)
        currentTab = self:GetID()
        for j, p in ipairs(tabPanels) do
            p:SetShown(j == currentTab)
            StyleTabButton(tabBtns[j], j == currentTab)
        end
        -- Re-apply all text/locale-dependent values whenever the tab changes,
        -- so a language switch made earlier in the session is never left
        -- stale on a tab that wasn't re-opened since.
        if BPS_RefreshOptions then BPS_RefreshOptions() end
    end)
    btn:SetScript("OnEnter", function(self)
        if self:GetID() ~= currentTab then
            self:SetBackdropColor(0.22, 0.22, 0.25, 1)
        end
    end)
    btn:SetScript("OnLeave", function(self)
        StyleTabButton(self, self:GetID() == currentTab)
    end)

    label:SetText(TAB_LABELS_FALLBACK[i])
    StyleTabButton(btn, i == currentTab)
    tabBtns[i] = btn
end

local function MakePanel()
    local p = CreateFrame("Frame", nil, optionsFrame)
    p:SetPoint("TOPLEFT", 18, -90)
    p:SetPoint("BOTTOMRIGHT", -18, 16)
    p:Hide()
    return p
end

local optPanel  = MakePanel()
local statPanel = MakePanel()
local intPanel  = MakePanel()
tabPanels[TAB_OPTIONS]   = optPanel
tabPanels[TAB_STATS]     = statPanel
tabPanels[TAB_INTERFACE] = intPanel

-- ===================== OPTIONS TAB =====================

-- Language
local langLabel = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
langLabel:SetPoint("TOPLEFT", 0, 0)

local langDropdown = CreateFrame("Frame", "BPSLangDropdown", optPanel, "UIDropDownMenuTemplate")
langDropdown:SetPoint("TOPLEFT", langLabel, "BOTTOMLEFT", -16, -4)

local function SetLanguage(mode)
    EnsureDB()
    BattlePetStatisticsDB.language = mode
    BPS_RefreshOptions()
end

local function LangDropdown_Init(self, level)
    EnsureDB()
    local L = BPS_GetLocale()
    local opts = {
        { v="AUTO", t=L.LANG_AUTO },
        { v="EN",   t=L.LANG_EN },
        { v="FR",   t=L.LANG_FR },
        { v="DE",   t=L.LANG_DE },
        { v="IT",   t=L.LANG_IT },
        { v="PL",   t=L.LANG_PL },
        { v="SV",   t=L.LANG_SV },
        { v="CS",   t=L.LANG_CS },
        { v="RU",   t=L.LANG_RU },
        { v="ZH",   t=L.LANG_ZH },
        { v="PT",   t=L.LANG_PT },
        { v="ES",   t=L.LANG_ES },
    }
    for _, o in ipairs(opts) do
        local info = UIDropDownMenu_CreateInfo()
        info.text    = o.t
        info.value   = o.v
        info.func    = function(self) SetLanguage(self.value) end
        info.checked = (BattlePetStatisticsDB.language == o.v)
        UIDropDownMenu_AddButton(info, level)
    end
end
UIDropDownMenu_Initialize(langDropdown, LangDropdown_Init)
UIDropDownMenu_SetWidth(langDropdown, 150)

-- Goal section: completed achievements list (green, read-only) + dropdown for
-- picking the current goal among the NOT-YET-completed ones only.
-- Anchored directly below the language dropdown.
local goalTitle = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
goalTitle:SetPoint("TOPLEFT", langDropdown, "BOTTOMLEFT", 16, -18)

local goalCompletedLabel = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
goalCompletedLabel:SetPoint("TOPLEFT", goalTitle, "BOTTOMLEFT", 4, -8)

-- Up to #BPS_GOALS lines to list completed achievements in green; hidden when unused.
local MAX_GOALS = #BPS_GOALS
local completedLines = {}
for i = 1, MAX_GOALS do
    local fs = optPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    if i == 1 then
        fs:SetPoint("TOPLEFT", goalCompletedLabel, "BOTTOMLEFT", 4, -4)
    else
        fs:SetPoint("TOPLEFT", completedLines[i - 1], "BOTTOMLEFT", 0, -2)
    end
    fs:SetTextColor(0.2, 1.0, 0.2)
    fs:Hide()
    completedLines[i] = fs
end

local goalNoneCompletedText = optPanel:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
goalNoneCompletedText:SetPoint("TOPLEFT", goalCompletedLabel, "BOTTOMLEFT", 4, -4)
goalNoneCompletedText:Hide()

-- goalCurrentLabel is anchored dynamically in BPS_RefreshOptions, right below
-- whichever completed-goal element is currently the last one shown, so the
-- completed list only takes up as much vertical space as it actually needs.
local goalCurrentLabel = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")

local goalDropdown = CreateFrame("Frame", "BPSGoalDropdown", optPanel, "UIDropDownMenuTemplate")
goalDropdown:SetPoint("TOPLEFT", goalCurrentLabel, "BOTTOMLEFT", -16, -4)

local function GoalDropdown_Init(self, level)
    EnsureDB()
    local L = BPS_GetLocale()

    -- Only the NOT-YET-completed goals are selectable here; completed ones are
    -- shown separately above in the green "completed" list.
    for _, goal in ipairs(BPS_GOALS) do
        if not BPS_IsGoalCompleted(goal.id) then
            local isCurrent = (BattlePetStatisticsDB.currentGoalId == goal.id)
            local progress  = BPS_GetRealProgress(goal)
            local progressLabel = math.min(progress, goal.threshold) .. "/" .. goal.threshold
            local info = UIDropDownMenu_CreateInfo()
            if isCurrent then
                info.text = "|cffffff00> " .. BPS_GetGoalName(goal) .. " (" .. progressLabel .. ")|r"
            else
                info.text = BPS_GetGoalName(goal) .. " (" .. progressLabel .. ")"
            end
            info.value    = goal.id
            info.checked  = isCurrent
            info.disabled = false
            info.func = function(self)
                BPS_SetCurrentGoal(self.value)
                BPS_RefreshOptions()
            end
            UIDropDownMenu_AddButton(info, level)
        end
    end
end
UIDropDownMenu_Initialize(goalDropdown, GoalDropdown_Init)
UIDropDownMenu_SetWidth(goalDropdown, 360)

-- ===================== INTERFACE TAB =====================
-- Everything about what gets displayed on the battle overlay during a pet
-- battle: which lines are shown, and the two buttons (Forfeit / the
-- Forfeit-vs-Random override).
local dispTitle = intPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
dispTitle:SetPoint("TOPLEFT", 0, 0)

local dispHint = intPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
dispHint:SetPoint("TOPLEFT", dispTitle, "BOTTOMLEFT", 4, -6)
dispHint:SetWidth(500)
dispHint:SetJustifyH("LEFT")

local DISP_KEYS   = {
    "showOverall", "showWinrate", "showGoal", "showVs",
    "showVsStats", "showVsWinrate", "showPrediction", "showForfeitButton",
    "showNRStats", "showForfeitVsRandomWhenWinning",
}
local DISP_LKEYS  = {
    "SHOW_OVERALL", "SHOW_WINRATE", "SHOW_GOAL", "SHOW_VS",
    "SHOW_VS_STATS", "SHOW_VS_WINRATE", "SHOW_PREDICTION", "SHOW_FORFEIT_BUTTON",
    "SHOW_NR_STATS", "SHOW_FORFEIT_VS_RANDOM_WHEN_WINNING",
}
local dispChecks  = {}

for i, key in ipairs(DISP_KEYS) do
    local col = (i - 1) % 2
    local row = math.floor((i - 1) / 2)
    local cb = CreateFrame("CheckButton", "BPSDispCB" .. i, intPanel, "UICheckButtonTemplate")
    cb:SetPoint("TOPLEFT", dispHint, "BOTTOMLEFT", -4 + col * 265, -10 - row * 24)
    cb.dbKey = key
    cb:SetScript("OnClick", function(self)
        EnsureDB()
        BattlePetStatisticsDB.display[self.dbKey] = self:GetChecked() and true or false
    end)
    if not cb.labelFS then
        local fs = intPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        fs:SetPoint("LEFT", cb, "RIGHT", 2, 0)
        fs:SetWidth(240)
        fs:SetJustifyH("LEFT")
        cb.labelFS = fs
    end
    dispChecks[i] = cb
end

-- Keybinding section
local kbTitle = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
kbTitle:SetPoint("TOPLEFT", goalDropdown, "BOTTOMLEFT", 16, -14)

local kbHint = optPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
kbHint:SetPoint("TOPLEFT", kbTitle, "BOTTOMLEFT", 4, -6)
kbHint:SetWidth(500)
kbHint:SetJustifyH("LEFT")

local kbOpenBtn = CreateFrame("Button", nil, optPanel, "UIPanelButtonTemplate")
kbOpenBtn:SetSize(200, 24)
kbOpenBtn:SetPoint("TOPLEFT", kbHint, "BOTTOMLEFT", 0, -6)
kbOpenBtn:SetScript("OnClick", function()
    local opened = false
    pcall(function()
        if Settings and Settings.OpenToCategory and Settings.KEYBINDINGS_CATEGORY_ID then
            Settings.OpenToCategory(Settings.KEYBINDINGS_CATEGORY_ID)
            opened = true
        end
    end)
    if not opened then
        pcall(function()
            if _G.KeyBindingFrame then ToggleFrame(KeyBindingFrame); opened = true end
        end)
    end
    if not opened then
        print("|cff33ff99BPS|r: Esc → Key Bindings → \"Battle Pet Statistics\"")
    end
end)

-- ===================== Trade Win Session section =====================
local twTitle = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
twTitle:SetPoint("TOPLEFT", kbOpenBtn, "BOTTOMLEFT", -16, -18)

local twIntro = optPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
twIntro:SetPoint("TOPLEFT", twTitle, "BOTTOMLEFT", 4, -6)
twIntro:SetWidth(500)
twIntro:SetJustifyH("LEFT")

-- Group 1: when to show the alias popup (4 mutually exclusive radio buttons)
local SHOW_WINDOW_MODES = { "startup", "after_first_battle", "never", "if_not_renamed" }
local twRadios = {}
for i, mode in ipairs(SHOW_WINDOW_MODES) do
    local radio = CreateFrame("CheckButton", "BPSShowWindowRadio" .. i, optPanel, "UIRadioButtonTemplate")
    if i == 1 then
        radio:SetPoint("TOPLEFT", twIntro, "BOTTOMLEFT", 0, -10)
    else
        radio:SetPoint("TOPLEFT", twRadios[i - 1], "BOTTOMLEFT", 0, -6)
    end
    radio.text = _G[radio:GetName() .. "Text"]
    radio.text:SetWidth(460)
    radio.text:SetJustifyH("LEFT")
    radio:SetScript("OnClick", function()
        BattlePetStatisticsDB.showWindowMode = mode
        for j, r in ipairs(twRadios) do r:SetChecked(j == i) end
    end)
    twRadios[i] = radio
end

-- Group 2: restore pet names to default on logout (2 mutually exclusive radios)
local twRestoreLabel = optPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
twRestoreLabel:SetPoint("TOPLEFT", twRadios[#twRadios], "BOTTOMLEFT", 0, -14)
twRestoreLabel:SetWidth(500)
twRestoreLabel:SetJustifyH("LEFT")

local twRestoreYes = CreateFrame("CheckButton", "BPSRestoreYesRadio", optPanel, "UIRadioButtonTemplate")
twRestoreYes:SetPoint("TOPLEFT", twRestoreLabel, "BOTTOMLEFT", 0, -8)
twRestoreYes.text = _G[twRestoreYes:GetName() .. "Text"]

local twRestoreNo = CreateFrame("CheckButton", "BPSRestoreNoRadio", optPanel, "UIRadioButtonTemplate")
twRestoreNo:SetPoint("LEFT", twRestoreYes.text, "RIGHT", 20, 0)
twRestoreNo.text = _G[twRestoreNo:GetName() .. "Text"]

twRestoreYes:SetScript("OnClick", function()
    BattlePetStatisticsDB.restoreNamesOnLogout = true
    twRestoreYes:SetChecked(true); twRestoreNo:SetChecked(false)
end)
twRestoreNo:SetScript("OnClick", function()
    BattlePetStatisticsDB.restoreNamesOnLogout = false
    twRestoreYes:SetChecked(false); twRestoreNo:SetChecked(true)
end)

-- ===================== Automation section =====================
local autoTitle = optPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
autoTitle:SetPoint("TOPLEFT", twRestoreYes, "BOTTOMLEFT", 0, -18)

-- ---- Auto-requeue ----
local autoRequeueCheck = CreateFrame("CheckButton", "BPSAutoRequeueCheck", optPanel, "UICheckButtonTemplate")
autoRequeueCheck:SetPoint("TOPLEFT", autoTitle, "BOTTOMLEFT", 0, -6)
autoRequeueCheck:SetSize(24, 24)
local autoRequeueLabel = optPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
autoRequeueLabel:SetPoint("LEFT", autoRequeueCheck, "RIGHT", 4, 0)
local autoRequeueHint = optPanel:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
autoRequeueHint:SetPoint("TOPLEFT", autoRequeueCheck, "BOTTOMLEFT", 26, -2)
autoRequeueHint:SetWidth(480)
autoRequeueHint:SetJustifyH("LEFT")

-- autoDelayRow must be declared (forward-declared local) BEFORE the OnClick
-- script below is defined. In Lua, a function body resolves free variables
-- lexically at the point the function is DEFINED, not when it later runs.
-- Referencing "autoDelayRow" here before any "local autoDelayRow" exists in
-- scope makes it resolve to a global (always nil), so the click handler's
-- "if autoDelayRow then ..." would silently never fire. Declaring the local
-- up front fixes that.
local autoDelayRow

autoRequeueCheck:SetScript("OnClick", function(self)
    EnsureDB()
    BattlePetStatisticsDB.autoRequeue = self:GetChecked() and true or false
    -- Show/hide the delay row depending on the checkbox state
    if autoDelayRow then autoDelayRow:SetShown(BattlePetStatisticsDB.autoRequeue) end
end)

-- ---- Delay row (visible only when auto-requeue is ON) ----
autoDelayRow = CreateFrame("Frame", nil, optPanel)
autoDelayRow:SetSize(500, 28)
autoDelayRow:SetPoint("TOPLEFT", autoRequeueHint, "BOTTOMLEFT", -26, -6)

local autoDelayLabel = autoDelayRow:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
autoDelayLabel:SetPoint("LEFT", 0, 0)

local autoDelayBox = CreateFrame("EditBox", "BPSAutoRequeueDelayBox", autoDelayRow, "InputBoxTemplate")
autoDelayBox:SetSize(50, 20)
autoDelayBox:SetPoint("LEFT", autoDelayLabel, "RIGHT", 8, 0)
autoDelayBox:SetNumeric(true)
autoDelayBox:SetMaxLetters(3)
autoDelayBox:SetAutoFocus(false)
autoDelayBox:SetScript("OnEnterPressed", function(self)
    self:ClearFocus()
    local v = tonumber(self:GetText()) or 3
    if v < 0 then v = 0 end
    if v > 120 then v = 120 end
    self:SetText(tostring(v))
    EnsureDB()
    BattlePetStatisticsDB.autoRequeueDelay = v
end)
autoDelayBox:SetScript("OnEscapePressed", function(self)
    self:ClearFocus()
    EnsureDB()
    self:SetText(tostring(BattlePetStatisticsDB.autoRequeueDelay or 3))
end)
autoDelayBox:SetScript("OnEditFocusLost", function(self)
    local v = tonumber(self:GetText()) or 3
    if v < 0 then v = 0 end
    if v > 120 then v = 120 end
    self:SetText(tostring(v))
    EnsureDB()
    BattlePetStatisticsDB.autoRequeueDelay = v
end)

local autoDelayHint = autoDelayRow:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
autoDelayHint:SetPoint("LEFT", autoDelayBox, "RIGHT", 8, 0)
autoDelayHint:SetWidth(340)
autoDelayHint:SetJustifyH("LEFT")

-- ===================== STATISTICS TAB =====================

-- Helper: create a FontString anchored below a reference
local function StatLine(parent, anchor, xOff, yOff, font)
    local fs = parent:CreateFontString(nil, "OVERLAY", font or "GameFontHighlight")
    fs:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", xOff or 0, yOff or -4)
    return fs
end

-- ----- Section header -----
local statsHeader = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
statsHeader:SetPoint("TOPLEFT", 0, 0)

-- Column X positions (left edge for each column)
local COL_LEFT   = 4     -- "Total" column
local COL_RIGHT  = 290   -- "Sans Random" column

-- ---- Global stats block ----
-- Col headers
local globalTotalLabel = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
globalTotalLabel:SetPoint("TOPLEFT", statsHeader, "BOTTOMLEFT", COL_LEFT, -8)
globalTotalLabel:SetTextColor(0.7, 0.85, 1.0)

local globalNRLabel = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
globalNRLabel:SetPoint("TOPLEFT", statsHeader, "BOTTOMLEFT", COL_RIGHT, -8)
globalNRLabel:SetTextColor(1.0, 0.85, 0.3)

-- Total column lines
local totalLine        = StatLine(statPanel, globalTotalLabel, 8, -4)
local totalWinLine     = StatLine(statPanel, totalLine, 8, -2)
local totalLoseLine    = StatLine(statPanel, totalWinLine, 0, -2)
local totalWinrateLine = StatLine(statPanel, totalLoseLine, 0, -2)

-- NR column lines (anchored to the same vertical positions as the Total column lines)
local nrTotalLine        = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
nrTotalLine:SetPoint("TOPLEFT", globalNRLabel, "BOTTOMLEFT", 8, -4)
local nrTotalWinLine     = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
nrTotalWinLine:SetPoint("TOPLEFT", nrTotalLine, "BOTTOMLEFT", 8, -2)
local nrTotalLoseLine    = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
nrTotalLoseLine:SetPoint("TOPLEFT", nrTotalWinLine, "BOTTOMLEFT", 0, -2)
local nrTotalWinrateLine = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
nrTotalWinrateLine:SetPoint("TOPLEFT", nrTotalLoseLine, "BOTTOMLEFT", 0, -2)

-- ----- Session count line (spans full width, below global block) -----
local sessionCountLine = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
sessionCountLine:SetPoint("TOPLEFT", totalWinrateLine, "BOTTOMLEFT", -8, -12)

-- ---- Current session block ----
local sessionCurrentTitle = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
sessionCurrentTitle:SetPoint("TOPLEFT", sessionCountLine, "BOTTOMLEFT", 0, -8)

-- Session Total column header + lines
local sessionTotalLabel = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
sessionTotalLabel:SetPoint("TOPLEFT", sessionCurrentTitle, "BOTTOMLEFT", COL_LEFT, -4)
sessionTotalLabel:SetTextColor(0.7, 0.85, 1.0)

local sessionGamesLine   = StatLine(statPanel, sessionTotalLabel, 8, -4)
local sessionWinLine     = StatLine(statPanel, sessionGamesLine, 8, -2)
local sessionLoseLine    = StatLine(statPanel, sessionWinLine, 0, -2)
local sessionWinrateLine = StatLine(statPanel, sessionLoseLine, 0, -2)

-- Session NR column header + lines
local sessionNRLabel = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
sessionNRLabel:SetPoint("TOPLEFT", sessionCurrentTitle, "BOTTOMLEFT", COL_RIGHT, -4)
sessionNRLabel:SetTextColor(1.0, 0.85, 0.3)

local sessionNRGamesLine   = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
sessionNRGamesLine:SetPoint("TOPLEFT", sessionNRLabel, "BOTTOMLEFT", 8, -4)
local sessionNRWinLine     = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
sessionNRWinLine:SetPoint("TOPLEFT", sessionNRGamesLine, "BOTTOMLEFT", 8, -2)
local sessionNRLoseLine    = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
sessionNRLoseLine:SetPoint("TOPLEFT", sessionNRWinLine, "BOTTOMLEFT", 0, -2)
local sessionNRWinrateLine = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
sessionNRWinrateLine:SetPoint("TOPLEFT", sessionNRLoseLine, "BOTTOMLEFT", 0, -2)

-- ----- Fair-play ranking block -----
local fairplayTitle = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
fairplayTitle:SetPoint("TOPLEFT", sessionWinrateLine, "BOTTOMLEFT", -8, -16)

local fairplayRefreshHint = statPanel:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
fairplayRefreshHint:SetPoint("TOPLEFT", fairplayTitle, "BOTTOMLEFT", 0, -2)

local COL_WIDTH_PET   = 163
local COL_WIDTH_WIN   = 45
local COL_WIDTH_LOSE  = 45
local COL_WIDTH_RATIO = 50
local COL_SECTION_GAP = 20

-- Remove old section labels and NR column headers from the old approach
local sectionTotalLabel    = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
local sectionNoRandomLabel = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")

local headerPet   = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
local headerWin   = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
local headerLose  = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
local headerRatio = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
headerPet:SetPoint("TOPLEFT", fairplayRefreshHint, "BOTTOMLEFT", 4, -10)
headerPet:SetWidth(COL_WIDTH_PET); headerPet:SetJustifyH("LEFT")
headerWin:SetPoint("LEFT", headerPet, "RIGHT", 0, 0)
headerWin:SetWidth(COL_WIDTH_WIN); headerWin:SetJustifyH("LEFT")
headerLose:SetPoint("LEFT", headerWin, "RIGHT", 10, 0)
headerLose:SetWidth(COL_WIDTH_LOSE); headerLose:SetJustifyH("LEFT")
headerRatio:SetPoint("LEFT", headerLose, "RIGHT", 0, 0)
headerRatio:SetWidth(COL_WIDTH_RATIO); headerRatio:SetJustifyH("LEFT")

-- Unused in this layout but declared to avoid breaking references elsewhere
local headerNRWin   = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
local headerNRLose  = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
local headerNRRatio = statPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
headerNRWin:Hide(); headerNRLose:Hide(); headerNRRatio:Hide()
sectionTotalLabel:Hide(); sectionNoRandomLabel:Hide()

local listArea = CreateFrame("Frame", nil, statPanel)
listArea:SetPoint("TOPLEFT", headerPet, "BOTTOMLEFT", 0, -6)

local ROW_HEIGHT = 18
local NUM_ROWS   = 16   -- more rows to fill the space
local rows       = {}
for i = 1, NUM_ROWS do
    local row = CreateFrame("Frame", nil, listArea)
    row:SetPoint("LEFT", listArea, "LEFT", 0, 0)
    row:SetPoint("RIGHT", listArea, "RIGHT", 0, 0)
    row:SetHeight(ROW_HEIGHT)
    if i == 1 then
        row:SetPoint("TOPLEFT", listArea, "TOPLEFT", 0, 0)
    else
        row:SetPoint("TOPLEFT", rows[i - 1], "BOTTOMLEFT", 0, 0)
    end
    row.pet   = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.pet:SetPoint("LEFT", 0, 0); row.pet:SetWidth(COL_WIDTH_PET); row.pet:SetJustifyH("LEFT")
    row.win   = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.win:SetPoint("LEFT", row.pet, "RIGHT", 0, 0); row.win:SetWidth(COL_WIDTH_WIN); row.win:SetJustifyH("LEFT")
    row.lose  = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.lose:SetPoint("LEFT", row.win, "RIGHT", 10, 0); row.lose:SetWidth(COL_WIDTH_LOSE); row.lose:SetJustifyH("LEFT")
    row.ratio = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.ratio:SetPoint("LEFT", row.lose, "RIGHT", 0, 0); row.ratio:SetWidth(COL_WIDTH_RATIO); row.ratio:SetJustifyH("LEFT")
    -- No-random columns (kept but hidden — NR stats are now in the summary block above)
    row.nrWin  = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.nrWin:SetPoint("LEFT", row.ratio, "RIGHT", COL_SECTION_GAP, 0); row.nrWin:SetWidth(COL_WIDTH_WIN); row.nrWin:SetJustifyH("LEFT"); row.nrWin:Hide()
    row.nrLose = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.nrLose:SetPoint("LEFT", row.nrWin, "RIGHT", 10, 0); row.nrLose:SetWidth(COL_WIDTH_LOSE); row.nrLose:SetJustifyH("LEFT"); row.nrLose:Hide()
    row.nrRatio= row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.nrRatio:SetPoint("LEFT", row.nrLose, "RIGHT", 0, 0); row.nrRatio:SetWidth(COL_WIDTH_RATIO); row.nrRatio:SetJustifyH("LEFT"); row.nrRatio:Hide()
    row.editBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
    row.editBtn:SetSize(55, 14)
    row.editBtn:SetPoint("LEFT", row.ratio, "RIGHT", 10, 0)
    row.deleteBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
    row.deleteBtn:SetSize(18, 14)
    row.deleteBtn:SetText("x")
    row.deleteBtn:SetPoint("LEFT", row.editBtn, "RIGHT", 4, 0)
    rows[i] = row
end

local noDataText = statPanel:CreateFontString(nil, "OVERLAY", "GameFontDisable")
noDataText:SetPoint("TOP", listArea, "TOP", 0, -40)
noDataText:Hide()

local resetBtn = CreateFrame("Button", nil, statPanel, "UIPanelButtonTemplate")
resetBtn:SetSize(220, 24)
resetBtn:SetPoint("BOTTOM", 0, 4)

local pageText   = statPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
pageText:SetPoint("BOTTOM", resetBtn, "TOP", 0, 10)
local prevBtn = CreateFrame("Button", nil, statPanel, "UIPanelButtonTemplate")
prevBtn:SetSize(80, 20); prevBtn:SetText("<")
prevBtn:SetPoint("RIGHT", pageText, "LEFT", -10, 0)
local nextBtn = CreateFrame("Button", nil, statPanel, "UIPanelButtonTemplate")
nextBtn:SetSize(80, 20); nextBtn:SetText(">")
nextBtn:SetPoint("LEFT", pageText, "RIGHT", 10, 0)

listArea:SetPoint("BOTTOMRIGHT", statPanel, "BOTTOMRIGHT", -18, 34)
resetBtn:SetScript("OnClick", function()
    local L = BPS_GetLocale()
    StaticPopupDialogs["BPS_RESET_CONFIRM"] = {
        text = L.RESET_CONFIRM, button1 = YES, button2 = NO,
        OnAccept = function() BPS_ResetStats(); BPS_RefreshOptions() end,
        timeout = 0, whileDead = true, hideOnEscape = true, preferredIndex = 3,
    }
    StaticPopup_Show("BPS_RESET_CONFIRM")
end)

-- ===================== Manual edit popup =====================
local editFrame = CreateFrame("Frame", "BPSEditFrame", UIParent, "BackdropTemplate")
editFrame:SetSize(300, 150)
editFrame:SetPoint("CENTER")
editFrame:SetFrameStrata("FULLSCREEN_DIALOG")
editFrame:SetBackdrop({
    bgFile   = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Buttons\\WHITE8x8",
    edgeSize = 1,
})
editFrame:SetBackdropColor(0.08, 0.08, 0.09, 0.97)
editFrame:SetBackdropBorderColor(0.16, 0.55, 0.95, 1)
editFrame:EnableMouse(true)
editFrame:Hide()

local editTitle    = editFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
editTitle:SetPoint("TOP", 0, -16); editTitle:SetWidth(260)
local editWLabel   = editFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
editWLabel:SetPoint("TOPLEFT", 24, -46)
local editWBox     = CreateFrame("EditBox", nil, editFrame, "InputBoxTemplate")
editWBox:SetSize(60, 20)
editWBox:SetPoint("LEFT", editWLabel, "RIGHT", 10, 0)
editWBox:SetAutoFocus(false); editWBox:SetNumeric(true)
local editLLabel   = editFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
editLLabel:SetPoint("TOPLEFT", editWLabel, "BOTTOMLEFT", 0, -18)
local editLBox     = CreateFrame("EditBox", nil, editFrame, "InputBoxTemplate")
editLBox:SetSize(60, 20)
editLBox:SetPoint("LEFT", editLLabel, "RIGHT", 10, 0)
editLBox:SetAutoFocus(false); editLBox:SetNumeric(true)
local editSaveBtn  = CreateFrame("Button", nil, editFrame, "UIPanelButtonTemplate")
editSaveBtn:SetSize(100, 22); editSaveBtn:SetPoint("BOTTOMLEFT", 20, 12)
local editCancelBtn = CreateFrame("Button", nil, editFrame, "UIPanelButtonTemplate")
editCancelBtn:SetSize(100, 22); editCancelBtn:SetPoint("BOTTOMRIGHT", -20, 12)
editCancelBtn:SetScript("OnClick", function() editFrame:Hide() end)

local editingKey = nil
-- Global so it can also be opened from the battle overlay (BattleFrame.lua)
-- via its own "Edit" button, not just from the Statistics tab list.
function BPS_OpenEditPopup(key, wins, losses)
    local L = BPS_GetLocale()
    editingKey = key
    editTitle:SetText(string.format(L.EDIT_TITLE, key))
    editWLabel:SetText(L.EDIT_WINS)
    editLLabel:SetText(L.EDIT_LOSSES)
    editSaveBtn:SetText(L.SAVE_BUTTON)
    editCancelBtn:SetText(L.CANCEL_BUTTON)
    editWBox:SetText(tostring(wins))
    editLBox:SetText(tostring(losses))
    editFrame:Show()
end

editSaveBtn:SetScript("OnClick", function()
    if editingKey then
        BPS_SetStatsFor(editingKey, tonumber(editWBox:GetText()) or 0, tonumber(editLBox:GetText()) or 0)
    end
    editFrame:Hide()
    BPS_RefreshOptions()
    -- Also refresh the pet-battle overlay in case wins/losses vs the
    -- current opponent were just edited from inside a live battle.
    if BPS_UpdateBattleFrame then BPS_UpdateBattleFrame() end
end)

-- ===================== Sorted list + pagination =====================
local sortedCache = {}
local currentPage = 1
local function GetNumPages() return math.max(1, math.ceil(#sortedCache / NUM_ROWS)) end

-- A key is a "Random" entry when it starts with the localised VS_RANDOM prefix
-- (e.g. "Random (CAT/FEL/DRA)").  We test the raw English word as a safe
-- fallback so detection works regardless of the active language.
local function IsRandomKey(key)
    if not key then return false end
    local lk = key:lower()
    return lk:find("^random%(") ~= nil
        or lk:find("^random ") ~= nil
        or key:find("^" .. (BPS_GetLocale().VS_RANDOM or "Random") .. "%(") ~= nil
        or key:find("^" .. (BPS_GetLocale().VS_RANDOM or "Random") .. " ") ~= nil
end

local function BuildSortedList()
    local list = {}
    for key, e in pairs(BPS_GetAllStats()) do
        local total = e.wins + e.losses
        local winrate = total == 0 and 50 or (e.wins / total) * 100
        table.insert(list, { key=key, wins=e.wins, losses=e.losses, winrate=winrate,
            dist=math.abs(winrate - 50), total=total, isRandom=IsRandomKey(key) })
    end
    table.sort(list, function(a, b)
        if a.dist ~= b.dist then return a.dist < b.dist end
        -- Same distance from 50% (e.g. 100% vs 0%): the higher winrate wins.
        if a.winrate ~= b.winrate then return a.winrate > b.winrate end
        return a.total > b.total
    end)
    return list
end

local function UpdatePage()
    local L = BPS_GetLocale()
    local np = GetNumPages()
    currentPage = math.max(1, math.min(currentPage, np))
    local off = (currentPage - 1) * NUM_ROWS
    for i = 1, NUM_ROWS do
        local d = sortedCache[i + off]
        local row = rows[i]
        if d then
            row.pet:SetText(d.key)
            row.win:SetText(d.wins)
            row.lose:SetText(d.losses)
            row.ratio:SetText(string.format("%.0f%%", d.winrate))
            -- No-random columns: show dashes for random entries, real data otherwise
            if d.isRandom then
                row.nrWin:SetText("-"); row.nrLose:SetText("-"); row.nrRatio:SetText("-")
                row.nrWin:SetTextColor(0.5, 0.5, 0.5)
                row.nrLose:SetTextColor(0.5, 0.5, 0.5)
                row.nrRatio:SetTextColor(0.5, 0.5, 0.5)
            else
                local nrTotal = d.wins + d.losses
                local nrWR = nrTotal == 0 and 0 or (d.wins / nrTotal) * 100
                row.nrWin:SetText(d.wins); row.nrLose:SetText(d.losses)
                row.nrRatio:SetText(string.format("%.0f%%", nrWR))
                row.nrWin:SetTextColor(1,1,1); row.nrLose:SetTextColor(1,1,1); row.nrRatio:SetTextColor(1,1,1)
            end
            row.editBtn:SetText(BPS_GetLocale().EDIT_BUTTON)
            row.editBtn:SetScript("OnClick", function() BPS_OpenEditPopup(d.key, d.wins, d.losses) end)
            row.deleteBtn:SetScript("OnClick", function()
                local Lx = BPS_GetLocale()
                StaticPopupDialogs["BPS_DELETE_ENTRY_CONFIRM"] = {
                    text = string.format(Lx.DELETE_ENTRY_CONFIRM or "Delete stats for %s?", d.key),
                    button1 = YES, button2 = NO,
                    OnAccept = function()
                        BPS_DeleteStatsFor(d.key)
                        BPS_RefreshOptions()
                    end,
                    timeout = 0, whileDead = true, hideOnEscape = true, preferredIndex = 3,
                }
                StaticPopup_Show("BPS_DELETE_ENTRY_CONFIRM")
            end)
            row:Show()
        else
            row:Hide()
        end
    end
    pageText:SetText(string.format("%d / %d", currentPage, np))
    prevBtn:SetEnabled(currentPage > 1)
    nextBtn:SetEnabled(currentPage < np)
end

prevBtn:SetScript("OnClick", function() currentPage = currentPage - 1; UpdatePage() end)
nextBtn:SetScript("OnClick", function() currentPage = currentPage + 1; UpdatePage() end)

-- ===================== Master refresh =====================
-- Split into several smaller local functions (each only capturing the
-- upvalues it actually needs) because a single function referencing every
-- widget in this file exceeds Lua's 60-upvalue limit per function.

local function RefreshTabsAndLang(L)
    tabBtns[TAB_OPTIONS].label:SetText(L.OPTIONS_TITLE_TAB or "Options")
    tabBtns[TAB_STATS].label:SetText(L.STATS_TITLE_TAB or "Statistics")
    tabBtns[TAB_INTERFACE].label:SetText(L.INTERFACE_TITLE_TAB or "Interface")

    langLabel:SetText(L.LANGUAGE)
    UIDropDownMenu_Initialize(langDropdown, LangDropdown_Init)
    local LANG_LABELS = {
        EN = L.LANG_EN, FR = L.LANG_FR, DE = L.LANG_DE, IT = L.LANG_IT,
        PL = L.LANG_PL, SV = L.LANG_SV, CS = L.LANG_CS,
        RU = L.LANG_RU, ZH = L.LANG_ZH, PT = L.LANG_PT, ES = L.LANG_ES,
    }
    local lbl = LANG_LABELS[BattlePetStatisticsDB.language] or L.LANG_AUTO
    UIDropDownMenu_SetText(langDropdown, lbl)
end

local function RefreshInterfaceTab(L)
    dispTitle:SetText(L.DISPLAY_SECTION_TITLE)
    dispHint:SetText(L.DISPLAY_SECTION_HINT or "")
    for i, key in ipairs(DISP_KEYS) do
        local cb = dispChecks[i]
        cb:SetChecked(BattlePetStatisticsDB.display[key] and true or false)
        cb.labelFS:SetText(L[DISP_LKEYS[i]])
    end
end

local function RefreshGoalSection(L)
    goalTitle:SetText(L.GOAL_SECTION_TITLE)
    goalCompletedLabel:SetText(L.GOAL_COMPLETED_LIST_TITLE or "Haut Fait validés :")
    goalCurrentLabel:SetText(L.GOAL_CURRENT)

    local completedGoals = {}
    for _, goal in ipairs(BPS_GOALS) do
        if BPS_IsGoalCompleted(goal.id) then
            table.insert(completedGoals, goal)
        end
    end

    local lastCompletedElement -- whichever element ends up at the bottom of the completed list
    if #completedGoals == 0 then
        goalNoneCompletedText:SetText(L.GOAL_NONE_COMPLETED or "Aucun haut fait validé pour le moment.")
        goalNoneCompletedText:Show()
        for _, fs in ipairs(completedLines) do fs:Hide() end
        lastCompletedElement = goalNoneCompletedText
    else
        goalNoneCompletedText:Hide()
        for i, fs in ipairs(completedLines) do
            local goal = completedGoals[i]
            if goal then
                fs:SetText("[OK] " .. BPS_GetGoalName(goal) .. " (" .. goal.threshold .. "/" .. goal.threshold .. ")")
                fs:Show()
                lastCompletedElement = fs
            else
                fs:Hide()
            end
        end
    end

    -- Re-anchor the "current goal" label right below the last visible completed
    -- element, so the completed list only takes the vertical space it needs.
    goalCurrentLabel:ClearAllPoints()
    goalCurrentLabel:SetPoint("TOPLEFT", lastCompletedElement, "BOTTOMLEFT", -4, -14)

    UIDropDownMenu_Initialize(goalDropdown, GoalDropdown_Init)
    -- Catch up the current goal in case it was already completed before the
    -- player ever opened this panel (e.g. fresh install with pre-existing
    -- achievement progress), so we don't show "All done" prematurely.
    if BPS_AutoAdvanceGoal then BPS_AutoAdvanceGoal() end
    local cg = BPS_GetCurrentGoal()
    if cg and not BPS_IsGoalCompleted(cg.id) then
        local prog = BPS_GetRealProgress(cg)
        UIDropDownMenu_SetText(goalDropdown, string.format("%s (%d/%d)", BPS_GetGoalName(cg), math.min(prog, cg.threshold), cg.threshold))
    else
        UIDropDownMenu_SetText(goalDropdown, L.GOAL_DONE_LINE or "All done!")
    end
end

local function RefreshAutomation(L)
    autoTitle:SetText(L.AUTO_SECTION_TITLE or "Automation")
    autoRequeueLabel:SetText(L.AUTO_REQUEUE_LABEL or "Auto-requeue after each battle")
    autoRequeueHint:SetText(L.AUTO_REQUEUE_HINT or "Automatically re-enters the PvP queue after each battle ends.")
    autoDelayLabel:SetText(L.AUTO_REQUEUE_DELAY_LABEL or "Delay before requeue (seconds):")
    autoDelayHint:SetText(L.AUTO_REQUEUE_DELAY_HINT or "Increase if your PC is slow to load between battles.")

    EnsureDB()
    autoRequeueCheck:SetChecked(BattlePetStatisticsDB.autoRequeue == true)
    autoDelayBox:SetText(tostring(BattlePetStatisticsDB.autoRequeueDelay or 3))
    autoDelayRow:SetShown(BattlePetStatisticsDB.autoRequeue == true)
end

local function RefreshKeybindAndTradeWin(L)
    kbTitle:SetText(L.KEYBIND_SECTION_TITLE)
    kbHint:SetText(L.KEYBIND_HINT)
    kbOpenBtn:SetText(L.OPEN_KEYBINDINGS)

    twTitle:SetText(L.TRADEWIN_SECTION_TITLE)
    twIntro:SetText(L.TRADEWIN_INTRO)
    local SHOW_WINDOW_LABEL_KEYS = {
        "TRADEWIN_SHOW_STARTUP", "TRADEWIN_SHOW_AFTER_BATTLE",
        "TRADEWIN_SHOW_NEVER", "TRADEWIN_SHOW_IF_NOT_RENAMED",
    }
    for i, radio in ipairs(twRadios) do
        radio.text:SetText(L[SHOW_WINDOW_LABEL_KEYS[i]])
        radio:SetChecked(BattlePetStatisticsDB.showWindowMode == SHOW_WINDOW_MODES[i])
    end
    twRestoreLabel:SetText(L.TRADEWIN_RESTORE_QUESTION)
    twRestoreYes.text:SetText(L.TRADEWIN_RESTORE_YES)
    twRestoreNo.text:SetText(L.TRADEWIN_RESTORE_NO)
    twRestoreYes:SetChecked(BattlePetStatisticsDB.restoreNamesOnLogout == true)
    twRestoreNo:SetChecked(BattlePetStatisticsDB.restoreNamesOnLogout == false)
end

local function RefreshStatsGlobal(L)
    statsHeader:SetText(L.STATS_GLOBAL_TITLE)
    local allStats = BPS_GetAllStats()
    local totalW, totalL = 0, 0
    local nrW, nrL = 0, 0
    for key, e in pairs(allStats) do
        totalW = totalW + e.wins; totalL = totalL + e.losses
        if not IsRandomKey(key) then nrW = nrW + e.wins; nrL = nrL + e.losses end
    end
    local totalGames   = totalW + totalL
    local nrGames      = nrW + nrL
    local totalWinrate = totalGames == 0 and 0 or (totalW / totalGames) * 100
    local nrWinrate    = nrGames == 0 and 0 or (nrW / nrGames) * 100

    globalTotalLabel:SetText(L.COL_SECTION_TOTAL or "Total")
    globalNRLabel:SetText(L.COL_SECTION_NO_RANDOM or "Without Random")

    totalLine:SetText(string.format(L.TOTAL_GAMES, totalGames))
    totalWinLine:SetText(string.format(L.TOTAL_WON, totalW))
    totalLoseLine:SetText(string.format(L.TOTAL_LOST, totalL))
    totalWinrateLine:SetText(string.format(L.WINRATE, Round(totalWinrate)))

    nrTotalLine:SetText(string.format(L.NR_TOTAL_GAMES or "Without Random: %d", nrGames))
    nrTotalWinLine:SetText(string.format(L.NR_TOTAL_WON or "Won: %d", nrW))
    nrTotalLoseLine:SetText(string.format(L.NR_TOTAL_LOST or "Lost: %d", nrL))
    nrTotalWinrateLine:SetText(string.format(L.WINRATE, Round(nrWinrate)))

    sessionCountLine:SetText(string.format(L.SESSION_COUNT_LINE, BPS_GetSessionCount()))

    sessionCurrentTitle:SetText(L.SESSION_CURRENT_TITLE)
    sessionTotalLabel:SetText(L.COL_SECTION_TOTAL or "Total")
    sessionNRLabel:SetText(L.COL_SECTION_NO_RANDOM or "Without Random")

    local sW, sL = BPS_GetSessionTotals()
    local sNRW, sNRL = BPS_GetSessionNRTotals()
    local sessionGames = sW + sL
    local sNRGames     = sNRW + sNRL
    local sessionWinrate = sessionGames == 0 and 0 or (sW / sessionGames) * 100
    local sNRWinrate     = sNRGames == 0 and 0 or (sNRW / sNRGames) * 100

    sessionGamesLine:SetText(string.format(L.SESSION_GAMES_PLAYED, sessionGames))
    sessionWinLine:SetText(string.format(L.TOTAL_WON, sW))
    sessionLoseLine:SetText(string.format(L.TOTAL_LOST, sL))
    sessionWinrateLine:SetText(string.format(L.WINRATE, Round(sessionWinrate)))

    sessionNRGamesLine:SetText(string.format(L.NR_SESSION_GAMES or "Without Random: %d", sNRGames))
    sessionNRWinLine:SetText(string.format(L.NR_TOTAL_WON or "Won: %d", sNRW))
    sessionNRLoseLine:SetText(string.format(L.NR_TOTAL_LOST or "Lost: %d", sNRL))
    sessionNRWinrateLine:SetText(string.format(L.WINRATE, Round(sNRWinrate)))
end

local function RefreshFairplayTable(L)
    fairplayTitle:SetText(L.FAIRPLAY_TITLE)
    fairplayRefreshHint:SetText(L.FAIRPLAY_REFRESH_HINT)
    headerPet:SetText(L.COL_PET)
    headerWin:SetText(L.COL_WIN)
    headerLose:SetText(L.COL_LOSE)
    headerRatio:SetText(L.COL_WINRATE)
    resetBtn:SetText(L.RESET_BUTTON)

    sortedCache = BuildSortedList()
    currentPage = 1

    if #sortedCache == 0 then
        noDataText:SetText(L.NO_DATA); noDataText:Show()
        listArea:Hide(); pageText:Hide(); prevBtn:Hide(); nextBtn:Hide()
    else
        noDataText:Hide(); listArea:Show(); pageText:Show(); prevBtn:Show(); nextBtn:Show()
    end
    UpdatePage()
end

function BPS_RefreshOptions()
    EnsureDB()
    local L = BPS_GetLocale()

    RefreshTabsAndLang(L)
    RefreshGoalSection(L)
    RefreshKeybindAndTradeWin(L)
    RefreshAutomation(L)
    RefreshInterfaceTab(L)
    RefreshStatsGlobal(L)
    RefreshFairplayTable(L)
end

function BPS_ToggleOptions()
    EnsureDB()
    if optionsFrame:IsShown() then
        optionsFrame:Hide()
    else
        -- Show Options tab by default
        currentTab = TAB_OPTIONS
        for i, p in ipairs(tabPanels) do
            p:SetShown(i == currentTab)
            StyleTabButton(tabBtns[i], i == currentTab)
        end
        BPS_RefreshOptions()
        optionsFrame:Show()
    end
end
