--[[
============================================================================
ETB v11.0 - MainFrame.lua (V8 INTEGRATED - COMPREHENSIVE FIX)
============================================================================
Fixes:
1. Pagination triggers item name refresh
2. Hover preview (ModelPreview popup) properly shows on hover
3. Live Preview with slot buttons + remove/hide icons
4. Accurate per-slot collection stats
5. Clear indication of current slot/weapon type being viewed

Preview Separation:
- HOVER PREVIEW: ETB.ModelPreview floating popup (single item, resets)
- LIVE PREVIEW: Right panel for building outfits (stacks items)
============================================================================
]]--

local ADDON_NAME, NS = ...
local ETB = NS.ETB

ETB.MainFrame = {
    frame = nil,
    items = {},
    totalItemCount = 0,  -- Total count without loading all items
    itemRows = {},
    currentPage = 1,
    itemsPerPage = 28,  -- 14 rows x 2 columns
    categoryMode = "armor",
    previewRotation = 0,
    livePreviewSlots = {},  -- Track what's in each slot
    lastPageLoaded = 0,  -- Track which page we last loaded
}

local MF = ETB.MainFrame

-- =============================================================================
-- CONSTANTS - Carefully measured for proper layout
-- =============================================================================

local FRAME_WIDTH = 1350   -- Expanded from 1200
local FRAME_HEIGHT = 850   -- Expanded from 750
local SIDEBAR_WIDTH = 210  -- Slightly wider for dropdowns
local PREVIEW_WIDTH = 340  -- Wider preview panel (was 280)
local ITEM_ROW_WIDTH = 310 -- Wider item rows
local ITEM_ROW_HEIGHT = 36
local ROWS_PER_COLUMN = 14 -- More rows with taller frame
local COLUMNS = 2

-- Layout measurements - Golden border is ~32px, content inset is 8px
-- All positions relative to frame edges
local FRAME_MARGIN = 15           -- Margin from frame edge to content panels
local TITLE_AREA_HEIGHT = 45      -- Space for title at top (inside border)
local TOPBAR_HEIGHT = 95          -- Category buttons bar height
local BOTTOMBAR_HEIGHT = 50       -- Navigation bar height

-- Calculated positions (Y is negative from top, positive from bottom)
local TOPBAR_Y = -(TITLE_AREA_HEIGHT)           -- TopBar starts after title (-45)
local CONTENT_Y = -(TITLE_AREA_HEIGHT + TOPBAR_HEIGHT + 5)  -- Content starts after topbar (-145)
local BOTTOM_MARGIN = FRAME_MARGIN + BOTTOMBAR_HEIGHT + 5   -- Space reserved at bottom (70)

-- NOTE: Armor slots and weapon categories are now selected via dropdowns in the sidebar
-- The actual category definitions are in ETB.CATEGORY_INFO (Core/Init.lua)

-- Live Preview slot layout (left and right of model)
local PREVIEW_SLOTS_LEFT = { "Head", "Shoulder", "Back", "Chest", "Shirt", "Tabard", "Wrist" }
local PREVIEW_SLOTS_RIGHT = { "Hands", "Waist", "Legs", "Feet", "MainHand", "OffHand" }

-- =============================================================================
-- CREATE MAIN FRAME
-- =============================================================================

function MF:Create()
    if self.frame then return self.frame end
    
    local f = CreateFrame("Frame", "ETBMainFrame", UIParent, "BackdropTemplate")
    f:SetSize(FRAME_WIDTH, FRAME_HEIGHT)
    f:SetPoint("CENTER")
    f:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Border",
        tile = true, tileSize = 32, edgeSize = 32,
        insets = { left = 8, right = 8, top = 8, bottom = 8 }
    })
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", f.StopMovingOrSizing)
    f:SetFrameStrata("HIGH")
    f:Hide()
    
    -- Hide ModelPreview when main frame is hidden (ESC, close button, etc.)
    f:SetScript("OnHide", function()
        if ETB.ModelPreview then
            ETB.ModelPreview:Hide()
        end
        if ETB.AppearanceDetail then
            ETB.AppearanceDetail:Hide()
        end
    end)
    
    -- ESC closes frame (taint-safe: avoids modifying UISpecialFrames)
    f:SetScript("OnKeyDown", function(self, key)
        if key == "ESCAPE" then
            pcall(self.SetPropagateKeyboardInput, self, false)
            self:Hide()
        else
            pcall(self.SetPropagateKeyboardInput, self, true)
        end
    end)
    
    f.closeBtn = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    f.closeBtn:SetPoint("TOPRIGHT", -8, -8)
    
    f.title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    f.title:SetPoint("TOP", 0, -18)
    f.title:SetText("|cffffd100Enhanced Transmog Browser|r")
    
    f.version = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.version:SetPoint("TOPRIGHT", -40, -22)
    f.version:SetText("|cff888888v" .. (ETB.version or "12.0") .. "|r")
    
    self:CreateTopBar(f)
    self:CreateLeftSidebar(f)
    self:CreateItemGrid(f)
    self:CreateLivePreview(f)
    self:CreateBottomBar(f)
    
    self.frame = f
    return f
end

-- =============================================================================
-- TOP BAR - With clear current category display
-- =============================================================================

