-- Window functions

StefansLootTracker.loadedHistoryStartIndex = 1
StefansLootTracker.loadedHistoryEndIndex = StefansLootTracker.itemsHistoryPerLoad or 30
StefansLootTracker.itemsHistoryPerLoad = 30

StefansLootTracker.frameHistoryPool = {}
StefansLootTracker.visibleHistoryFrames = {}
StefansLootTracker.frameHistoryHeight = 15

StefansLootTracker.currentDataSource = "Loot" -- Default data source

function StefansLootTracker:matchLootMessage(msg)
    local lootPatterns = {
        "You receive loot: .+",
        "You receive item: .+",
        "You receive: .+",
        "You create item: .+",
        "You create: .+",
        "(.+) receives loot: .+",
        "(.+) receives item: .+",
        "(.+) receives: .+",
        "(.+) creates item: .+",
        "(.+) creates: .+"
    }

    for _, pattern in ipairs(lootPatterns) do
        if msg:match(pattern) then
            return true
        end
    end
    return false
end

function StefansLootTracker:matchRollMessage(msg)
    local rollPatterns = {
        "(.+) Need Roll .+",
        "(.+) Greed Roll .+",
        "(.+) passed on: .+",
        "(.+) has selected Greed for: .+",
        "(.+) have selected Greed for: .+",
        "(.+) has selected Need for: .+",
        "(.+) have selected Need for: .+",
        "(.+) won: .+"
    }

    for _, pattern in ipairs(rollPatterns) do
        if msg:match(pattern) then
            return true
        end
    end
    return false
end

function StefansLootTracker:HandleLootMessage(lootMessage)
    self:debugPrint("Received loot string: " .. lootMessage)

    -- Remove the specific pattern |HlootHistory:<digit>|h[Loot]|h:
    lootMessage = lootMessage:gsub("|HlootHistory:%d+|h%[Loot%]|h: ", "")

    -- Replace "You" with the character's name
    local playerName = UnitName("player")
    lootMessage = lootMessage:gsub("You", playerName)

    -- Parse the loot message
    local rarity = self:getRarity(lootMessage)

    self:debugPrint("Parsed rarity: " .. (rarity or "Unknown"))

    -- Check if the item meets the rarity filter
    if rarity < self.db.profile.rarityFilter then
        self:debugPrint("LOOT DOESN'T MEET RARITY FILTER")
        return
    end

    -- Get the current timestamp
    local timestamp = date("%Y-%m-%d %H:%M:%S")

    -- Update the data structure
    self.db.profile.loothist = self.db.profile.loothist or {}

    -- Insert a new loot event with its timestamp and modified message
    table.insert(self.db.profile.loothist, {
        message = lootMessage,
        timestamp = timestamp
    })

    -- Update the window only if it's open
    self:UpdateHistoryIfOpen()
end

function StefansLootTracker:HandleRollMessage(rollMessage)
    self:debugPrint("Received roll string: " .. rollMessage)

    -- Remove the specific pattern |HlootHistory:<digit>|h[Loot]|h:
    rollMessage = rollMessage:gsub("|HlootHistory:%d+|h%[Loot%]|h: ", "")

    -- Replace "You" with the character's name
    local playerName = UnitName("player")
    rollMessage = rollMessage:gsub("You", playerName)

    -- Parse the loot message
    local rarity = self:getRarity(rollMessage)

    self:debugPrint("Parsed rarity: " .. (rarity or "Unknown"))

    -- Check if the item meets the rarity filter
    if rarity < self.db.profile.rarityFilter then
        self:debugPrint("ROLL DOESN'T MEET RARITY FILTER")
        return
    end

    -- Get the current timestamp
    local timestamp = date("%Y-%m-%d %H:%M:%S")

    -- Update the data structure
    self.db.profile.rollhist = self.db.profile.rollhist or {}

    -- Insert a new roll event with its timestamp and modified message
    table.insert(self.db.profile.rollhist, {
        message = rollMessage,
        timestamp = timestamp
    })

    -- Update the window only if it's open
    self:UpdateHistoryIfOpen()
