--[[
============================================================================
ETB v11.0 - ModelPreview.lua
============================================================================
3D model preview panel with dressing room functionality.
- Larger preview window
- Shows collection status
- No tooltip overlap
============================================================================
]]--

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

ETB.ModelPreview = {
    frame = nil,
    currentItem = nil,
    rotation = 0,
    isActive = false,  -- Track if preview is showing
}

local Preview = ETB.ModelPreview

-- =============================================================================
-- CREATE
-- =============================================================================

function Preview:Create(parent)
    if self.frame then return self.frame end
    
    -- Larger size: 280x400 (was 200x280)
    local f = CreateFrame("Frame", "ETBModelPreview", parent or UIParent, "BackdropTemplate")
    f:SetSize(280, 420)
    f:SetFrameStrata("TOOLTIP")
    f:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        tile = true, tileSize = 32, edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 }
    })
    
    -- Title bar with item name
    f.titleBg = f:CreateTexture(nil, "BACKGROUND")
    f.titleBg:SetPoint("TOPLEFT", 4, -4)
    f.titleBg:SetPoint("TOPRIGHT", -4, -4)
    f.titleBg:SetHeight(24)
    f.titleBg:SetColorTexture(0.1, 0.1, 0.1, 0.8)
    
    f.title = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    f.title:SetPoint("TOP", 0, -8)
    f.title:SetWidth(260)
    f.title:SetText("Preview")
    
    -- Model (larger: 260x300, was 180x200)
    f.model = CreateFrame("DressUpModel", nil, f)
    f.model:SetSize(260, 300)
    f.model:SetPoint("TOP", 0, -32)
    f.model:SetUnit("player")
    f.model:SetRotation(0)
    f.model:SetPosition(0, 0, 0)
    f.model:Show()
    
    -- Collection status panel
    f.statusPanel = CreateFrame("Frame", nil, f, "BackdropTemplate")
    f.statusPanel:SetSize(260, 45)
    f.statusPanel:SetPoint("TOP", f.model, "BOTTOM", 0, -5)
    f.statusPanel:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 10,
        insets = { left = 2, right = 2, top = 2, bottom = 2 }
    })
    
    -- Collection status text
    f.collectionStatus = f.statusPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    f.collectionStatus:SetPoint("TOP", f.statusPanel, "TOP", 0, -8)
    f.collectionStatus:SetWidth(250)
    f.collectionStatus:SetText("|cff888888Unknown|r")
    
    -- Source info text
    f.sourceInfo = f.statusPanel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    f.sourceInfo:SetPoint("TOP", f.collectionStatus, "BOTTOM", 0, -4)
    f.sourceInfo:SetWidth(250)
    f.sourceInfo:SetText("")
    
    -- Control buttons row
    local btnY = 8
    
    f.rotateLeft = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    f.rotateLeft:SetSize(40, 24)
    f.rotateLeft:SetPoint("BOTTOMLEFT", 10, btnY)
    f.rotateLeft:SetText("◀")
    f.rotateLeft:SetScript("OnClick", function()
        Preview.rotation = Preview.rotation - 0.5
        f.model:SetRotation(Preview.rotation)
    end)
    
    f.resetBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    f.resetBtn:SetSize(60, 24)
    f.resetBtn:SetPoint("BOTTOM", 0, btnY)
    f.resetBtn:SetText("Reset")
    f.resetBtn:SetScript("OnClick", function()
        Preview:Reset()
    end)
    
    f.rotateRight = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    f.rotateRight:SetSize(40, 24)
    f.rotateRight:SetPoint("BOTTOMRIGHT", -10, btnY)
    f.rotateRight:SetText("▶")
    f.rotateRight:SetScript("OnClick", function()
        Preview.rotation = Preview.rotation + 0.5
        f.model:SetRotation(Preview.rotation)
    end)
    
    -- Close button
    f.closeBtn = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    f.closeBtn:SetPoint("TOPRIGHT", 2, 2)
    f.closeBtn:SetScript("OnClick", function()
        Preview:Hide()
    end)
    
    -- Make draggable
    f:EnableMouse(true)
    f:SetMovable(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", f.StopMovingOrSizing)
    
    -- Mouse wheel zoom
    f.model:EnableMouseWheel(true)
    f.model:SetScript("OnMouseWheel", function(self, delta)
        local x, y, z = self:GetPosition()
        z = z + (delta > 0 and 0.15 or -0.15)
        self:SetPosition(x, y, math.max(-1, math.min(2, z)))
    end)
    
    -- Drag to rotate
    f.model:EnableMouse(true)
    -- v15.8.8: bind OnUpdate only while dragging (was every-frame check)
    local function onUpdateRotate(self)
        local x = GetCursorPosition()
        local diff = (x - self.startX) * 0.01
        Preview.rotation = Preview.rotation + diff
        self:SetRotation(Preview.rotation)
        self.startX = x
    end
    f.model:SetScript("OnMouseDown", function(self, button)
        if button == "LeftButton" then
            self.startX = GetCursorPosition()
            self:SetScript("OnUpdate", onUpdateRotate)
        end
    end)
    f.model:SetScript("OnMouseUp", function(self)
        self:SetScript("OnUpdate", nil)
    end)
    
    -- Legacy compatibility
    f.itemName = f.title
    
    f:Hide()
    self.frame = f
    return f
end

-- =============================================================================
-- METHODS
-- =============================================================================

function Preview:PreviewItem(item)
    if not item then return end
    if not self.frame then self:Create() end

    -- v15.8.6: Skip the entire pipeline if the user is re-hovering the
    -- same itemID that's already shown. Rescrolling past the same row
    -- (mouse leaves + re-enters) used to trigger a full Undress + TryOn
    -- on the DressUpModel each time — the dominant lag source while
    -- browsing. Reposition the existing frame to the new cursor pos but
    -- skip the model work.
    local sameItem = self.frame:IsShown()
        and self.currentItem
        and item.itemID
        and self.currentItem.itemID == item.itemID

    self.currentItem = item
    self.isActive = true
    
    -- v15.8.6: Position the hover-preview frame on the LEFT of the cursor
    -- (was right). The item list sits to the RIGHT of the cursor when the
    -- user hovers a row, so a right-anchored preview covered the next rows
    -- the user wanted to compare against. Leftward of the cursor is where
    -- the filter panel lives — plenty of vertical space without obstructing
    -- the comparison rows.
    --
    -- Edge handling: if the cursor is too close to the left screen edge to
    -- fit the 280px-wide preview, fall back to the right side.
    local scale = UIParent:GetEffectiveScale()
    local x, y = GetCursorPosition()
    local screenX = x / scale
    local PREVIEW_WIDTH = 280
    local LEFT_GAP = 20  -- gap between cursor and preview

    self.frame:ClearAllPoints()
    if screenX > PREVIEW_WIDTH + LEFT_GAP + 10 then
        -- Place RIGHT EDGE of preview at (cursor - LEFT_GAP)
        self.frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMLEFT",
            screenX - LEFT_GAP, (y / scale) - 200)
    else
        -- Not enough left-side space; fall back to right of cursor
        self.frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT",
            screenX + LEFT_GAP, (y / scale) - 200)
    end
    
    -- Update item name with quality color
    local color = ETB:GetQualityColor(item.quality or 1)
    self.frame.title:SetText(color .. (item.name or "Loading...") .. "|r")
    
    -- Update collection status
    self:UpdateCollectionStatus(item)
    
    -- Show frame (this will hide the game tooltip)
    self.frame:Show()
    
    -- Hide the default tooltip since we're showing our preview
    GameTooltip:Hide()
    
    if not item.itemID then
        ETB:Debug("ModelPreview", "No itemID for preview")
        return
    end

    -- v15.8.6: When rescrolling past the same row, skip the expensive
    -- Undress + TryOn — the model already shows the right item. The
    -- position was updated above so the user sees the preview track
    -- the cursor without the per-row graphics churn.
    if sameItem then return end

    -- Reset model (avoid SetUnit which causes graphics reset - just undress)
    self.frame.model:Undress()
    self.frame.model:SetRotation(self.rotation or 0)
    self.frame.model:SetPosition(0, 0, 0)
    
    -- Helper to perform the actual TryOn
    local function doTryOn()
        if self.currentItem ~= item then return end
        
        local itemLink = item.link
        if not itemLink or itemLink == "" then
            if ETB.RequestItemInfoThrottled then
                ETB:RequestItemInfoThrottled(item.itemID)
            end
        end
        
        local tryOnArg
        if itemLink and itemLink ~= "" and itemLink:match("^|c%x%x%x%x%x%x%x%x|Hitem:") then
            tryOnArg = itemLink
        else
            tryOnArg = "item:" .. item.itemID
        end
        
        local success, err = pcall(function()
            self.frame.model:TryOn(tryOnArg)
        end)
        
        if success then
            ETB:Debug("ModelPreview", string.format("TryOn success for item %d", item.itemID))
        else
            ETB:Debug("ModelPreview", string.format("TryOn failed for item %d: %s", item.itemID, tostring(err)))
            if ETB.RequestItemInfoThrottled then
                ETB:RequestItemInfoThrottled(item.itemID)
            end
        end
    end
    
    if ETB.QueueWork then
        ETB:QueueWork("modelPreview.tryOn", function() doTryOn(); return true end, { pauseInCombat = true, startupIdleOptions = { ignoreQuietWindow = true } })
    else
        C_Timer.After(0.1, doTryOn)
    end