function MF:CreateTopBar(f)
    local topBar = CreateFrame("Frame", nil, f, "BackdropTemplate")
    topBar:SetPoint("TOPLEFT", FRAME_MARGIN, TOPBAR_Y)
    topBar:SetPoint("TOPRIGHT", -FRAME_MARGIN, TOPBAR_Y)
    topBar:SetHeight(TOPBAR_HEIGHT)
    topBar:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 12,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    f.topBar = topBar
    
    -- ==========================================================================
    -- ROW 1: Current Viewing (left) + Sort (right)
    -- ==========================================================================
    
    f.currentViewingLabel = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    f.currentViewingLabel:SetPoint("TOPLEFT", 10, -8)
    f.currentViewingLabel:SetText("|cff00ff00Currently Viewing: Head|r")
    
    -- Sort dropdown (right side, row 1)
    local sortLabel = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    sortLabel:SetPoint("TOPRIGHT", -160, -8)
    sortLabel:SetText("|cffffffffSort:|r")
    
    f.sortDropdown = ETBDropdown:Create("ETBSortDropdown", topBar)
    f.sortDropdown:SetPoint("TOPRIGHT", -10, -3)
    ETBDropdown:SetWidth(f.sortDropdown, 110)
    ETBDropdown:SetText(f.sortDropdown, "Name (A-Z)")
    ETBDropdown:Initialize(f.sortDropdown, function(self, _items_)
        local info = ETBDropdown:CreateInfo()
        for _, s in ipairs({
            { text = "Name (A-Z)", sort = "name", asc = true },
            { text = "Name (Z-A)", sort = "name", asc = false },
            { text = "Quality ↑", sort = "quality", asc = true },
            { text = "Quality ↓", sort = "quality", asc = false },
        }) do
            info.text = s.text
            info.func = function()
                ETBDropdown:SetText(f.sortDropdown, s.text)
                ETB.Filtering:SetSort(s.sort, s.asc)
                MF:RefreshItems()
            end
            ETBDropdown:AddButton(_items_, info)
        end
    end)
    
    -- ==========================================================================
    -- ROW 2: Armor slot buttons (11 buttons, 28px each = 308px + label)
    -- ==========================================================================
    
    f.slotIcons = {}
    f.weaponTypeIcons = {}
    
    local armorLabel = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    armorLabel:SetPoint("TOPLEFT", 10, -32)
    armorLabel:SetText("|cffffd100Armor:|r")
    
    local armorSlots = {
        { id = 1,  name = "Head" },
        { id = 2,  name = "Shoulder" },
        { id = 3,  name = "Back" },
        { id = 4,  name = "Chest" },
        { id = 5,  name = "Shirt" },
        { id = 6,  name = "Tabard" },
        { id = 7,  name = "Wrist" },
        { id = 8,  name = "Hands" },
        { id = 9,  name = "Waist" },
        { id = 10, name = "Legs" },
        { id = 11, name = "Feet" },
    }
    
    local BUTTON_SIZE = 28
    local BUTTON_SPACING = 30
    local ARMOR_ROW_START_X = 55
    local ARMOR_ROW_Y = -28
    
    for i, slot in ipairs(armorSlots) do
        local btn = CreateFrame("Button", nil, topBar)
        btn:SetSize(BUTTON_SIZE, BUTTON_SIZE)
        btn:SetPoint("TOPLEFT", ARMOR_ROW_START_X + (i-1) * BUTTON_SPACING, ARMOR_ROW_Y)
        
        btn.icon = btn:CreateTexture(nil, "ARTWORK")
        btn.icon:SetAllPoints()
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
        btn.icon:SetTexture(catInfo and catInfo.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
        btn.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        
        btn.selected = btn:CreateTexture(nil, "OVERLAY")
        btn.selected:SetAllPoints()
        btn.selected:SetColorTexture(0, 1, 0, 0.4)
        btn.selected:Hide()
        
        btn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
        
        btn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_BOTTOM")
            GameTooltip:SetText(slot.name)
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
        btn:SetScript("OnClick", function()
            ETB.Filtering:SetCategory(slot.id)
            MF.categoryMode = "armor"
            MF:UpdateSlotHighlight(slot.id)
            MF:UpdateCategoryTabs()
            MF:UpdateCurrentViewingLabel(slot.name)
            MF:UpdateWeaponCategoryUI()
            MF:RefreshItems()
        end)
        
        btn.slotID = slot.id
        btn.slotName = slot.name
        f.slotIcons[slot.id] = btn
    end
    
    -- ==========================================================================
    -- ROW 3: Weapon buttons (16 buttons organized in groups)
    -- ==========================================================================
    
    local WEAPON_ROW_Y = -60
    
    -- 1H Weapons (6 buttons)
    local label1H = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label1H:SetPoint("TOPLEFT", 10, WEAPON_ROW_Y - 4)
    label1H:SetText("|cffffd1001H:|r")
    
    local weapons1H = {
        { id = 14, name = "1H Swords" },
        { id = 13, name = "1H Axes" },
        { id = 15, name = "1H Maces" },
        { id = 16, name = "Daggers" },
        { id = 17, name = "Fist Weapons" },
        { id = 12, name = "Wands" },
    }
    
    local START_1H = 35
    for i, slot in ipairs(weapons1H) do
        local btn = CreateFrame("Button", nil, topBar)
        btn:SetSize(BUTTON_SIZE, BUTTON_SIZE)
        btn:SetPoint("TOPLEFT", START_1H + (i-1) * BUTTON_SPACING, WEAPON_ROW_Y)
        
        btn.icon = btn:CreateTexture(nil, "ARTWORK")
        btn.icon:SetAllPoints()
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
        btn.icon:SetTexture(catInfo and catInfo.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
        btn.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        
        btn.selected = btn:CreateTexture(nil, "OVERLAY")
        btn.selected:SetAllPoints()
        btn.selected:SetColorTexture(0, 1, 0, 0.4)
        btn.selected:Hide()
        
        btn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
        
        btn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_BOTTOM")
            GameTooltip:SetText(slot.name)
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
        btn:SetScript("OnClick", function()
            ETB.Filtering:SetCategory(slot.id)
            MF.categoryMode = "weapons"
            MF:UpdateSlotHighlight(slot.id)
            MF:UpdateCategoryTabs()
            local catInfo2 = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
            MF:UpdateCurrentViewingLabel(catInfo2 and catInfo2.name or slot.name)
            MF:UpdateWeaponCategoryUI()
            MF:RefreshItems()
        end)
        
        btn.slotID = slot.id
        btn.slotName = slot.name
        f.slotIcons[slot.id] = btn
    end
    
    -- 2H Weapons (5 buttons)
    local label2H = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label2H:SetPoint("TOPLEFT", 225, WEAPON_ROW_Y - 4)
    label2H:SetText("|cffffd1002H:|r")
    
    local weapons2H = {
        { id = 21, name = "2H Swords" },
        { id = 20, name = "2H Axes" },
        { id = 22, name = "2H Maces" },
        { id = 23, name = "Staves" },
        { id = 24, name = "Polearms" },
    }
    
    local START_2H = 250
    for i, slot in ipairs(weapons2H) do
        local btn = CreateFrame("Button", nil, topBar)
        btn:SetSize(BUTTON_SIZE, BUTTON_SIZE)
        btn:SetPoint("TOPLEFT", START_2H + (i-1) * BUTTON_SPACING, WEAPON_ROW_Y)
        
        btn.icon = btn:CreateTexture(nil, "ARTWORK")
        btn.icon:SetAllPoints()
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
        btn.icon:SetTexture(catInfo and catInfo.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
        btn.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        
        btn.selected = btn:CreateTexture(nil, "OVERLAY")
        btn.selected:SetAllPoints()
        btn.selected:SetColorTexture(0, 1, 0, 0.4)
        btn.selected:Hide()
        
        btn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
        
        btn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_BOTTOM")
            GameTooltip:SetText(slot.name)
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
        btn:SetScript("OnClick", function()
            ETB.Filtering:SetCategory(slot.id)
            MF.categoryMode = "weapons"
            MF:UpdateSlotHighlight(slot.id)
            MF:UpdateCategoryTabs()
            local catInfo2 = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
            MF:UpdateCurrentViewingLabel(catInfo2 and catInfo2.name or slot.name)
            MF:UpdateWeaponCategoryUI()
            MF:RefreshItems()
        end)
        
        btn.slotID = slot.id
        btn.slotName = slot.name
        f.slotIcons[slot.id] = btn
    end
    
    -- Ranged + Off-Hand (5 buttons)
    local labelRanged = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    labelRanged:SetPoint("TOPLEFT", 415, WEAPON_ROW_Y - 4)
    labelRanged:SetText("|cffffd100Ranged/OH:|r")
    
    local weaponsRanged = {
        { id = 25, name = "Bows" },
        { id = 26, name = "Guns" },
        { id = 27, name = "Crossbows" },
        { id = 18, name = "Shields" },
        { id = 19, name = "Off-Hands" },
    }
    
    local START_RANGED = 490
    for i, slot in ipairs(weaponsRanged) do
        local btn = CreateFrame("Button", nil, topBar)
        btn:SetSize(BUTTON_SIZE, BUTTON_SIZE)
        btn:SetPoint("TOPLEFT", START_RANGED + (i-1) * BUTTON_SPACING, WEAPON_ROW_Y)
        
        btn.icon = btn:CreateTexture(nil, "ARTWORK")
        btn.icon:SetAllPoints()
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
        btn.icon:SetTexture(catInfo and catInfo.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
        btn.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        
        btn.selected = btn:CreateTexture(nil, "OVERLAY")
        btn.selected:SetAllPoints()
        btn.selected:SetColorTexture(0, 1, 0, 0.4)
        btn.selected:Hide()
        
        btn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
        
        btn:SetScript("OnEnter", function(self)
            GameTooltip:SetOwner(self, "ANCHOR_BOTTOM")
            GameTooltip:SetText(slot.name)
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
        btn:SetScript("OnClick", function()
            ETB.Filtering:SetCategory(slot.id)
            MF.categoryMode = "weapons"
            MF:UpdateSlotHighlight(slot.id)
            MF:UpdateCategoryTabs()
            local catInfo2 = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[slot.id]
            MF:UpdateCurrentViewingLabel(catInfo2 and catInfo2.name or slot.name)
            MF:UpdateWeaponCategoryUI()
            MF:RefreshItems()
        end)
        
        btn.slotID = slot.id
        btn.slotName = slot.name
        f.slotIcons[slot.id] = btn
    end
    
    -- Hidden placeholder frames for compatibility
    f.weaponTypeLabel = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.weaponTypeLabel:Hide()
    f.weaponTypeDropdown = ETBDropdown:Create("ETBWeaponTypeDropdown", topBar)
    f.weaponTypeDropdown:Hide()
    f.weaponTypeIconsFrame = CreateFrame("Frame", nil, topBar)
    f.weaponTypeIconsFrame:Hide()
    f.weaponTypeSectionLabel = topBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.weaponTypeSectionLabel:Hide()
end

function MF:UpdateSlotHighlight(selectedID)
    if not self.frame then return end
    -- Update all slot buttons (indexed by slot ID)
    for slotID, btn in pairs(self.frame.slotIcons) do
        if btn and btn.selected then
            btn.selected:SetShown(slotID == selectedID)
        end
    end
end

function MF:UpdateWeaponTypeHighlight(selectedType)
    if not self.frame then return end
    -- Update all weapon type buttons
    for weaponType, btn in pairs(self.frame.weaponTypeIcons or {}) do
        if btn and btn.selected then
            -- Highlight if this is the selected type, or if "all" is selected and this is "all"
            local shouldHighlight = (weaponType == selectedType) or (selectedType == nil and weaponType == "all")
            btn.selected:SetShown(shouldHighlight)
        end
    end
end

function MF:UpdateCurrentViewingLabel(slotName)
    if not self.frame then return end
    local categoryID = ETB.Filtering.filters.category
    local stats = ETB.BlizzardData:GetCollectionStats(categoryID)
    
    -- Check if viewing as different character
    local profileInfo = ""
    if ETB.ProfileManager then
        local PM = ETB.ProfileManager
        if PM.activeProfileKey and PM.currentCharacterKey and PM.activeProfileKey ~= PM.currentCharacterKey then
            local profile = PM:GetActiveProfile()
            if profile then
                profileInfo = string.format(" |cffff8800[As %s - %s]|r", profile.name, profile.classID)
            end
        end
    end
    
    self.frame.currentViewingLabel:SetText(string.format(
        "|cff00ff00Currently Viewing:|r |cffffd100%s|r |cff888888(%d/%d - %.1f%%)|r%s",
        slotName or "Unknown",
        stats.collected, stats.total, stats.percent,
        profileInfo
    ))
end

-- =============================================================================
-- LEFT SIDEBAR
-- =============================================================================

function MF:CreateLeftSidebar(f)
    local sidebar = CreateFrame("Frame", nil, f, "BackdropTemplate")
    sidebar:SetWidth(SIDEBAR_WIDTH)
    sidebar:SetPoint("TOPLEFT", FRAME_MARGIN, CONTENT_Y)
    sidebar:SetPoint("BOTTOMLEFT", FRAME_MARGIN, BOTTOM_MARGIN)
    sidebar:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 12,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    sidebar:SetClipsChildren(true)
    f.sidebar = sidebar
    
    -- ==========================================================================
    -- SIDEBAR LAYOUT (all Y positions relative to sidebar top)
    -- Total height available: ~625px
    -- ==========================================================================
    
    local title = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, -10)
    title:SetText("|cff00ffffFilters|r")
    
    -- Section divider 1
    local divider1 = sidebar:CreateTexture(nil, "ARTWORK")
    divider1:SetSize(SIDEBAR_WIDTH - 16, 2)
    divider1:SetPoint("TOP", 0, -32)
    divider1:SetColorTexture(0.5, 0.5, 0.5, 0.5)
    
    -- Category tabs section - 2x2 grid: Armor/Weapons top, Sets/Builder bottom
    local catLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    catLabel:SetPoint("TOPLEFT", 10, -42)
    catLabel:SetText("|cffffd100Category|r")
    
    -- Row 1: Armor (left), Weapons (right)
    f.armorTab = self:CreateTab(sidebar, "Armor", 10, -60)
    f.armorTab:SetScript("OnClick", function()
        MF:HideSetsView()
        MF.categoryMode = "armor"
        MF:UpdateCategoryTabs()
        ETB.Filtering:SetCategory(1)
        MF:UpdateSlotHighlight(1)
        MF:UpdateCurrentViewingLabel("Head")
        MF:UpdateWeaponCategoryUI()
        MF:RefreshItems()
    end)
    
    f.weaponsTab = self:CreateTab(sidebar, "Weapons", 65, -60)
    f.weaponsTab:SetScript("OnClick", function()
        MF:HideSetsView()
        MF.categoryMode = "weapons"
        MF:UpdateCategoryTabs()
        ETB.Filtering:SetCategory(14)
        MF:UpdateSlotHighlight(14)
        MF:UpdateCurrentViewingLabel("1H Swords")
        MF:UpdateWeaponCategoryUI()
        MF:RefreshItems()
    end)
    
    -- Row 2: Sets (left), Builder (right)
    f.setsTab = self:CreateTab(sidebar, "Sets", 10, -84)
    f.setsTab:SetScript("OnClick", function()
        MF:HideSetBuilderView()
        MF.categoryMode = "sets"
        MF:UpdateCategoryTabs()
        MF:ShowSetsView()
    end)
    
    f.setBuilderTab = self:CreateTab(sidebar, "Builder", 65, -84)
    f.setBuilderTab:SetScript("OnClick", function()
        -- Open Set Builder as a separate standalone window
        if ETB.SetBuilder then
            ETB.SetBuilder:Show()
        end
    end)
    
    -- Section divider 2 (below 2x2 tabs)
    local divider2 = sidebar:CreateTexture(nil, "ARTWORK")
    divider2:SetSize(SIDEBAR_WIDTH - 16, 2)
    divider2:SetPoint("TOP", 0, -112)
    divider2:SetColorTexture(0.5, 0.5, 0.5, 0.5)
    
    -- CLASS FILTER SECTION
    local classLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    classLabel:SetPoint("TOPLEFT", 10, -122)
    classLabel:SetText("|cffffd100Display Options|r")
    
    -- My Class Only checkbox
    f.myClassCheck = CreateFrame("CheckButton", nil, sidebar, "UICheckButtonTemplate")
    f.myClassCheck:SetPoint("TOPLEFT", 8, -138)
    f.myClassCheck:SetChecked(false)
    ETB.Filtering:SetMyClassOnly(false)
    f.myClassCheck:SetScript("OnClick", function(self)
        ETB.Filtering:SetMyClassOnly(self:GetChecked())
        MF:RefreshItems()
    end)
    
    local checkText = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    checkText:SetPoint("LEFT", f.myClassCheck, "RIGHT", 0, 0)
    checkText:SetText("|cff00ff00My Class Only|r")
    
    -- Group by Appearance: always on (one item per unique visual appearance)
    ETB.Filtering:SetGroupByAppearance(true)
    
    -- Armor Type Filter
    local armorTypeLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    armorTypeLabel:SetPoint("TOPLEFT", 10, -166)
    armorTypeLabel:SetText("|cffffd100Armor Type|r")
    
    f.armorTypeDropdown = ETBDropdown:Create("ETBArmorTypeDropdown", sidebar)
    f.armorTypeDropdown:SetPoint("TOPLEFT", 0, -182)
    ETBDropdown:SetWidth(f.armorTypeDropdown, SIDEBAR_WIDTH - 50)
    ETBDropdown:SetText(f.armorTypeDropdown, "All Types")
    ETBDropdown:Initialize(f.armorTypeDropdown, function(self, _items_)
        local info = ETBDropdown:CreateInfo()
        local armorTypes = {
            { value = "all", text = "All Types" },
            { value = "PLATE", text = "Plate" },
            { value = "MAIL", text = "Mail" },
            { value = "LEATHER", text = "Leather" },
            { value = "CLOTH", text = "Cloth" },
        }
        for _, at in ipairs(armorTypes) do
            info.text = at.text
            info.value = at.value
            info.func = function()
                ETBDropdown:SetText(f.armorTypeDropdown, at.text)
                ETB.Filtering:SetArmorType(at.value)
                MF:RefreshItems()
            end
            ETBDropdown:AddButton(_items_, info)
        end
    end)
    
    -- Note: Profile system is still loaded for tracking but UI is removed
    -- Initialize profile system silently if available
    if ETB.ProfileSelector and ETB.ProfileSelector.Initialize then
        ETB.ProfileSelector:Initialize()
    end
    
    -- Section divider 3
    local divider3 = sidebar:CreateTexture(nil, "ARTWORK")
    divider3:SetSize(SIDEBAR_WIDTH - 16, 2)
    divider3:SetPoint("TOP", 0, -214)
    divider3:SetColorTexture(0.5, 0.5, 0.5, 0.5)
    
    -- Source Type
    local sourceLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    sourceLabel:SetPoint("TOPLEFT", 10, -222)
    sourceLabel:SetText("|cffffd100Source Type|r")
    
    f.sourceDropdown = ETBDropdown:Create("ETBSourceDropdown", sidebar)
    f.sourceDropdown:SetPoint("TOPLEFT", 0, -238)
    ETBDropdown:SetWidth(f.sourceDropdown, SIDEBAR_WIDTH - 50)
    ETBDropdown:SetText(f.sourceDropdown, "All Sources")
    ETBDropdown:Initialize(f.sourceDropdown, function(self, _items_)
        local info = ETBDropdown:CreateInfo()
        local sourceTypes = {
            { value = "all",        text = "All Sources" },
            { value = "raid",       text = "Raid" },
            { value = "dungeon",    text = "Dungeon" },
            { value = "quest",      text = "Quest" },
            { value = "vendor",     text = "Vendor" },
            { value = "crafted",    text = "Crafted" },
            { value = "pvp",        text = "PvP" },
            { value = "world_drop", text = "World Drop" },
        }
        for _, st in ipairs(sourceTypes) do
            info.text = st.text
            info.value = st.value
            info.func = function()
                ETBDropdown:SetText(f.sourceDropdown, st.text)
                ETB.Filtering:SetSourceType(st.value)
                MF:RefreshItems()
            end
            ETBDropdown:AddButton(_items_, info)
        end
    end)
    
    -- Expansion Filter
    local expansionLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    expansionLabel:SetPoint("TOPLEFT", 10, -270)
    expansionLabel:SetText("|cffffd100Expansion|r")
    
    f.expansionDropdown = ETBDropdown:Create("ETBExpansionDropdown", sidebar)
    f.expansionDropdown:SetPoint("TOPLEFT", 0, -286)
    ETBDropdown:SetWidth(f.expansionDropdown, SIDEBAR_WIDTH - 50)
    ETBDropdown:SetText(f.expansionDropdown, "All Expansions")
    ETBDropdown:Initialize(f.expansionDropdown, function(self, _items_)
        local info = ETBDropdown:CreateInfo()
        for _, exp in ipairs(ETB.EXPANSIONS) do
            info.text = exp.name
            info.value = exp.id
            info.func = function()
                ETBDropdown:SetText(f.expansionDropdown, exp.name)
                ETB.Filtering:SetExpansion(exp.id)
                MF:RefreshItems()
            end
            ETBDropdown:AddButton(_items_, info)
        end
    end)
    
    -- Section divider 4 - Collection filter section
    local divider4 = sidebar:CreateTexture(nil, "ARTWORK")
    divider4:SetSize(SIDEBAR_WIDTH - 16, 2)
    divider4:SetPoint("TOP", 0, -318)
    divider4:SetColorTexture(0.5, 0.5, 0.5, 0.5)
    
    -- Collection Filter Section
    local collectionLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    collectionLabel:SetPoint("TOPLEFT", 10, -326)
    collectionLabel:SetText("|cffffd100Collection|r")
    
    -- Collection Filter Buttons
    f.collAllBtn = CreateFrame("Button", nil, sidebar, "UIPanelButtonTemplate")
    f.collAllBtn:SetSize(SIDEBAR_WIDTH - 20, 24)
    f.collAllBtn:SetPoint("TOP", 0, -344)
    f.collAllBtn:SetText("» All «")
    f.collAllBtn:SetScript("OnClick", function()
        ETB.Filtering:SetCollectedFilter("all")
        MF:UpdateCollectionButtons("all")
        MF:RefreshItems()
    end)
    
    f.collectedBtn = CreateFrame("Button", nil, sidebar, "UIPanelButtonTemplate")
    f.collectedBtn:SetSize(SIDEBAR_WIDTH - 20, 24)
    f.collectedBtn:SetPoint("TOP", 0, -370)
    f.collectedBtn:SetText("Collected")
    f.collectedBtn:SetScript("OnClick", function()
        ETB.Filtering:SetCollectedFilter("collected")
        MF:UpdateCollectionButtons("collected")
        MF:RefreshItems()
    end)
    
    f.notCollectedBtn = CreateFrame("Button", nil, sidebar, "UIPanelButtonTemplate")
    f.notCollectedBtn:SetSize(SIDEBAR_WIDTH - 20, 24)
    f.notCollectedBtn:SetPoint("TOP", 0, -396)
    f.notCollectedBtn:SetText("Not Collected")
    f.notCollectedBtn:SetScript("OnClick", function()
        ETB.Filtering:SetCollectedFilter("uncollected")
        MF:UpdateCollectionButtons("uncollected")
        MF:RefreshItems()
    end)
    
    -- Set initial button colors
    MF:UpdateCollectionButtons("all")
    
    -- Section divider 5 - Stats
    local divider5 = sidebar:CreateTexture(nil, "ARTWORK")
    divider5:SetSize(SIDEBAR_WIDTH - 16, 2)
    divider5:SetPoint("TOP", 0, -424)
    divider5:SetColorTexture(0.5, 0.5, 0.5, 0.5)
    
    -- Collection Stats
    local statsLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    statsLabel:SetPoint("TOPLEFT", 10, -432)
    statsLabel:SetText("|cffffd100Collection Stats|r")
    
    f.statsOverall = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.statsOverall:SetPoint("TOPLEFT", 10, -448)
    f.statsOverall:SetText("|cffffffffOverall: 0/0 (0%)|r")
    
    f.statsSlot = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.statsSlot:SetPoint("TOPLEFT", 10, -462)
    f.statsSlot:SetText("|cffffffffSlot: 0/0 (0%)|r")
    
    f.statsShowing = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.statsShowing:SetPoint("TOPLEFT", 10, -476)
    f.statsShowing:SetText("|cffffffffShowing: 0 items|r")
    
    -- Refresh Data Button
    f.refreshBtn = CreateFrame("Button", nil, sidebar, "UIPanelButtonTemplate")
    f.refreshBtn:SetSize(SIDEBAR_WIDTH - 20, 24)
    f.refreshBtn:SetPoint("TOP", 0, -498)
    f.refreshBtn:SetText("Refresh Data")
    f.refreshBtn:SetScript("OnClick", function()
        MF:RefreshItems()
    end)
    
    -- Set Builder embed container (below Refresh Data)
    f.setBuilderEmbedContainer = CreateFrame("Frame", nil, sidebar, "BackdropTemplate")
    f.setBuilderEmbedContainer:SetPoint("TOPLEFT", sidebar, 0, -528)
    f.setBuilderEmbedContainer:SetPoint("BOTTOMRIGHT", sidebar, 0, 8)
    f.setBuilderEmbedContainer:SetFrameLevel(sidebar:GetFrameLevel() + 10)
    f.setBuilderEmbedContainer:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 8,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    f.setBuilderEmbedContainer:Hide()
    
    -- Note: Removed Armor Slot Dropdown (use top icons instead)
    -- Note: Removed Profile Selector (still load profiles for tracking)
    
    -- Hidden dummy for armor slot label (for compatibility)
    f.armorSlotLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    f.armorSlotLabel:Hide()
    
    -- Weapon Category Selector (only shown when in weapon mode)
    f.weaponCategoryLabel = sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    f.weaponCategoryLabel:SetPoint("TOPLEFT", 10, -329)
    f.weaponCategoryLabel:SetText("|cffffd100Weapon Category|r")
    f.weaponCategoryLabel:Hide()
    
    f.weaponCategoryDropdown = ETBDropdown:Create("ETBWeaponCategoryDropdown", sidebar)
    f.weaponCategoryDropdown:SetPoint("TOPLEFT", 0, -346)
    ETBDropdown:SetWidth(f.weaponCategoryDropdown, SIDEBAR_WIDTH - 50)
    ETBDropdown:SetText(f.weaponCategoryDropdown, "Select Weapon Type")
    ETBDropdown:Initialize(f.weaponCategoryDropdown, function(self, _items_)
        local info = ETBDropdown:CreateInfo()
        local weaponCategories = {
            { id = 14, name = "1H Swords (399)" },
            { id = 13, name = "1H Axes (256)" },
            { id = 15, name = "1H Maces (390)" },
            { id = 16, name = "Daggers (452)" },
            { id = 17, name = "Fist Weapons (148)" },
            { id = 21, name = "2H Swords (212)" },
            { id = 20, name = "2H Axes (210)" },
            { id = 22, name = "2H Maces (200)" },
            { id = 23, name = "Staves (428)" },
            { id = 24, name = "Polearms (152)" },
            { id = 25, name = "Bows (165)" },
            { id = 26, name = "Guns (150)" },
            { id = 27, name = "Crossbows (87)" },
            { id = 12, name = "Wands (241)" },
            { id = 18, name = "Shields (368)" },
            { id = 19, name = "Off-Hands (263)" },
        }
        for _, cat in ipairs(weaponCategories) do
            info.text = cat.name
            info.value = cat.id
            info.func = function()
                ETBDropdown:SetText(f.weaponCategoryDropdown, cat.name)
                ETB.Filtering:SetCategory(cat.id)
                MF:UpdateSlotHighlight(cat.id)
                local catInfo = ETB.CATEGORY_INFO[cat.id]
                MF:UpdateCurrentViewingLabel(catInfo and catInfo.name or cat.name)
                MF:RefreshItems()
            end
            ETBDropdown:AddButton(_items_, info)
        end
    end)
    f.weaponCategoryDropdown:Hide()
    
    -- Compatibility aliases
    f.weaponTypeLabel = f.weaponCategoryLabel
    f.weaponTypeDropdown = f.weaponCategoryDropdown
end

function MF:CreateTab(parent, text, x, y)
    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(50, 22)
    btn:SetPoint("TOPLEFT", x, y)
    
    btn.bg = btn:CreateTexture(nil, "BACKGROUND")
    btn.bg:SetAllPoints()
    btn.bg:SetColorTexture(0.2, 0.2, 0.2, 0.8)
    
    btn.text = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    btn.text:SetAllPoints()
    btn.text:SetText(text)
    
    return btn
end

function MF:UpdateCategoryTabs()
    local f = self.frame
    if not f then return end
    
    local tabs = { armor = f.armorTab, weapons = f.weaponsTab, sets = f.setsTab, setbuilder = f.setBuilderTab }
    for mode, tab in pairs(tabs) do
        if tab then
            if mode == self.categoryMode then
                tab.bg:SetColorTexture(0.1, 0.4, 0.1, 0.9)
                tab.text:SetTextColor(0, 1, 0)
            else
                tab.bg:SetColorTexture(0.2, 0.2, 0.2, 0.8)
                tab.text:SetTextColor(1, 1, 1)
            end
        end
    end
end

function MF:UpdateCollectionButtons(filter)
    local f = self.frame
    if not f then return end
    
    filter = filter or ETB.Filtering.filters.collected or "all"
    
    -- Reset all buttons to default
    if f.collAllBtn then
        f.collAllBtn:SetText(filter == "all" and "|cff00ffff» All «|r" or "» All «")
    end
    if f.collectedBtn then
        f.collectedBtn:SetText(filter == "collected" and "|cff00ff00Collected|r" or "Collected")
    end
    if f.notCollectedBtn then
        f.notCollectedBtn:SetText(filter == "uncollected" and "|cffff6666Not Collected|r" or "Not Collected")
    end
    
    -- Legacy support for old button names
    if f.allItemsBtn then
        f.allItemsBtn:SetText(filter == "all" and "|cffffffff» All «|r" or "All Items")
    end
end

function MF:UpdateWeaponTypeFilter()
    -- Delegate to new function name
    self:UpdateWeaponCategoryUI()
end

function MF:UpdateWeaponCategoryUI()
    local f = self.frame
    if not f then return end
    
    -- Show/hide appropriate category selector based on mode
    if self.categoryMode == "weapons" then
        -- Weapon category selected via icon buttons at top - no dropdown needed
        if f.weaponCategoryLabel then f.weaponCategoryLabel:Hide() end
        if f.weaponCategoryDropdown then f.weaponCategoryDropdown:Hide() end
        if f.armorSlotLabel then f.armorSlotLabel:Hide() end
        if f.armorSlotDropdown then f.armorSlotDropdown:Hide() end
        
        -- Update dropdown text to show current category
        local categoryID = ETB.Filtering.filters.category
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[categoryID]
        if catInfo and f.weaponCategoryDropdown then
            ETBDropdown:SetText(f.weaponCategoryDropdown, catInfo.name)
        end
    else
        -- Show armor slot dropdown, hide weapon category dropdown
        if f.armorSlotLabel then f.armorSlotLabel:Show() end
        if f.armorSlotDropdown then f.armorSlotDropdown:Show() end
        if f.weaponCategoryLabel then f.weaponCategoryLabel:Hide() end
        if f.weaponCategoryDropdown then f.weaponCategoryDropdown:Hide() end
        
        -- Update dropdown text to show current slot
        local categoryID = ETB.Filtering.filters.category
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[categoryID]
        if catInfo and f.armorSlotDropdown then
            ETBDropdown:SetText(f.armorSlotDropdown, catInfo.name)
        end
    end
end

-- =============================================================================
-- ITEM GRID (Uses ItemRow components)
-- =============================================================================

function MF:CreateItemGrid(f)
    local grid = CreateFrame("Frame", nil, f, "BackdropTemplate")
    grid:SetPoint("TOPLEFT", f.sidebar, "TOPRIGHT", 8, 0)
    grid:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(PREVIEW_WIDTH + FRAME_MARGIN + 8), BOTTOM_MARGIN)
    grid:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 12,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    f.gridFrame = grid
    f.grid = grid
    
    -- Enable mouse wheel scrolling for page navigation
    grid:EnableMouseWheel(true)
    grid:SetScript("OnMouseWheel", function(self, delta)
        if delta > 0 then
            -- Scroll up = previous page
            if MF.currentPage > 1 then
                MF.currentPage = MF.currentPage - 1
                MF:LoadPageData(MF.currentPage)
                MF:UpdateDisplay()
                MF:UpdateStats()
            end
        else
            -- Scroll down = next page
            local totalPages = math.ceil(MF.totalItemCount / MF.itemsPerPage)
            if MF.currentPage < totalPages then
                MF.currentPage = MF.currentPage + 1
                MF:LoadPageData(MF.currentPage)
                MF:UpdateDisplay()
                MF:UpdateStats()
            end
        end
    end)
    
    -- Section label
    local gridLabel = grid:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    gridLabel:SetPoint("TOPLEFT", 8, -8)
    gridLabel:SetText("|cff00ffffItem Browser|r")
    
    -- Scroll hint
    local scrollHint = grid:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    scrollHint:SetPoint("TOPRIGHT", -8, -8)
    scrollHint:SetText("|cff888888Mouse wheel to scroll pages|r")
    
    self.itemRows = {}
    for row = 1, ROWS_PER_COLUMN do
        for col = 1, COLUMNS do
            local idx = (row - 1) * COLUMNS + col
            local itemRow = ETB.ItemRow:Acquire(grid)
            itemRow:SetSize(ITEM_ROW_WIDTH, ITEM_ROW_HEIGHT - 2)
            -- Start at -28 to leave room for "Item Browser" label (at -8)
            itemRow:SetPoint("TOPLEFT", 5 + (col-1) * (ITEM_ROW_WIDTH + 3), -28 - (row-1) * ITEM_ROW_HEIGHT)
            itemRow:Hide()
            self.itemRows[idx] = itemRow
        end
    end
end

-- =============================================================================
-- LIVE PREVIEW (Right Panel with Slot Buttons + Remove Icons)
-- =============================================================================

function MF:CreateLivePreview(f)
    local preview = CreateFrame("Frame", nil, f, "BackdropTemplate")
    preview:SetWidth(PREVIEW_WIDTH)
    preview:SetPoint("TOPRIGHT", -FRAME_MARGIN, CONTENT_Y)
    preview:SetPoint("BOTTOMRIGHT", -FRAME_MARGIN, BOTTOM_MARGIN)
    preview:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 12,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    f.preview = preview
    
    local title = preview:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, -12)
    title:SetText("|cff00ffffLive Preview|r")
    
    -- Section divider
    local previewDivider = preview:CreateTexture(nil, "ARTWORK")
    previewDivider:SetSize(PREVIEW_WIDTH - 8, 2)
    previewDivider:SetPoint("TOP", 0, -35)
    previewDivider:SetColorTexture(0.5, 0.5, 0.5, 0.5)
    
    -- Model (BIGGER: 260x420)
    f.previewModel = CreateFrame("DressUpModel", nil, preview)
    f.previewModel:SetSize(260, 420)
    f.previewModel:SetPoint("TOP", 0, -40)
    f.previewModel:SetUnit("player")
    f.previewModel:SetRotation(0)
    
    -- Mouse rotation/zoom
    f.previewModel:EnableMouse(true)
    f.previewModel:EnableMouseWheel(true)
    f.previewModel.zoomLevel = 0
    f.previewModel:SetScript("OnMouseDown", function(self, btn)
        if btn == "LeftButton" then
            self.rotating = true
            self.startX = GetCursorPosition()
        end
    end)
    f.previewModel:SetScript("OnMouseUp", function(self) self.rotating = false end)
    f.previewModel:SetScript("OnUpdate", function(self)
        if self.rotating then
            local x = GetCursorPosition()
            MF.previewRotation = (MF.previewRotation or 0) + (x - self.startX) * 0.01
            self:SetRotation(MF.previewRotation)
            self.startX = x
        end
    end)
    -- Proper zoom using camera distance
    f.previewModel:SetScript("OnMouseWheel", function(self, delta)
        self.zoomLevel = (self.zoomLevel or 0) + delta * 0.1
        self.zoomLevel = math.max(-0.5, math.min(1.5, self.zoomLevel))
        self:SetCamDistanceScale(1 - self.zoomLevel)
    end)
    
    -- Create slot buttons on left side
    f.leftSlotBtns = {}
    for i, slotName in ipairs(PREVIEW_SLOTS_LEFT) do
        local btn = self:CreateSlotButton(preview, slotName, i)
        btn:SetPoint("TOPLEFT", 5, -30 - (i-1) * 26)
        f.leftSlotBtns[slotName] = btn
    end
    
    -- Create slot buttons on right side  
    f.rightSlotBtns = {}
    for i, slotName in ipairs(PREVIEW_SLOTS_RIGHT) do
        local btn = self:CreateSlotButton(preview, slotName, i)
        btn:SetPoint("TOPRIGHT", -5, -30 - (i-1) * 26)
        f.rightSlotBtns[slotName] = btn
    end
    
    -- Hint
    local hint = preview:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    hint:SetPoint("TOP", f.previewModel, "BOTTOM", 0, -5)
    hint:SetText("|cff00ff00Drag rotate • Wheel zoom|r")
    
    -- Animation controls row
    local animLabel = preview:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    animLabel:SetPoint("TOP", hint, "BOTTOM", 0, -4)
    animLabel:SetText("|cffffd100Pose:|r")
    
    -- AnimationData.dbc IDs (NOT Emotes.dbc IDs)
    -- Confirmed from TrinityCore AnimationData.dbc + Wowpedia
    local ANIM_PRESETS = {
        { id = 0,   label = "Stand" },      -- AnimData 0: Stand
        { id = 4,   label = "Walk" },       -- AnimData 4: Walk
        { id = 5,   label = "Run" },        -- AnimData 5: Run
        { id = 16,  label = "Attack" },     -- AnimData 16: AttackUnarmed
        { id = 25,  label = "Ready" },      -- AnimData 25: ReadyUnarmed (combat idle)
        { id = 60,  label = "Talk" },       -- AnimData 60: EmoteTalk (confirmed by Wowpedia)
        { id = 32,  label = "Cast" },       -- AnimData 32: SpellCast
        { id = 1,   label = "Death" },      -- AnimData 1: Death
    }
    
    local animBtnWidth = 48
    local animBtnHeight = 18
    local animBtnsPerRow = 4
    local animStartX = -(animBtnWidth * animBtnsPerRow + 3 * (animBtnsPerRow - 1)) / 2
    
    f.animButtons = {}
    for i, preset in ipairs(ANIM_PRESETS) do
        local row = math.floor((i - 1) / animBtnsPerRow)
        local col = (i - 1) % animBtnsPerRow
        
        local animBtn = CreateFrame("Button", nil, preview, "UIPanelButtonTemplate")
        animBtn:SetSize(animBtnWidth, animBtnHeight)
        animBtn:SetPoint("TOP", animLabel, "BOTTOM", animStartX + col * (animBtnWidth + 3) + animBtnWidth / 2, -(row * (animBtnHeight + 2) + 2))
        animBtn:SetText(preset.label)
        animBtn:GetFontString():SetFont(animBtn:GetFontString():GetFont(), 10)
        
        local animID = preset.id
        animBtn:SetScript("OnClick", function()
            -- Highlight active animation
            for _, btn in ipairs(f.animButtons) do
                btn:GetFontString():SetTextColor(1, 0.82, 0)
            end
            animBtn:GetFontString():SetTextColor(0, 1, 0.5)
            f.previewModel:SetAnimation(animID)
            -- Update tester display
            if f.animIDLabel then
                f.animIDLabel:SetText("|cff00ff00ID: " .. animID .. "|r")
            end
            f._currentAnimID = animID
        end)
        
        f.animButtons[i] = animBtn
    end
    
    -- Animation Tester: Manual ID browser to discover correct animations
    local presetRows = math.ceil(#ANIM_PRESETS / animBtnsPerRow)
    local testerY = -(presetRows * (animBtnHeight + 2) + 6)
    
    f._currentAnimID = 0
    
    local testerRow = CreateFrame("Frame", nil, preview)
    testerRow:SetSize(PREVIEW_WIDTH - 20, 20)
    testerRow:SetPoint("TOP", animLabel, "BOTTOM", 0, testerY)
    
    -- Minus button
    local minusBtn = CreateFrame("Button", nil, testerRow, "UIPanelButtonTemplate")
    minusBtn:SetSize(22, 18)
    minusBtn:SetPoint("LEFT", testerRow, "CENTER", -55, 0)
    minusBtn:SetText("-")
    minusBtn:SetScript("OnClick", function()
        f._currentAnimID = max(0, (f._currentAnimID or 0) - 1)
        f.previewModel:SetAnimation(f._currentAnimID)
        f.animIDLabel:SetText("|cff00ff00ID: " .. f._currentAnimID .. "|r")
        -- Clear preset highlights
        for _, btn in ipairs(f.animButtons) do
            btn:GetFontString():SetTextColor(1, 0.82, 0)
        end
    end)
    
    -- ID display label
    f.animIDLabel = testerRow:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.animIDLabel:SetPoint("CENTER", testerRow, "CENTER", 0, 0)
    f.animIDLabel:SetText("|cff00ff00ID: 0|r")
    f.animIDLabel:SetWidth(50)
    f.animIDLabel:SetJustifyH("CENTER")
    
    -- Plus button
    local plusBtn = CreateFrame("Button", nil, testerRow, "UIPanelButtonTemplate")
    plusBtn:SetSize(22, 18)
    plusBtn:SetPoint("LEFT", testerRow, "CENTER", 33, 0)
    plusBtn:SetText("+")
    plusBtn:SetScript("OnClick", function()
        f._currentAnimID = min(500, (f._currentAnimID or 0) + 1)
        f.previewModel:SetAnimation(f._currentAnimID)
        f.animIDLabel:SetText("|cff00ff00ID: " .. f._currentAnimID .. "|r")
        -- Clear preset highlights
        for _, btn in ipairs(f.animButtons) do
            btn:GetFontString():SetTextColor(1, 0.82, 0)
        end
    end)
    
    -- +10 skip button
    local plus10Btn = CreateFrame("Button", nil, testerRow, "UIPanelButtonTemplate")
    plus10Btn:SetSize(28, 18)
    plus10Btn:SetPoint("LEFT", plusBtn, "RIGHT", 2, 0)
    plus10Btn:SetText("+10")
    plus10Btn:GetFontString():SetFont(plus10Btn:GetFontString():GetFont(), 9)
    plus10Btn:SetScript("OnClick", function()
        f._currentAnimID = min(500, (f._currentAnimID or 0) + 10)
        f.previewModel:SetAnimation(f._currentAnimID)
        f.animIDLabel:SetText("|cff00ff00ID: " .. f._currentAnimID .. "|r")
        for _, btn in ipairs(f.animButtons) do
            btn:GetFontString():SetTextColor(1, 0.82, 0)
        end
    end)
    
    f.previewItemName = preview:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    f.previewItemName:SetPoint("TOP", hint, "BOTTOM", 0, -(math.ceil(#ANIM_PRESETS / animBtnsPerRow) * (animBtnHeight + 2) + 30))
    f.previewItemName:SetWidth(260)
    
    -- Buttons (adjusted for taller model + animation controls)
    local btnY = -550
    
    f.undressBtn = CreateFrame("Button", nil, preview, "UIPanelButtonTemplate")
    f.undressBtn:SetSize(105, 22)
    f.undressBtn:SetPoint("TOPLEFT", 10, btnY)
    f.undressBtn:SetText("Undress")
    f.undressBtn:SetScript("OnClick", function()
        f.previewModel:Undress()
        wipe(MF.livePreviewSlots)
        MF:UpdateSlotButtons()
    end)
    
    f.myGearBtn = CreateFrame("Button", nil, preview, "UIPanelButtonTemplate")
    f.myGearBtn:SetSize(105, 22)
    f.myGearBtn:SetPoint("TOPRIGHT", -10, btnY)
    f.myGearBtn:SetText("My Gear")
    f.myGearBtn:SetScript("OnClick", function()
        -- Avoid SetUnit which causes graphics reset - just dress
        f.previewModel:Dress()
        wipe(MF.livePreviewSlots)
    end)
    
    f.clearBtn = CreateFrame("Button", nil, preview, "UIPanelButtonTemplate")
    f.clearBtn:SetSize(PREVIEW_WIDTH - 30, 22)
    f.clearBtn:SetPoint("TOP", 0, btnY - 28)
    f.clearBtn:SetText("Clear All")
    f.clearBtn:SetScript("OnClick", function()
        f.previewModel:Undress()
        MF.previewRotation = 0
        f.previewModel:SetRotation(0)
        f.previewModel:SetPosition(0, 0, 0)
        f.previewItemName:SetText("")
        wipe(MF.livePreviewSlots)
        MF:UpdateSlotButtons()
    end)
    
    -- Random Outfit button
    f.randomOutfitBtn = CreateFrame("Button", nil, preview, "UIPanelButtonTemplate")
    f.randomOutfitBtn:SetSize(PREVIEW_WIDTH - 30, 22)
    f.randomOutfitBtn:SetPoint("TOP", 0, btnY - 56)
    f.randomOutfitBtn:SetText("|cffffd100Random Outfit|r")
    f.randomOutfitBtn:SetScript("OnClick", function()
        MF:GenerateRandomOutfit()
    end)
    
    -- See Sources button
    f.seeSourcesBtn = CreateFrame("Button", nil, preview, "UIPanelButtonTemplate")
    f.seeSourcesBtn:SetSize(PREVIEW_WIDTH - 30, 22)
    f.seeSourcesBtn:SetPoint("TOP", 0, btnY - 84)
    f.seeSourcesBtn:SetText("|cff00ffffSee Sources|r")
    f.seeSourcesBtn:SetScript("OnClick", function()
        if ETB.SourcesSummaryPanel then
            ETB.SourcesSummaryPanel:Show(MF.livePreviewSlots)
        end
    end)
    f.seeSourcesBtn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_TOP")
        GameTooltip:SetText("See Sources")
        GameTooltip:AddLine("View all sources for your outfit items", 0.8, 0.8, 0.8)
        GameTooltip:AddLine("grouped by location for efficient farming", 0.8, 0.8, 0.8)
        GameTooltip:Show()
    end)
    f.seeSourcesBtn:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)
end

-- Slot name to category ID mapping
local SLOT_TO_CATEGORY_MAP = {
    Head = 1,
    Shoulder = 2,
    Back = 3,
    Chest = 4,
    Shirt = 5,
    Tabard = 6,
    Wrist = 7,
    Hands = 8,
    Waist = 9,
    Legs = 10,
    Feet = 11,
    -- MainHand and OffHand handled specially
}

-- Get first usable weapon category for player's class
-- mainHand: true for main hand weapons, false for off-hand
local function GetFirstUsableWeaponCategory(forOffHand)
    local usableWeapons = ETB.BlizzardData and ETB.BlizzardData:GetPlayerWeaponTypes() or {}
    
    if forOffHand then
        -- Off-hand categories: 18 (Shields), 19 (Off-Hands/Holdables)
        -- Check shields first
        if usableWeapons["SHIELD"] then
            return 18  -- Shields
        end
        -- Then holdables (most classes can use these)
        if usableWeapons["HOLDABLE"] then
            return 19  -- Off-Hands
        end
        -- Fallback to shields
        return 18
    else
        -- Main hand weapon categories in preferred order for browsing
        -- Order: 1H weapons first (more variety), then 2H, then ranged
        local categoryOrder = {
            -- One-handed melee (most common, good variety)
            { cat = 14, type = "SWORD" },      -- 1H Swords
            { cat = 13, type = "AXE" },        -- 1H Axes
            { cat = 15, type = "MACE" },       -- 1H Maces
            { cat = 16, type = "DAGGER" },     -- Daggers
            { cat = 17, type = "FIST" },       -- Fist Weapons
            -- Two-handed melee
            { cat = 21, type = "SWORD" },      -- 2H Swords
            { cat = 20, type = "AXE" },        -- 2H Axes
            { cat = 22, type = "MACE" },       -- 2H Maces
            { cat = 23, type = "STAFF" },      -- Staves
            { cat = 24, type = "POLEARM" },    -- Polearms
            -- Ranged
            { cat = 25, type = "BOW" },        -- Bows
            { cat = 26, type = "GUN" },        -- Guns
            { cat = 27, type = "CROSSBOW" },   -- Crossbows
            { cat = 12, type = "WAND" },       -- Wands
        }
        
        for _, entry in ipairs(categoryOrder) do
            if usableWeapons[entry.type] then
                return entry.cat
            end
        end
        
        -- Fallback to first weapon category
        return 14  -- 1H Swords
    end
end

function MF:CreateSlotButton(parent, slotName, index)
    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(55, 22)
    
    btn.bg = btn:CreateTexture(nil, "BACKGROUND")
    btn.bg:SetAllPoints()
    btn.bg:SetColorTexture(0.15, 0.15, 0.15, 0.8)
    
    btn.text = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    btn.text:SetPoint("LEFT", 3, 0)
    btn.text:SetText(slotName:sub(1, 6))
    btn.text:SetTextColor(0.6, 0.6, 0.6)
    
    -- Remove/Hide icon (X button)
    btn.removeBtn = CreateFrame("Button", nil, btn)
    btn.removeBtn:SetSize(14, 14)
    btn.removeBtn:SetPoint("RIGHT", -2, 0)
    btn.removeBtn:SetNormalTexture("Interface\\Buttons\\UI-StopButton")
    btn.removeBtn:SetHighlightTexture("Interface\\Buttons\\UI-StopButton", "ADD")
    btn.removeBtn:Hide()
    btn.removeBtn:SetScript("OnClick", function()
        -- Remove this slot from preview
        MF.livePreviewSlots[slotName] = nil
        MF:RebuildLivePreview()
        btn.text:SetTextColor(0.6, 0.6, 0.6)
        btn.bg:SetColorTexture(0.15, 0.15, 0.15, 0.8)
        btn.removeBtn:Hide()
    end)
    
    -- Click empty slot to filter to that category; Right-click filled slot to see sources
    btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    btn:SetScript("OnClick", function(self, button)
        if button == "RightButton" then
            -- Right-click: Open SourcesModal for the item in this slot
            local item = MF.livePreviewSlots[slotName]
            if item and ETB.SourcesModal then
                ETB.SourcesModal:Show(item)
            end
            return
        end
        
        -- Left-click: Only act if slot is empty
        if MF.livePreviewSlots[slotName] then
            return  -- Slot is filled, do nothing (use X button to remove)
        end
        
        local categoryID
        
        if slotName == "MainHand" then
            -- Get first usable weapon category for main hand
            categoryID = GetFirstUsableWeaponCategory(false)
        elseif slotName == "OffHand" then
            -- Get first usable off-hand category
            categoryID = GetFirstUsableWeaponCategory(true)
        else
            -- Armor slot - direct mapping
            categoryID = SLOT_TO_CATEGORY_MAP[slotName]
        end
        
        if categoryID and ETB.Filtering then
            ETB.Filtering:SetCategory(categoryID)
            -- Refresh the item list
            if MF.RefreshItems then
                MF:RefreshItems()
            end
            -- Update UI highlighting
            if MF.UpdateSlotHighlight then
                MF:UpdateSlotHighlight(categoryID)
            end
        end
    end)
    
    btn:SetScript("OnEnter", function(self)
        self.bg:SetColorTexture(0.25, 0.25, 0.25, 0.8)
        GameTooltip:SetOwner(self, "ANCHOR_LEFT")
        
        if MF.livePreviewSlots[slotName] then
            local item = MF.livePreviewSlots[slotName]
            GameTooltip:SetText(item.name or "Unknown")
            GameTooltip:AddLine("Click X to remove", 0.7, 0.7, 0.7)
            GameTooltip:AddLine("Right-click for source info", 0, 1, 1)
        else
            -- Empty slot - show hint to click to filter
            GameTooltip:SetText(slotName .. " (Empty)")
            GameTooltip:AddLine("Click to browse " .. slotName .. " items", 0, 1, 1)
            if slotName == "MainHand" or slotName == "OffHand" then
                GameTooltip:AddLine("(Shows weapons your class can use)", 0.7, 0.7, 0.7)
            end
        end
        GameTooltip:Show()
    end)
    btn:SetScript("OnLeave", function(self)
        if not MF.livePreviewSlots[slotName] then
            self.bg:SetColorTexture(0.15, 0.15, 0.15, 0.8)
        else
            self.bg:SetColorTexture(0.1, 0.3, 0.1, 0.8)
        end
        GameTooltip:Hide()
    end)
    
    btn.slotName = slotName
    return btn
end

function MF:UpdateSlotButtons()
    if not self.frame then return end
    
    local allBtns = {}
    for name, btn in pairs(self.frame.leftSlotBtns or {}) do allBtns[name] = btn end
    for name, btn in pairs(self.frame.rightSlotBtns or {}) do allBtns[name] = btn end
    
    for slotName, btn in pairs(allBtns) do
        if self.livePreviewSlots[slotName] then
            btn.text:SetTextColor(0, 1, 0)
            btn.bg:SetColorTexture(0.1, 0.3, 0.1, 0.8)
            btn.removeBtn:Show()
        else
            btn.text:SetTextColor(0.6, 0.6, 0.6)
            btn.bg:SetColorTexture(0.15, 0.15, 0.15, 0.8)
            btn.removeBtn:Hide()
        end
    end
end

function MF:RebuildLivePreview()
    if not self.frame then return end
    
    self.frame.previewModel:Undress()
    for slotName, item in pairs(self.livePreviewSlots) do
        if item and item.itemID then
            self.frame.previewModel:TryOn("item:" .. item.itemID)
        end
    end
    self:UpdateSlotButtons()
end

-- =============================================================================
-- BOTTOM BAR (Pagination with refresh + Memory display)
-- =============================================================================

function MF:CreateBottomBar(f)
    local bar = CreateFrame("Frame", nil, f, "BackdropTemplate")
    bar:SetPoint("BOTTOMLEFT", FRAME_MARGIN, FRAME_MARGIN)
    bar:SetPoint("BOTTOMRIGHT", -FRAME_MARGIN, FRAME_MARGIN)
    bar:SetHeight(BOTTOMBAR_HEIGHT)
    bar:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 12,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    f.bottomBar = bar
    
    -- Section label
    local barLabel = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    barLabel:SetPoint("TOPLEFT", 8, -5)
    barLabel:SetText("|cffffd100Navigation|r")
    
    -- Previous button (left side)
    f.prevBtn = CreateFrame("Button", nil, bar, "UIPanelButtonTemplate")
    f.prevBtn:SetSize(90, 26)
    f.prevBtn:SetPoint("LEFT", 15, 0)
    f.prevBtn:SetText("« Previous")
    f.prevBtn:SetScript("OnClick", function()
        if MF.currentPage > 1 then
            MF.currentPage = MF.currentPage - 1
            MF:UpdateDisplayWithRefresh()
        end
    end)
    
    -- Page text (center)
    f.pageText = bar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    f.pageText:SetPoint("CENTER", 0, 8)
    
    f.itemCountText = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.itemCountText:SetPoint("TOP", f.pageText, "BOTTOM", 0, -2)
    
    -- Next button (right side)
    f.nextBtn = CreateFrame("Button", nil, bar, "UIPanelButtonTemplate")
    f.nextBtn:SetSize(90, 26)
    f.nextBtn:SetPoint("RIGHT", -15, 0)
    f.nextBtn:SetText("Next »")
    f.nextBtn:SetScript("OnClick", function()
        local totalPages = math.ceil(MF.totalItemCount / MF.itemsPerPage)
        if MF.currentPage < totalPages then
            MF.currentPage = MF.currentPage + 1
            MF:UpdateDisplayWithRefresh()
        end
    end)
    
    -- Memory display (bottom right, away from buttons)
    f.memoryText = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.memoryText:SetPoint("BOTTOMRIGHT", bar, "BOTTOMRIGHT", -120, 8)
    f.memoryText:SetText("|cff888888Mem: 0 KB|r")
end

-- =============================================================================
-- ADD TO LIVE PREVIEW (builds outfit)
-- =============================================================================

function MF:AddToLivePreview(item)
    if not self.frame or not item or not item.itemID then return end
    
    -- Determine slot name from item
    local slotName = self:GetSlotNameForItem(item)
    
    -- Store in slot
    if slotName then
        self.livePreviewSlots[slotName] = item
    end
    
    -- Add to model (don't undress)
    self.frame.previewModel:TryOn("item:" .. item.itemID)
    
    local color = ETB:GetQualityColor(item.quality)
    self.frame.previewItemName:SetText(color .. (item.name or "Unknown") .. "|r")
    
    self:UpdateSlotButtons()
end

function MF:GetSlotNameForItem(item)
    -- Map category ID to slot name
    local categoryID = ETB.Filtering.filters.category
    local catInfo = ETB.CATEGORY_INFO[categoryID]
    if catInfo then
        return catInfo.name
    end
    return "Unknown"
end

-- =============================================================================
-- SETS VIEW
-- =============================================================================

function MF:ShowSetsView()
    if not self.frame then return end
    
    -- Hide Set Builder panel when showing Sets
    MF:HideSetBuilderView()
    
    -- Hide the regular items grid
    if self.frame.gridFrame then
        self.frame.gridFrame:Hide()
    end
    
    -- Hide live preview when in sets mode
    if self.frame.preview then
        self.frame.preview:Hide()
    end
    
    -- Hide Set Builder container
    if self.setBuilderContainer then
        self.setBuilderContainer:Hide()
    end
    
    -- Show the sets UI
    if ETB.SetsUI then
        if not self.setsContainer then
            self.setsContainer = CreateFrame("Frame", nil, self.frame)
            self.setsContainer:SetPoint("TOPLEFT", self.frame.sidebar, "TOPRIGHT", 8, 0)
            self.setsContainer:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", -FRAME_MARGIN, BOTTOM_MARGIN)
        end
        self.setsContainer:Show()
        ETB.SetsUI:Show(self.setsContainer)
    else
        ETB:Print("|cffff0000SetsUI module not loaded!|r")
    end
    
    self:UpdateCurrentViewingLabel("Transmog Sets")
end

function MF:ShowSetBuilderView()
    if not self.frame then return end
    
    -- Hide sets UI and item grid
    if ETB.SetsUI then ETB.SetsUI:Hide() end
    if self.setsContainer then self.setsContainer:Hide() end
    if self.frame.gridFrame then self.frame.gridFrame:Hide() end
    if self.frame.preview then self.frame.preview:Hide() end
    
    -- Create and show Set Builder container (same area as Sets)
    if not self.setBuilderContainer then
        self.setBuilderContainer = CreateFrame("Frame", nil, self.frame)
        self.setBuilderContainer:SetPoint("TOPLEFT", self.frame.sidebar, "TOPRIGHT", 8, 0)
        self.setBuilderContainer:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", -FRAME_MARGIN, BOTTOM_MARGIN)
    end
    self.setBuilderContainer:Show()
    
    if ETB.SetBuilder and ETB.SetBuilder.ShowAsPanel then
        ETB.SetBuilder:ShowAsPanel(self.setBuilderContainer)
    end
    
    self:UpdateCurrentViewingLabel("Set Builder")
end

function MF:HideSetBuilderView()
    if self.setBuilderContainer then
        self.setBuilderContainer:Hide()
    end
    if ETB.SetBuilder and ETB.SetBuilder.HidePanel then
        ETB.SetBuilder:HidePanel()
    end
end

function MF:ShowSetBuilderEmbedded()
    local f = self.frame
    if not f or not f.setBuilderEmbedContainer then return end
    f.setBuilderEmbedContainer:Show()
    if ETB.SetBuilder and ETB.SetBuilder.ShowEmbedded then
        ETB.SetBuilder:ShowEmbedded(f.setBuilderEmbedContainer)
    end
end

function MF:HideSetBuilderEmbedded()
    local f = self.frame
    if not f or not f.setBuilderEmbedContainer then return end
    f.setBuilderEmbedContainer:Hide()
    if ETB.SetBuilder and ETB.SetBuilder.HideEmbedded then
        ETB.SetBuilder:HideEmbedded()
    end
end

function MF:IsSetBuilderEmbeddedShown()
    local f = self.frame
    return f and f.setBuilderEmbedContainer and f.setBuilderEmbedContainer:IsShown()
end

function MF:HideSetsView()
    if ETB.SetsUI then
        ETB.SetsUI:Hide()
    end
    
    if self.setsContainer then
        self.setsContainer:Hide()
    end
    
    MF:HideSetBuilderView()
    MF:HideSetBuilderEmbedded()
    
    if self.frame and self.frame.gridFrame then
        self.frame.gridFrame:Show()
    end
    
    if self.frame and self.frame.preview then
        self.frame.preview:Show()
    end
end

-- =============================================================================
-- REFRESH & DISPLAY
-- =============================================================================

function MF:RefreshItems()
    if not self.frame then return end
    
    -- Hide hover preview when changing categories/refreshing
    if ETB.ModelPreview then
        ETB.ModelPreview:Hide()
    end
    
    -- Clear old items from memory first
    self.items = {}
    self.lastPageLoaded = 0
    
    -- Clear all ItemRow data
    self:ClearAllItemRows()
    
    -- Get total count by loading items (but don't store them all)
    local allItems = ETB.Filtering:GetItems({ limit = 100000 })
    self.totalItemCount = #allItems
    
    -- Immediately clear the large array to free memory
    allItems = nil
    collectgarbage("step")
    
    self.currentPage = 1
    self:LoadPageData(self.currentPage)
    self:UpdateDisplay()
    self:UpdateStats()
end

function MF:LoadPageData(pageNum)
    if not self.frame then return end
    if self.lastPageLoaded == pageNum and #self.items > 0 then
        return
    end
    
    self.items = {}
    self.lastPageLoaded = pageNum
    
    local startIdx = (pageNum - 1) * self.itemsPerPage + 1
    local endIdx = startIdx + self.itemsPerPage - 1
    local loadLimit = endIdx
    
    local allItems = ETB.Filtering:GetItems({ limit = loadLimit })
    
    for i = startIdx, math.min(endIdx, #allItems) do
        if allItems[i] then
            local item = allItems[i]
            if item.itemID and ETB.BlizzardData and ETB.BlizzardData.itemCache then
                local cached = ETB.BlizzardData.itemCache:Get(item.itemID)
                if cached then
                    item = cached
                end
            end
            self.items[#self.items + 1] = item
        end
    end
    
    allItems = nil
    
    self:UpdateDisplay()
    
    if pageNum == 1 then
        self:PreloadFirstPageItemInfo()
    else
        self:RequestItemInfoAsync()
    end
end

function MF:PreloadFirstPageItemInfo()
    -- Pre-load item info for first page - try both GetItemInfoInstant and GetItemInfo
    if not ETB.BlizzardData then return end
    
    local updated = false
    local processed = 0
    local maxPerLoad = 20  -- Process all items on first page
    
    for i, item in ipairs(self.items) do
        if item and item.itemID and processed < maxPerLoad then
            -- Get the cached version to ensure we're updating the right object
            local cached = ETB.BlizzardData.itemCache and ETB.BlizzardData.itemCache:Get(item.itemID)
            if cached then
                item = cached  -- Use cached version
                self.items[i] = cached  -- Update reference in items array
            end
            
            -- Check if we need to load the name
            if not item.name or item.name == "Loading..." or item.name == "Unknown" or type(item.name) == "number" then
                local name, link, quality, itemLevel, minLevel, itemType, itemSubType, stackCount, equipLoc, texture
                
                -- Try GetItemInfo first (works if item is already cached by WoW)
                name, link, quality, itemLevel, minLevel, itemType, itemSubType, stackCount, equipLoc, texture = GetItemInfo(item.itemID)
                
                -- If GetItemInfo didn't work, try GetItemInfoInstant for icon only
                -- NOTE: GetItemInfoInstant returns (itemID, itemType, itemSubType, equipLoc, icon, classID, subclassID)
                -- It does NOT return name/link/quality — different signature from GetItemInfo!
                if (not name or name == "") and GetItemInfoInstant then
                    local _, _, _, instEquipLoc, instTexture = GetItemInfoInstant(item.itemID)
                    -- Only use for icon and equipLoc, NOT for name/quality
                    texture = instTexture
                    equipLoc = instEquipLoc
                end
                
                -- If we got a name, update immediately
                if name and name ~= "" then
                    item.name = name
                    if type(quality) == "number" then item.quality = quality end
                    item.icon = texture or item.icon
                    item.invType = equipLoc or item.invType
                    updated = true
                else
                    -- Request async loading
                    ETB.BlizzardData.pendingItemInfo[item.itemID] = true
                    GetItemInfo(item.itemID)  -- Request async
                end
                processed = processed + 1
            end
        end
    end
    
    -- If we got instant results, update display immediately
    if updated then
        self:UpdateDisplay()
    end
    
    -- Also try to update any async items that might have loaded
    local asyncUpdated = false
    for _ = 1, 5 do  -- More passes to catch items
        if ETB.BlizzardData:UpdatePendingItems() then
            asyncUpdated = true
        end
    end
    if asyncUpdated then
        -- Refresh item references from cache before updating display
        for i, item in ipairs(self.items) do
            if item and item.itemID and ETB.BlizzardData.itemCache then
                local cached = ETB.BlizzardData.itemCache:Get(item.itemID)
                if cached then
                    self.items[i] = cached
                end
            end
        end
        self:UpdateDisplay()
    end
end

function MF:RequestItemInfoAsync()
    -- Request item info async for all items on the current page
    if not ETB.BlizzardData then return end
    
    for _, item in ipairs(self.items) do
        if item and item.itemID and
           (not item.name or item.name == "Loading..." or item.name == "Unknown" or type(item.name) == "number") then
            ETB.BlizzardData.pendingItemInfo[item.itemID] = true
            GetItemInfo(item.itemID)  -- Request async
        end
    end
    
    -- Try to update items immediately if available
    local updated = false
    
    for _, item in ipairs(self.items) do
        if item and item.itemID and ETB.BlizzardData.pendingItemInfo[item.itemID] then
            if ETB.BlizzardData:UpdateItemInfo(item.itemID) then
                ETB.BlizzardData.pendingItemInfo[item.itemID] = nil
                updated = true
            end
        end
    end
    
    if updated then
        self:UpdateDisplay()
    end
end

function MF:ClearAllItemRows()
    -- Clear itemData from all rows to free memory
    for _, row in ipairs(self.itemRows) do
        if row then
            row.itemData = nil
            row.index = nil
            row:Hide()
        end
    end
end

function MF:UpdateDisplayWithRefresh()
    -- Force clear old data first
    self:ClearAllItemRows()
    
    -- Load new page data
    self:LoadPageData(self.currentPage)
    
    -- Display is already updated in LoadPageData if items were updated
    -- Just ensure display is current
    self:UpdateDisplay()
end

function MF:UpdateDisplay()
    if not self.frame then return end
    
    local startIdx = (self.currentPage - 1) * self.itemsPerPage + 1
    
    -- Refresh item references from cache before displaying to ensure we have latest data
    if ETB.BlizzardData and ETB.BlizzardData.itemCache then
        for i, item in ipairs(self.items) do
            if item and item.itemID then
                local cached = ETB.BlizzardData.itemCache:Get(item.itemID)
                if cached then
                    self.items[i] = cached  -- Use cached version with latest data
                end
            end
        end
    end
    
    -- Update visible rows and clear unused ones
    for i, row in ipairs(self.itemRows) do
        local item = self.items[i]  -- Items array now only contains current page
        if item then
            ETB.ItemRow:Update(row, item, startIdx + i - 1)
            row:Show()
        else
            -- Clear data from unused rows
            row.itemData = nil
            row.index = nil
            row:Hide()
        end
    end
    
    local totalPages = math.max(1, math.ceil(self.totalItemCount / self.itemsPerPage))
    self.frame.pageText:SetText(string.format("Page %d / %d", self.currentPage, totalPages))
    self.frame.itemCountText:SetText(string.format("%d Appearances", self.totalItemCount))
    self.frame.prevBtn:SetEnabled(self.currentPage > 1)
    self.frame.nextBtn:SetEnabled(self.currentPage < totalPages)
end

function MF:UpdatePendingItemsForCurrentPage()
    -- Only update items that are on the current page to reduce work
    if not ETB.BlizzardData or not ETB.BlizzardData.pendingItemInfo then
        return false
    end
    
    local updated = false
    local processed = 0
    local maxPerTick = 5  -- Limit to 5 items per tick to avoid lag
    
    -- Check only items on current page
    for _, item in ipairs(self.items) do
        if item and item.itemID and ETB.BlizzardData.pendingItemInfo[item.itemID] then
            if ETB.BlizzardData:UpdateItemInfo(item.itemID) then
                ETB.BlizzardData.pendingItemInfo[item.itemID] = nil
                updated = true
                processed = processed + 1
                if processed >= maxPerTick then
                    break  -- Don't process too many at once
                end
            end
        end
    end
    
    return updated
end

function MF:UpdateChangedRows()
    -- Only update rows whose items have changed, not all rows
    if not self.frame then return end
    
    local startIdx = (self.currentPage - 1) * self.itemsPerPage + 1
    
    for i, row in ipairs(self.itemRows) do
        local item = self.items[i]
        if item then
            -- Check if this row needs updating
            local needsUpdate = false
            if not row.itemData or row.itemData.itemID ~= item.itemID then
                -- Different item or no item data
                needsUpdate = true
            elseif row.itemData.name ~= item.name then
                -- Name changed
                needsUpdate = true
            end
            
            if needsUpdate then
                ETB.ItemRow:Update(row, item, startIdx + i - 1)
                row:Show()
            end
        elseif row:IsShown() then
            -- Item removed, hide row
            row.itemData = nil
            row.index = nil
            row:Hide()
        end
    end
end

function MF:UpdateStats()
    if not self.frame then return end
    
    -- Overall stats
    local allStats = ETB.BlizzardData:GetAllStats()
    local overallElement = self.frame.statsOverall or self.frame.overallStats
    if overallElement then
        overallElement:SetText(string.format("|cffffd100Overall:|r %d/%d (%.1f%%)", 
            allStats.collected, allStats.total, allStats.percent))
    end
    
    -- Current slot stats
    local categoryID = ETB.Filtering.filters.category
    local slotStats = ETB.BlizzardData:GetCollectionStats(categoryID)
    local catInfo = ETB.CATEGORY_INFO[categoryID]
    local slotElement = self.frame.statsSlot or self.frame.currentSlotStats
    if slotElement then
        slotElement:SetText(string.format("|cffffd100%s:|r %d/%d (%.1f%%)",
            catInfo and catInfo.name or "Slot",
            slotStats.collected, slotStats.total, slotStats.percent))
    end
    
    -- Current view count
    local showingElement = self.frame.statsShowing or self.frame.currentViewStats
    if showingElement then
        showingElement:SetText(string.format("Showing: %d items", self.totalItemCount))
    end
    
    -- Update the "Currently Viewing" label too
    self:UpdateCurrentViewingLabel(catInfo and catInfo.name or "Unknown")
end

-- =============================================================================
-- SHOW/HIDE/TOGGLE
-- =============================================================================

function MF:Show()
    if not self.frame then self:Create() end
    
    if not ETB.BlizzardData.initialized then
        ETB.BlizzardData:Initialize()
    end
    
    -- Ensure Sets are initialized
    if ETB.Sets and not ETB.Sets.INITIALIZED then
        ETB.Sets:Initialize()
    end
    
    self:UpdateCategoryTabs()
    self:UpdateCollectionButtons()
    self:UpdateSlotHighlight(ETB.Filtering.filters.category)
    self:UpdateWeaponTypeFilter()  -- Show/hide weapon type filter based on category
    
    -- Initial slot label
    local catInfo = ETB.CATEGORY_INFO[ETB.Filtering.filters.category]
    self:UpdateCurrentViewingLabel(catInfo and catInfo.name or "Head")
    
    self.frame:Show()
    self:RefreshItems()
    
    -- Periodic update for item info loading AND memory display
    -- Reduced frequency and limited processing per tick to improve performance
    if not self.updateTicker then
        self.updateTicker = C_Timer.NewTicker(0.5, function()  -- More frequent for faster name loading
            if self.frame and self.frame:IsShown() then
                -- Only update items for current page to reduce work
                local updated = self:UpdatePendingItemsForCurrentPage()
                
                if updated then
                    -- Refresh item references from cache
                    if ETB.BlizzardData and ETB.BlizzardData.itemCache then
                        for i, item in ipairs(self.items) do
                            if item and item.itemID then
                                local cached = ETB.BlizzardData.itemCache:Get(item.itemID)
                                if cached then
                                    self.items[i] = cached
                                end
                            end
                        end
                    end
                    -- Update all rows to show new names
                    self:UpdateDisplay()
                end
                
                -- Update memory display (less frequently)
                if not self.memoryUpdateCounter then self.memoryUpdateCounter = 0 end
                self.memoryUpdateCounter = self.memoryUpdateCounter + 1
                if self.memoryUpdateCounter >= 6 then  -- Update memory every 3 seconds (6 * 0.5s)
                    self:UpdateMemoryDisplay()
                    self.memoryUpdateCounter = 0
                end
            end
        end)
    end
end

function MF:UpdateMemoryDisplay()
    if not self.frame or not self.frame.memoryText then return end
    local mem = ETB:GetMemoryUsage()
    local formatted = ETB:FormatMemory(mem)
    local color = mem > 50000 and "|cffff6666" or mem > 20000 and "|cffffff00" or "|cff00ff00"
    self.frame.memoryText:SetText(color .. "Mem: " .. formatted .. "|r")
end

function MF:Hide()
    if self.frame then self.frame:Hide() end
    if self.updateTicker then
        self.updateTicker:Cancel()
        self.updateTicker = nil
    end
    -- Also hide the hover preview popup
    if ETB.ModelPreview then
        ETB.ModelPreview:Hide()
    end
end

function MF:Toggle()
    if self.frame and self.frame:IsShown() then self:Hide() else self:Show() end
end

function MF:IsShown()
    return self.frame and self.frame:IsShown()
end

-- =============================================================================
-- HOOKS: Click adds to Live Preview, Hover shows floating ModelPreview
-- =============================================================================

ETB.EventBus:Register("ETB_INITIALIZED", function()
    -- Override ItemRow click to add to Live Preview
    local origOnClick = ETB.ItemRow.OnClick
    ETB.ItemRow.OnClick = function(self, frame, button)
        local item = frame.itemData
        if not item then return end
        
        if button == "LeftButton" then
            if IsShiftKeyDown() then
                -- Shift+Click: Link in chat
                if item.itemID then
                    local _, link = GetItemInfo(item.itemID)
                    if link then ChatEdit_InsertLink(link) end
                end
            elseif IsControlKeyDown() then
                -- Ctrl+Click: Dressing room
                if item.itemID and DressUpItemLink then
                    DressUpItemLink("item:" .. item.itemID)
                end
            elseif IsAltKeyDown() then
                -- Alt+Click: Toggle favorite
                if ETB.FavoritesManager then
                    ETB.FavoritesManager:ToggleFavorite(item.itemID)
                    ETB.ItemRow:Update(frame, item, frame.index)
                end
            else
                -- Left-Click: Add to LIVE PREVIEW
                MF:AddToLivePreview(item)
            end
        elseif button == "RightButton" then
            if ETB.SourcesModal then
                ETB.SourcesModal:Show(item)
            end
        end
    end
    
    -- Hover triggers floating ModelPreview popup (separate from Live Preview)
    -- This is already handled by ItemRow.OnEnter which calls ETB.ModelPreview:PreviewItem()
    -- when ETB.Settings:Get("preview.showOnHover") is true
    
end, ETB.EventBus.Priority.LATE)

-- =============================================================================
-- RANDOM OUTFIT GENERATOR
-- =============================================================================

function MF:GenerateRandomOutfit()
    local f = self.frame
    if not f or not f.previewModel then return end
    
    -- Get player's armor type
    local _, playerClass = UnitClass("player")
    local armorTypes = {
        WARRIOR = "PLATE", PALADIN = "PLATE", DEATHKNIGHT = "PLATE",
        HUNTER = "MAIL", SHAMAN = "MAIL",
        ROGUE = "LEATHER", DRUID = "LEATHER", MONK = "LEATHER",
        MAGE = "CLOTH", WARLOCK = "CLOTH", PRIEST = "CLOTH",
    }
    local playerArmorType = armorTypes[playerClass] or "LEATHER"
    
    -- Numeric subclass IDs for armor type comparison (GetItemInfoInstant returns these)
    local ARMOR_SUBCLASS = { CLOTH = 1, LEATHER = 2, MAIL = 3, PLATE = 4 }
    local playerArmorSubclass = ARMOR_SUBCLASS[playerArmorType] or 2
    
    -- Category to preview slot name (matches PREVIEW_SLOTS_LEFT / PREVIEW_SLOTS_RIGHT)
    local catToSlotName = {
        [1] = "Head", [2] = "Shoulder", [3] = "Back", [4] = "Chest", [5] = "Shirt", [6] = "Tabard",
        [7] = "Wrist", [8] = "Hands", [9] = "Waist", [10] = "Legs", [11] = "Feet",
    }
    
    -- All transmog slots (armor + shirt + tabard)
    local slots = {
        {cat = 1, invSlot = 1},   -- Head
        {cat = 2, invSlot = 3},   -- Shoulder
        {cat = 3, invSlot = 15},  -- Back (cloak)
        {cat = 4, invSlot = 5},   -- Chest
        {cat = 5, invSlot = 4},   -- Shirt
        {cat = 6, invSlot = 19},  -- Tabard
        {cat = 7, invSlot = 9},   -- Wrist
        {cat = 8, invSlot = 10},  -- Hands
        {cat = 9, invSlot = 6},   -- Waist
        {cat = 10, invSlot = 7},  -- Legs
        {cat = 11, invSlot = 8},  -- Feet
    }
    
    -- Undress first
    f.previewModel:Undress()
    wipe(MF.livePreviewSlots)
    
    local itemsApplied = 0
    local function addToSlot(slotName, itemID)
        if not slotName or not itemID then return end
        local name, _, quality, _, _, _, _, _, _, icon = GetItemInfo(itemID)
        MF.livePreviewSlots[slotName] = { itemID = itemID, name = name, icon = icon, quality = quality or 1 }
    end
    
    for _, slotInfo in ipairs(slots) do
        local appearances = {}
        
        if C_TransmogCollection and C_TransmogCollection.GetCategoryAppearances then
            local catAppearances = C_TransmogCollection.GetCategoryAppearances(slotInfo.cat)
            if catAppearances then
                for _, app in ipairs(catAppearances) do
                    if app.isCollected then
                        local sources = C_TransmogCollection.GetAppearanceSources(app.visualID)
                        if sources and sources[1] then
                            local source = sources[1]
                            local itemID = source.itemID or source.itemId
                            if itemID then
                                local _, _, _, equipLoc, _, classID, subclassID = GetItemInfoInstant(itemID)
                                -- Back(3), Shirt(5), Tabard(6) accept any armor type
                                -- For other slots: classID 4 = Armor, check subclassID matches player
                                -- subclassID: 1=Cloth, 2=Leather, 3=Mail, 4=Plate, 0=Misc/Cosmetic
                                local armorOK = true
                                if slotInfo.cat ~= 3 and slotInfo.cat ~= 5 and slotInfo.cat ~= 6 then
                                    if classID == 4 and subclassID and subclassID > 0 then
                                        armorOK = (subclassID == playerArmorSubclass)
                                    end
                                end
                                if armorOK then
                                    table.insert(appearances, { itemID = itemID, visualID = app.visualID })
                                end
                            end
                        end
                    end
                end
            end
        end
        
        if #appearances > 0 then
            local randomIndex = math.random(1, #appearances)
            local item = appearances[randomIndex]
            if item.itemID then
                f.previewModel:TryOn("item:" .. item.itemID)
                addToSlot(catToSlotName[slotInfo.cat], item.itemID)
                itemsApplied = itemsApplied + 1
            end
        end
    end
    
    -- Weapon slots: one random Main Hand, one random Off Hand
    local mainHandCats = { 12, 13, 14, 15, 16, 17, 20, 21, 22, 23, 24, 25, 26, 27 }
    local offHandCats = { 18, 19 }
    local function collectItemIDsFromCategories(catList)
        local out = {}
        if not C_TransmogCollection or not C_TransmogCollection.GetCategoryAppearances then return out end
        for _, categoryID in ipairs(catList) do
            local catAppearances = C_TransmogCollection.GetCategoryAppearances(categoryID)
            if catAppearances then
                for _, app in ipairs(catAppearances) do
                    if app.isCollected then
                        local sources = C_TransmogCollection.GetAppearanceSources(app.visualID)
                        local src = sources[1]
                        local id = src and (src.itemID or src.itemId)
                        if id then
                            table.insert(out, id)
                        end
                    end
                end
            end
        end
        return out
    end
    local mainHandItems = collectItemIDsFromCategories(mainHandCats)
    if #mainHandItems > 0 then
        local itemID = mainHandItems[math.random(1, #mainHandItems)]
        f.previewModel:TryOn("item:" .. itemID)
        addToSlot("MainHand", itemID)
        itemsApplied = itemsApplied + 1
    end
    local offHandItems = collectItemIDsFromCategories(offHandCats)
    if #offHandItems > 0 then
        local itemID = offHandItems[math.random(1, #offHandItems)]
        f.previewModel:TryOn("item:" .. itemID)
        addToSlot("OffHand", itemID)
        itemsApplied = itemsApplied + 1
    end
    
    if itemsApplied > 0 then
        f.previewItemName:SetText("|cff00ff00Random " .. playerArmorType .. " Outfit|r")
        -- Rebuild model from livePreviewSlots so every applied slot is shown (avoids TryOn order issues)
        MF:RebuildLivePreview()
    else
        f.previewItemName:SetText("|cffff0000No collected items found|r")
        MF:UpdateSlotButtons()
    end
end

-- Handle profile changes
ETB.EventBus:Register("ETB_PROFILE_CHANGED", function(key, profile)
    if MF.frame and MF.frame:IsShown() then
        -- Debug: Show what profile we're switching to
        local classID = profile and profile.classID or "nil"
        local primaryArmor = ETB.BlizzardData and ETB.BlizzardData:GetPlayerPrimaryArmor() or "unknown"
        ETB:Debug("MainFrame", "Profile Switch: Class " .. tostring(classID) .. " → Primary Armor: " .. tostring(primaryArmor))
        
        -- Clear caches so items are re-filtered for new class
        if ETB.BlizzardData then
            ETB.BlizzardData:ClearCache()
        end
        -- Refresh items with new class filtering
        MF:RefreshItems()
        -- Update the viewing label using CATEGORY_INFO
        local categoryID = ETB.Filtering.filters.category
        local catInfo = ETB.CATEGORY_INFO and ETB.CATEGORY_INFO[categoryID]
        local slotName = catInfo and catInfo.name or "Head"
        MF:UpdateCurrentViewingLabel(slotName)
        -- Update profile selector display
        if ETB.ProfileSelector then
            ETB.ProfileSelector:Update()
        end
    end
end)

ETB:Debug("MainFrame", "MainFrame.lua loaded (V8 UI - Comprehensive Fix)")