end

function StefansLootTracker:HandleChatMessage(chatMessage)
    self:debugPrint("Received chat string: " .. chatMessage)

    -- Parse the chat message
    local rarity = self:getRarity(chatMessage)

    self:debugPrint("Parsed rarity: " .. (rarity or "Unknown"))

    -- Check if the item meets the rarity filter
    if rarity < self.db.profile.rarityFilter then
        self:debugPrint("CHAT DOESN'T MEET RARITY FILTER")
        return
    end

    -- Get the current timestamp
    local timestamp = date("%Y-%m-%d %H:%M:%S")

    -- Update the data structure
    self.db.profile.chathist = self.db.profile.chathist or {}

    -- Insert a new chat event with its timestamp and modified message
    table.insert(self.db.profile.chathist, {
        message = chatMessage,
        timestamp = timestamp
    })

    -- Update the window only if it's open
    self:UpdateHistoryIfOpen()
end

function StefansLootTracker:HandleChatRollMessage(chatrollMessage)
    self:debugPrint("Received roll string: " .. chatrollMessage)

    local rollmatch = chatrollMessage:match("(.+) rolls .*")
    if not rollmatch then
        self:debugPrint("CHAT DOESN'T MATCH ROLL PATTERN")
        return
    end

    -- Get the current timestamp
    local timestamp = date("%Y-%m-%d %H:%M:%S")

    -- Update the data structure
    self.db.profile.chathist = self.db.profile.chathist or {}

    -- Insert a new chatroll event with its timestamp and message
    table.insert(self.db.profile.chathist, {
        message = chatrollMessage,
        timestamp = timestamp
    })

    -- Update the window only if it's open
    self:UpdateHistoryIfOpen()
end

function StefansLootTracker:ClearLootHistory()
    self.db.profile.loothist = {} -- Loot History Database
    self:Print("Loot History has been cleared.")
    if self.historyFrame and self.historyFrame:IsShown() then
        self:UpdateHistoryWindow()  -- Update the loot history window if it's open
    end
end

function StefansLootTracker:ClearRollHistory()
    self.db.profile.rollhist = {} -- Roll History Database
    self:Print("Roll History has been cleared.")
    if self.historyFrame and self.historyFrame:IsShown() then
        self:UpdateHistoryWindow()  -- Update the roll history window if it's open
    end
end

function StefansLootTracker:ClearChatHistory()
    self.db.profile.chathist = {} -- Chat History Database
    self:Print("Chat History has been cleared.")
    if self.historyFrame and self.historyFrame:IsShown() then
        self:UpdateHistoryWindow()  -- Update the chat history window if it's open
    end
end

function StefansLootTracker:ClearMailHistory()
    self.db.profile.mailhist = {} -- Mail History Database
    self.db.profile.mailIDhist = {} -- Mail ID History Database
    self:Print("Mail History has been cleared.")
    if self.historyFrame and self.historyFrame:IsShown() then
        self:UpdateHistoryWindow()  -- Update the chat history window if it's open
    end
end

local searchHistoryDebounceTimer
function StefansLootTracker:HistoryDebouncedSearch(searchHistoryTerm)
    if searchHistoryDebounceTimer then
        searchHistoryDebounceTimer:Cancel()
    end
    searchHistoryDebounceTimer = C_Timer.After(0.5, function()
        self:UpdateHistoryWindow(searchHistoryTerm)
    end)
end

local loadHistoryMoreTimer
function StefansLootTracker:DebouncedHistoryLoadMoreResults()
    if loadHistoryMoreTimer then
        loadHistoryMoreTimer:Cancel()
    end
    loadHistoryMoreTimer = C_Timer.NewTimer(0.2, function()
        self:HistoryLoadMoreResults()
    end)
end