end

function Preview:UpdateCollectionStatus(item)
    if not self.frame or not item then return end
    
    -- Check if collected
    local isCollected = item.isCollected
    local sourceText = ""
    
    if isCollected then
        self.frame.collectionStatus:SetText(ETB.Style:StatusText(true, "Collected"))
    else
        self.frame.collectionStatus:SetText(ETB.Style:StatusText(false, "Not Collected"))
    end
    
    -- Get source info from SourceMapper (same data as tooltip/modal)
    if item.itemID and ETB.SourceMapper and ETB.SourceMapper.initialized then
        local source = ETB.SourceMapper:GetPrimarySource(item.itemID)
        if source then
            local parts = {}
            if source.sourceName and source.sourceName ~= "Unknown" then
                parts[#parts + 1] = source.sourceName
            end
            if source.sourceLocation and source.sourceLocation ~= "" then
                parts[#parts + 1] = source.sourceLocation
            end
            if #parts > 0 then
                local color = isCollected and "|cff888888" or "|cffffd100"
                sourceText = color .. "From: " .. table.concat(parts, " - ") .. "|r"
            end
        end
    end
    -- Fallback to item.dropSource if SourceMapper unavailable
    if sourceText == "" and item.dropSource and item.dropSource ~= "" then
        local color = isCollected and "|cff888888" or "|cffffd100"
        sourceText = color .. "From: " .. item.dropSource .. "|r"
    end
    
    self.frame.sourceInfo:SetText(sourceText)
end

function Preview:Reset()
    if not self.frame then return end
    
    self.rotation = 0
    -- Avoid SetUnit which causes graphics reset - just undress
    self.frame.model:Undress()
    self.frame.model:SetRotation(0)
    self.frame.model:SetPosition(0, 0, 0)
    self.frame.title:SetText("Preview")
    self.frame.collectionStatus:SetText("|cff888888Unknown|r")
    self.frame.sourceInfo:SetText("")
    self.currentItem = nil
end

function Preview:Show()
    if not self.frame then self:Create() end
    self.frame:Show()
    self.isActive = true
end

function Preview:Hide()
    if self.frame then 
        self.frame:Hide() 
    end
    self.isActive = false
    self.currentItem = nil
end

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

function Preview:IsActive()
    return self.isActive and self.frame and self.frame:IsShown()
end

function Preview:AttachTo(parent, point, relPoint, x, y)
    if not self.frame then self:Create() end
    self.frame:ClearAllPoints()
    self.frame:SetParent(parent)
    self.frame:SetPoint(point, parent, relPoint, x, y)
end

ETB:Debug("ModelPreview", "ModelPreview.lua loaded")