function StefansLootTracker:CreateHistoryWindow()
    -- Check if the frame already exists to avoid creating duplicates
    if self.historyFrame then
        self.historyFrame:Show()
        return
    end

    local f = CreateFrame("Frame", "StefansLootTrackerHistoryFrame", UIParent, "BackdropTemplate")
    local pos = self.db.profile.historyWindowPosition
    local size = self.db.profile.historyWindowSize
    f:SetSize(size.width, size.height)
    f:SetPoint(pos.point, UIParent, pos.relativePoint, pos.x, pos.y)
    f:SetMovable(true)
    f:SetResizable(true)
    f:SetResizeBounds(300, 200) -- Set minimum size
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", function()
        f:StopMovingOrSizing()
        StefansLootTracker:SaveWindowPosition()  -- Save position after dragging
    end)
    f:SetHyperlinksEnabled(true)
    f:SetScript("OnHyperlinkClick", ChatFrame_OnHyperlinkShow)
    f:Hide()

    -- Set up key handling
    f:SetPropagateKeyboardInput(true)
    f:SetScript("OnKeyDown", function(self, key)
        if key == "ESCAPE" then
            -- Prevent the default Escape behavior
            self:SetPropagateKeyboardInput(false)
            StefansLootTracker:ToggleHistoryWindow()
            return
        end
        -- Allow all other keys to propagate
        self:SetPropagateKeyboardInput(true)
    end)

    f:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        tile = true,
        tileSize = 32,
        edgeSize = 32,
        insets = { left = 8, right = 8, top = 8, bottom = 8 }
    })

    -- Create the title frame
    local titleFrame = CreateFrame("Frame", nil, f)
    titleFrame:SetSize(f:GetWidth(), 30)
    titleFrame:SetPoint("TOP", f, "TOP", 0, 10)

    local titleText = titleFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    titleText:SetPoint("CENTER", titleFrame, "CENTER")
    titleText:SetText("Stefans Loot Tracker")

    -- Create the close button
    local closeButton = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    closeButton:SetPoint("TOPRIGHT", f, "TOPRIGHT", -5, -5)
    closeButton:SetScript("OnClick", function()
        StefansLootTracker:ToggleHistoryWindow()
    end)

    -- Create data source buttons
    local buttonWidth = 80 -- Slightly reduced width for better fit
    local buttonHeight = 25
    local buttonOffsetY = -30
    local buttonSpacing = 10 -- Space between buttons
    local totalButtonWidth = (buttonWidth * 3) + (buttonSpacing * 2)

    local function CreateDataSourceButton(text, index)
        local button = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
        button:SetSize(buttonWidth, buttonHeight)
        local offsetX = ((index - 2) * (buttonWidth + buttonSpacing))
        button:SetPoint("TOP", f, "TOP", offsetX, buttonOffsetY)
        button:SetText(text)
        button:SetScript("OnClick", function()
            StefansLootTracker:SetDataSource(text)
        end)
        return button
    end

    f.lootButton = CreateDataSourceButton("Loot", 1)
    f.rollButton = CreateDataSourceButton("Roll", 2)
    f.chatButton = CreateDataSourceButton("Chat", 3)
    f.mailButton = CreateDataSourceButton("Mail", 4)

    -- Explicitly highlight the default button
    self:HighlightButton(f[self.currentDataSource:lower() .. "Button"])

    -- Create search box
    local searchBox = CreateFrame("EditBox", nil, f, "InputBoxTemplate")
    searchBox:SetPoint("TOPLEFT", f, "TOPLEFT", 15, -60)
    searchBox:SetPoint("RIGHT", f, "RIGHT", -15, 0)
    searchBox:SetHeight(20)
    searchBox:SetFontObject("GameFontNormal")
    searchBox:SetAutoFocus(false)
    searchBox:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    searchBox:SetTextInsets(22, 10, 2, 2)

    searchBox:SetScript("OnEnterPressed", function(self)
        -- Always open the default chat input when Enter is pressed
        ChatFrame_OpenChat("")
    end)

    local bg = searchBox:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    bg:SetColorTexture(0, 0, 0, 0.1)

    local searchIcon = f:CreateTexture(nil, "ARTWORK")
    searchIcon:SetTexture("Interface\\Common\\UI-Searchbox-Icon")
    searchIcon:SetSize(14, 14)
    searchIcon:SetPoint("LEFT", searchBox, "LEFT", 6, 0)

    searchBox.placeholderText = searchBox:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
    searchBox.placeholderText:SetPoint("LEFT", searchIcon, "RIGHT", 10, 0)
    searchBox.placeholderText:SetText("Search")
    searchBox.placeholderText:SetTextColor(0.6, 0.6, 0.6)

    searchBox:SetScript("OnTextChanged", function(self)
        local text = self:GetText()
        if text == "" then
            self.placeholderText:Show()
        else
            self.placeholderText:Hide()
        end
        StefansLootTracker:HistoryDebouncedSearch(text)
    end)

    searchBox:SetScript("OnEditFocusGained", function(self)
        self.placeholderText:Hide()
    end)

    searchBox:SetScript("OnEditFocusLost", function(self)
        if self:GetText() == "" then
            self.placeholderText:Show()
        end
    end)

    f.searchBox = searchBox

    -- Create the scroll frame
    local scrollFrame = CreateFrame("ScrollFrame", nil, f, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", f, "TOPLEFT", 10, -90)
    scrollFrame:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -30, 10)
    scrollFrame:SetScript("OnVerticalScroll", function(self, offset)
        self.ScrollBar:SetValue(offset)
        StefansLootTracker:UpdateHistoryDisplay()
    end)

    local scrollChild = CreateFrame("Frame", nil, scrollFrame)
    scrollChild:SetWidth(scrollFrame:GetWidth() - 16)  -- Subtract scrollbar width
    scrollFrame:SetScrollChild(scrollChild)
    f.scrollFrame = scrollFrame
    f.scrollChild = scrollChild

    f.noResultsText = f:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
    f.noResultsText:SetPoint("CENTER", f.scrollChild, "CENTER")
    f.noResultsText:SetText("No results found")
    f.noResultsText:Hide()

    f.UpdateNoResults = function(self, noResults)
        if noResults then
            self.noResultsText:Show()
        else
            self.noResultsText:Hide()
        end
    end

    -- Create resize handle
    local resizeHandle = CreateFrame("Button", nil, f)
    resizeHandle:SetPoint("BOTTOMRIGHT")
    resizeHandle:SetSize(16, 16)
    resizeHandle:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
    resizeHandle:SetHighlightTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
    resizeHandle:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Down")
    
    resizeHandle:SetScript("OnMouseDown", function(self, button)
        f:StartSizing("BOTTOMRIGHT")
    end)
    
    resizeHandle:SetScript("OnMouseUp", function(self, button)
        f:StopMovingOrSizing()
        StefansLootTracker:UpdateHistoryLayout()
        StefansLootTracker:SaveWindowPosition()
    end)

    self.historyFrame = f

    self.frameHistoryPool = {}
    self.visibleHistoryFrames = {}

    -- Set default data source
    StefansLootTracker.currentDataSource = StefansLootTracker.currentDataSource or "Loot"

    self:RestoreWindowPosition()
    self:UpdateHistoryWindow()
end

function StefansLootTracker:CreateHistoryEntryFrame(parent)
     local frame = CreateFrame("Frame", nil, parent)
    frame:SetSize(parent:GetWidth() - 10, self.frameHistoryHeight)

    frame.timestampText = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    frame.timestampText:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -3)

    -- Create a Button instead of a FontString for the message
    frame.messageButton = CreateFrame("Button", nil, frame, BackdropTemplateMixin and "BackdropTemplate")
    frame.messageButton:SetPoint("TOPLEFT", frame.timestampText, "TOPRIGHT", 5, 0)
    frame.messageButton:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
    
    frame.messageButton.text = frame.messageButton:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    frame.messageButton.text:SetPoint("TOPLEFT", frame.messageButton, "TOPLEFT")
    frame.messageButton.text:SetPoint("BOTTOMRIGHT", frame.messageButton, "BOTTOMRIGHT")
    frame.messageButton.text:SetJustifyH("LEFT")
    frame.messageButton.text:SetWordWrap(true)

    -- Enable hyperlinks on the button
    frame.messageButton:SetHyperlinksEnabled(true)
    frame.messageButton:SetScript("OnHyperlinkClick", ChatFrame_OnHyperlinkShow)
    frame.messageButton:SetScript("OnHyperlinkEnter", function(self, link, text)
        GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
        GameTooltip:SetHyperlink(link)
        GameTooltip:Show()
    end)
    frame.messageButton:SetScript("OnHyperlinkLeave", function(self)
        GameTooltip:Hide()
    end)

    -- Add click functionality for copying
    frame.messageButton:SetScript("OnClick", function(self, button)
        if button == "LeftButton" then
            local fullText = frame.timestampText:GetText() .. " " .. self.text:GetText()
            StefansLootTracker:CopyText(fullText)
        end
    end)

    -- Add highlight on mouse enter
    frame.messageButton:SetScript("OnEnter", function(self)
        self:SetBackdropColor(0.2, 0.2, 0.2, 0.4)
    end)
    frame.messageButton:SetScript("OnLeave", function(self)
        self:SetBackdropColor(0, 0, 0, 0)
    end)

    -- Set up the backdrop for highlighting
    frame.messageButton:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = nil,
        tile = true, tileSize = 16, edgeSize = 16,
        insets = { left = 0, right = 0, top = 0, bottom = 0 }
    })
    frame.messageButton:SetBackdropColor(0, 0, 0, 0)

    return frame
end

function StefansLootTracker:UpdateHistoryWindow(searchHistoryTerm)
    local frame = self.historyFrame
    if not frame then return end

    searchHistoryTerm = searchHistoryTerm or ""

    if searchHistoryTerm ~= self.lastSearchHistoryTerm then
        self:ResetHistoryLazyLoading()
        self.lastSearchHistoryTerm = searchHistoryTerm
    end

    -- Initialize FilteredHistoryIndices based on the current data source
    self.FilteredHistoryIndices = {}
    local sourceData = self:GetDataSourceData()
    for i = #sourceData, 1, -1 do
        local entry = sourceData[i]
        if searchHistoryTerm == "" or entry.message:lower():find(searchHistoryTerm:lower(), 1, true) then
            table.insert(self.FilteredHistoryIndices, i)
        end
    end

    local totalFilteredHistoryItems = #self.FilteredHistoryIndices
    local displayedHeight = totalFilteredHistoryItems * self.frameHistoryHeight
    frame.scrollChild:SetHeight(displayedHeight)

    self:UpdateHistoryLayout()
    self:UpdateHistoryDisplay()

    frame:UpdateNoResults(totalFilteredHistoryItems == 0)
end

function StefansLootTracker:UpdateHistoryDisplay()
    local frame = self.historyFrame
    local scrollFrame = frame.scrollFrame
    local scrollOffset = scrollFrame:GetVerticalScroll()
    local visibleHeight = scrollFrame:GetHeight()
    
    if not self.FilteredHistoryIndices then
        self:UpdateHistoryWindow()
    end

    local totalItems = #self.FilteredHistoryIndices
    local itemsPerPage = math.floor(visibleHeight / self.frameHistoryHeight)
    
    local startIndex = math.floor(scrollOffset / self.frameHistoryHeight) + 1
    local endIndex = math.min(startIndex + itemsPerPage - 1, totalItems)

    local sourceData = self:GetDataSourceData()

    for i = 1, itemsPerPage do
        local dataIndex = startIndex + i - 1
        local entryFrame = self.visibleHistoryFrames[i] or self:CreateHistoryEntryFrame(frame.scrollChild)
        
        if dataIndex <= totalItems then
            local entry = sourceData[self.FilteredHistoryIndices[dataIndex]]
            
            entryFrame:SetPoint("TOPLEFT", frame.scrollChild, "TOPLEFT", 5, -((dataIndex-1) * self.frameHistoryHeight))
            entryFrame:SetPoint("RIGHT", frame.scrollChild, "RIGHT", -5, 0)
            entryFrame:SetHeight(self.frameHistoryHeight)

            entryFrame.timestampText:SetTextColor(1.00, 0.50, 0.00)
            entryFrame.timestampText:SetText(string.format("[%s]", entry.timestamp))
            entryFrame.messageButton.text:SetText(entry.message)

            entryFrame:Show()
            self.visibleHistoryFrames[i] = entryFrame
        else
            entryFrame:Hide()
        end
    end

    -- Hide any extra frames
    for i = itemsPerPage + 1, #self.visibleHistoryFrames do
        self.visibleHistoryFrames[i]:Hide()
    end

    -- Update scroll child height
    local newHeight = totalItems * self.frameHistoryHeight
    if frame.scrollChild:GetHeight() ~= newHeight then
        frame.scrollChild:SetHeight(newHeight)
        scrollFrame.ScrollBar:SetMinMaxValues(0, max(newHeight - scrollFrame:GetHeight(), 0))
    end
end

function StefansLootTracker:UpdateHistoryLayout()
    local f = self.historyFrame
    if not f then return end

    -- Update button positions
    local buttonWidth = 80 -- Keep this consistent with CreateHistoryWindow
    local buttonSpacing = 10
    local totalButtonWidth = (buttonWidth * 4) + (buttonSpacing * 2)

    local startX = (f:GetWidth() - totalButtonWidth) / 2
    f.lootButton:SetPoint("TOPLEFT", f, "TOPLEFT", startX, -25)
    f.rollButton:SetPoint("TOPLEFT", f.lootButton, "TOPRIGHT", buttonSpacing, 0)
    f.chatButton:SetPoint("TOPLEFT", f.rollButton, "TOPRIGHT", buttonSpacing, 0)
    f.mailButton:SetPoint("TOPLEFT", f.chatButton, "TOPRIGHT", buttonSpacing, 0)
    
    -- Update search box width
    f.searchBox:SetWidth(f:GetWidth() - 30)

    -- Update scroll frame size
    f.scrollFrame:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -30, 10)

    -- Update scroll child width
    f.scrollChild:SetWidth(f.scrollFrame:GetWidth() - 16)  -- Subtract scrollbar width

    -- Update visible frames width
    for _, frame in ipairs(self.visibleHistoryFrames) do
        frame:SetWidth(f.scrollChild:GetWidth() - 10)
        frame.messageButton:SetPoint("RIGHT", frame, "RIGHT", 0, 0)
    end

    -- Update the display
    self:UpdateHistoryDisplay()
end

function StefansLootTracker:HighlightButton(button)
    if button then
        button:SetNormalFontObject("GameFontHighlight")
        button:SetHighlightFontObject("GameFontHighlight")
        button:LockHighlight()
    end
end

function StefansLootTracker:UpdateDataSourceButtons()
    local f = self.historyFrame
    if not f then return end

    local buttons = {f.lootButton, f.rollButton, f.chatButton, f.mailButton}
    for _, button in ipairs(buttons) do
        button:SetNormalFontObject("GameFontNormal")
        button:SetHighlightFontObject("GameFontHighlight")
        button:UnlockHighlight()
    end

    self:HighlightButton(f[self.currentDataSource:lower() .. "Button"])
end

function StefansLootTracker:SetDataSource(source)
    self.currentDataSource = source
    self:UpdateDataSourceButtons()
    self:ResetHistoryLazyLoading()
    self:UpdateHistoryWindow()
end

function StefansLootTracker:GetDataSourceData()
    if self.currentDataSource == "Loot" then
        return self.db.profile.loothist or {}
    elseif self.currentDataSource == "Roll" then
        return self.db.profile.rollhist or {}
    elseif self.currentDataSource == "Chat" then
        return self.db.profile.chathist or {}
    elseif self.currentDataSource == "Mail" then
        return self.db.profile.mailhist or {}
    end
    return {}
end

function StefansLootTracker:ToggleHistoryWindow()
    if not self.historyFrame then
        self:CreateHistoryWindow()
    end

    if self.isHistoryWindowOpen then
        self:SaveWindowPosition()
        self.historyFrame:Hide()
        self.isHistoryWindowOpen = false
        self:ResetHistoryLazyLoading()
    else
        self:RestoreWindowPosition()

        -- Reset the lazy loading and search box
        if self.historyFrame.searchBox then
            self.historyFrame.searchBox:SetText("")
            self.historyFrame.searchBox.placeholderText:Show()
        end

        self:ResetHistoryLazyLoading()

        -- Update the layout and window, then show it
        self:UpdateHistoryLayout()
        self:UpdateHistoryWindow()
        self.historyFrame:Show()
        self.isHistoryWindowOpen = true

        -- Ensure the scroll is at the top
        C_Timer.After(0, function()
            self.historyFrame.scrollFrame:SetVerticalScroll(0)
            self:UpdateHistoryDisplay()
        end)
    end
end

function StefansLootTracker:SaveWindowPosition()
    local f = self.historyFrame
    if f and f:IsShown() then
        local point, _, relativePoint, x, y = f:GetPoint()
        self.db.profile.historyWindowPosition = {
            point = point,
            relativePoint = relativePoint,
            x = x,
            y = y
        }
        self.db.profile.historyWindowSize = {
            width = f:GetWidth(),
            height = f:GetHeight()
        }
    end
end

function StefansLootTracker:RestoreWindowPosition()
    local f = self.historyFrame
    if f then
        local pos = self.db.profile.historyWindowPosition
        local size = self.db.profile.historyWindowSize
        if pos and size then
            f:ClearAllPoints()
            f:SetPoint(pos.point, UIParent, pos.relativePoint, pos.x, pos.y)
            f:SetSize(size.width, size.height)
        end
    end
end

function StefansLootTracker:UpdateHistoryIfOpen()
    if self.isHistoryWindowOpen then
        self:UpdateHistoryWindow()
    end
end

function StefansLootTracker:HistoryOnScroll()
    local frame = self.historyFrame
    local scrollFrame = frame.scrollFrame
    local scrollOffset = scrollFrame:GetVerticalScroll()
    local scrollHeight = scrollFrame:GetHeight()
    local childHeight = frame.scrollChild:GetHeight()

    -- Check if we're near the bottom of the scroll frame
    if (scrollOffset + scrollHeight) >= (childHeight - 20) then
        -- Prevent multiple triggers
        if self.loadingHistoryMore then return end
        self.loadingHistoryMore = true

        -- Load more results
        self:HistoryLoadMoreResults()
    end
end

function StefansLootTracker:HistoryLoadMoreResults()
    if self.loadingHistoryMore then return end
    self.loadingHistoryMore = true

    -- Increase the loaded history range for the next batch
    self.loadedHistoryStartIndex = math.max(self.loadedHistoryStartIndex - self.itemsHistoryPerLoad, 1)

    -- Update the window with the current search term
    self:UpdateHistoryWindow(self.lastSearchHistoryTerm)
    
    self.loadingHistoryMore = false
end

function StefansLootTracker:ResetHistoryLazyLoading()
    self.loadedHistoryStartIndex = 1
    self.loadedHistoryEndIndex = math.min(self.itemsHistoryPerLoad, #self:GetDataSourceData())
    self.FilteredHistoryIndices = nil  -- Reset FilteredHistoryIndices
    self.lastSearchHistoryTerm = nil
end

function StefansLootTracker:CopyText(text)
    -- Create the main frame
    local f = CreateFrame("Frame", "StefansLootTrackerCopyFrame", UIParent, "BackdropTemplate")
    f:SetPoint("CENTER")
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", f.StopMovingOrSizing)
    f:SetHyperlinksEnabled(true)
    f:SetScript("OnHyperlinkClick", ChatFrame_OnHyperlinkShow)

    -- Set frame strata and level to appear on top
    f:SetFrameStrata("FULLSCREEN_DIALOG")
    f:SetFrameLevel(100)

    f:SetBackdrop({
        bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
        edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
        tile = true,
        tileSize = 32,
        edgeSize = 32,
        insets = { left = 8, right = 8, top = 8, bottom = 8 }
    })

    -- Create the title frame
    local titleFrame = CreateFrame("Frame", nil, f)
    titleFrame:SetHeight(30)
    titleFrame:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 10)
    titleFrame:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, 10)

    local titleText = titleFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    titleText:SetPoint("CENTER", titleFrame, "CENTER")
    titleText:SetText("Copy Text")

    -- Create the close button
    local closeButton = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    closeButton:SetPoint("TOPRIGHT", f, "TOPRIGHT", -5, -5)
    closeButton:SetScript("OnClick", function() f:Hide() end)

    -- Create instruction text
    local instructionText = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    instructionText:SetPoint("TOPLEFT", f, "TOPLEFT", 15, -25)
    instructionText:SetText("Press Ctrl+C to copy text. Press Esc to close.")

    -- Create the scroll frame
    local scrollFrame = CreateFrame("ScrollFrame", nil, f, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", instructionText, "BOTTOMLEFT", 0, -10)
    scrollFrame:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -30, 15)

    -- Create the EditBox
    local editBox = CreateFrame("EditBox", nil, scrollFrame)
    editBox:SetMultiLine(true)
    editBox:SetFontObject(ChatFontNormal)
    editBox:SetAutoFocus(false)
    editBox:SetScript("OnEscapePressed", function() f:Hide() end)

    scrollFrame:SetScrollChild(editBox)

    -- Set initial size before setting text
    f:SetSize(300, 200)
    editBox:SetWidth(240)  -- 300 - 60 for initial padding

    -- Set the text after creating the EditBox
    editBox:SetText(text)

    -- Function to adjust frame size based on content
    local function AdjustFrameSize()
        -- Create a temporary font string to measure text dimensions
        local temp = f:CreateFontString(nil, "BACKGROUND", "ChatFontNormal")
        temp:SetText(editBox:GetText())
        
        local width = max(300, min(temp:GetStringWidth() + 60, GetScreenWidth() * 0.8))
        local height = max(200, min(temp:GetStringHeight() + 100, GetScreenHeight() * 0.8))
        
        f:SetSize(width, height)
        editBox:SetWidth(width - 60)
        
        temp:Hide()
    end

    -- Adjust size after setting text
    AdjustFrameSize()

    -- Make the frame closable by pressing Escape
    _G["StefansLootTrackerCopyFrame"] = f
    tinsert(UISpecialFrames, "StefansLootTrackerCopyFrame")

    -- Show the frame
    f:Show()

    -- Ensure the frame stays on top
    f:Raise()

    -- New: Focus the editBox and highlight text after a short delay
    C_Timer.After(0.05, function()
        editBox:SetFocus()
        editBox:HighlightText()
    end)

    -- New: Capture all key presses
    f:SetPropagateKeyboardInput(false)
    f:SetScript("OnKeyDown", function(self, key)
        if IsControlKeyDown() and key == "C" then
            -- Ctrl+C pressed, copy the text
            editBox:GetText() -- This triggers the copy to clipboard in WoW
        elseif key == "ESCAPE" then
            -- ESC pressed, close the frame
            f:Hide()
        else
            -- For all other keys, propagate the input
            self:SetPropagateKeyboardInput(true)
        end
    end)
    f:SetScript("OnKeyUp", function(self)
        -- Reset keyboard input propagation on key up
        self:SetPropagateKeyboardInput(false)
    end)
end