--LIBS
local LDB = LibStub("LibDataBroker-1.1")
local DBIcon = LibStub("LibDBIcon-1.0")
local AceDBOptions = LibStub("AceDBOptions-3.0")
StefansLootTracker = LibStub("AceAddon-3.0"):NewAddon("Stefans Loot Tracker", "AceConsole-3.0", "AceEvent-3.0")

local defaults = {
    profile = {
        enabled = true,
        loothist = {}, -- Loot History Database
        rollhist = {}, -- Roll History Database
        chathist = {}, -- Chat History Database
        mailhist = {}, -- Mail History Database
        mailIDhist = {}, -- Mail ID History Database
        qDebug = false,
        rarityFilter = 2,
        excludeItems = "",
        minimap = { hide = false },
        historyWindowPosition = { point = "CENTER", relativePoint = "CENTER", x = 0, y = 0 },
        historyWindowSize = { width = 450, height = 550 }
    },
}

local options = {
    name = "Stefans Loot Tracker",
    handler = StefansLootTracker,
    type = 'group',
    args = {
        enabled = {
            order = 1,
            type = "toggle",
            name = "Enable",
            get = function(info) return StefansLootTracker.db.profile.enabled end,
            set = function(info, value) StefansLootTracker.db.profile.enabled = value end,
        },
        qDebug = {
            order = 2,
            type = "toggle",
            name = "Debug",
            get = function(info) return StefansLootTracker.db.profile.qDebug end,
            set = function(info, value) StefansLootTracker.db.profile.qDebug = value end,
        },
        minimapIcon = {
            order = 3,
            type = "toggle",
            name = "Show Minimap Icon",
            get = function() return not StefansLootTracker.db.profile.minimap.hide end,
            set = function(_, value)
                if value then
                    StefansLootTracker:ShowMinimapIcon()
                else
                    StefansLootTracker:HideMinimapIcon()
                end
            end,
        },
        spacer1 = {
            order = 4,
            type = "description",
            name = " ",  -- A blank space
            width = "full",
        },
        rarityFilter = {
            order = 5,
            type = "select",
            name = "Minimum Loot Rarity",
            desc = "Only track loot of this rarity or higher",
            values = {
                [0] = "Poor (Gray)",
                [1] = "Common (White)",
                [2] = "Uncommon (Green)",
                [3] = "Rare (Blue)",
                [4] = "Epic (Purple)",
                [5] = "Legendary (Orange)",
            },
            get = function(info) return StefansLootTracker.db.profile.rarityFilter end,
            set = function(info, value) StefansLootTracker.db.profile.rarityFilter = value end,
        },
        spacer2 = {
            order = 6,
            type = "description",
            name = " ",  -- A blank space
            width = "full",
        },
        excludeItems = {
            order = 7,
            type = "input",
            name = "Exclude Items",
            desc = "Enter a comma-separated list of item names to exclude from logging.",
            get = function(info) return StefansLootTracker.db.profile.excludeItems end,
            set = function(info, value) StefansLootTracker.db.profile.excludeItems = value end,
            width = "full",
            multiline = 3,
        },
        spacer3 = {
            order = 8,
            type = "description",
            name = " ",  -- A blank space
            width = "full",
        },
        clearLootHistory = {
            order = 9,
            type = "execute",
            name = "Clear Loot History",
            desc = "Clear all recorded loot history",
            func = "ClearLootHistory",
            confirm = true,
            confirmText = "Are you sure you want to clear all loot history? This action cannot be undone."
        },
        clearRollHistory = {
            order = 10,
            type = "execute",
            name = "Clear Roll History",
            desc = "Clear all recorded roll history",
            func = "ClearRollHistory",
            confirm = true,
            confirmText = "Are you sure you want to clear all roll history? This action cannot be undone."
        },
        clearChatHistory = {
            order = 11,
            type = "execute",
            name = "Clear Chat History",
            desc = "Clear all recorded chat history",
            func = "ClearChatHistory",
            confirm = true,
            confirmText = "Are you sure you want to clear all chat history? This action cannot be undone."
        },
        clearMailHistory = {
            order = 12,
            type = "execute",
            name = "Clear Mail History",
            desc = "Clear all recorded mail history",
            func = "ClearMailHistory",
            confirm = true,
            confirmText = "Are you sure you want to clear all mail history? This action cannot be undone."
        },
    },
}

local pendingMailInfo = {
    subject = nil,
    body = nil,
    recipient = nil,
    money = nil,
    cod = nil,
    items = {} -- Initialize items as an empty table
}

function StefansLootTracker:OnInitialize()
    -- Called when the addon is loaded
    self.db = LibStub("AceDB-3.0"):New("StefansLootTrackerDB", defaults, true)
    
    -- Create profile options
    local profileOptions = AceDBOptions:GetOptionsTable(self.db)

    LibStub("AceConfig-3.0"):RegisterOptionsTable("StefansLootTracker", options, {"StefansLootTracker", "slt"})
    
    -- Register the profile options table
    LibStub("AceConfig-3.0"):RegisterOptionsTable("StefansLootTracker_Profiles", profileOptions)
    
    -- Create the options panels
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("StefansLootTracker", "Stefans Loot Tracker")
    
    -- Create a separate panel for profiles
    self.profilesFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("StefansLootTracker_Profiles", "Profiles", "Stefans Loot Tracker")

    self:RegisterChatCommand("slt", "ChatCommand")

    -- Create the LDB object
    local LDB_Object = LDB:NewDataObject("StefansLootTracker", {
        type = "data source",
        text = "Stefans Loot Tracker",
        icon = "Interface\\Icons\\INV_Misc_Bag_08",
        OnClick = function(_, button)
            if button == "LeftButton" then
                self:ToggleHistoryWindow()
            elseif button == "RightButton" then
                self:OpenOptions()
            end
        end,
        OnTooltipShow = function(tooltip)
            tooltip:AddLine("Stefans Loot Tracker")
            tooltip:AddLine("Left-click to toggle the history window")
            tooltip:AddLine("Right-click to open settings")
        end,
    })

    -- Register the LDB object with LibDBIcon
    DBIcon:Register("StefansLootTracker", LDB_Object, self.db.profile.minimap)

    -- Ensure the loot history window is closed by default
    if not self.historyFrame then
        self:CreateHistoryWindow()
    end
    self.historyFrame:Hide()
    self.isHistoryWindowOpen = false

    -- Register callbacks for profile changes
    self.db.RegisterCallback(self, "OnProfileChanged", "RefreshConfig")
    self.db.RegisterCallback(self, "OnProfileCopied", "RefreshConfig")
    self.db.RegisterCallback(self, "OnProfileReset", "RefreshConfig")

    self:ApplySettings()
end

function StefansLootTracker:OnEnable()
    -- Called when the addon is enabled
    self:RegisterEvent("CHAT_MSG_PARTY")
    self:RegisterEvent("CHAT_MSG_PARTY_LEADER")
    self:RegisterEvent("CHAT_MSG_RAID")
    self:RegisterEvent("CHAT_MSG_RAID_LEADER")
    self:RegisterEvent("CHAT_MSG_RAID_WARNING")
    self:RegisterEvent("CHAT_MSG_SYSTEM")
    self:RegisterEvent("CHAT_MSG_LOOT")
    self:RegisterEvent("MAIL_SHOW")
    self:RegisterEvent("MAIL_INBOX_UPDATE")
    self:RegisterEvent("MAIL_SEND_SUCCESS")
    self:RegisterEvent("MAIL_CLOSED")

    -- Add hooks for mail sending
    if SendMailFrame then
        -- Hook the send button if it exists
        if SendMailMailButton then
            SendMailMailButton:HookScript("PreClick", function() self:PrepareMail() end)
        end
        
        -- Also hook the mail frame's send function as a backup
        hooksecurefunc("SendMail", function()
            self:PrepareMail()
        end)
    end

    if not self.historyFrame then
        self:CreateHistoryWindow()
    end
end

function StefansLootTracker:OnDisable()
    -- Called when the addon is disabled
    self:UnregisterEvent("CHAT_MSG_PARTY")
    self:UnregisterEvent("CHAT_MSG_PARTY_LEADER")
    self:UnregisterEvent("CHAT_MSG_RAID")
    self:UnregisterEvent("CHAT_MSG_RAID_LEADER")
    self:UnregisterEvent("CHAT_MSG_RAID_WARNING")
    self:UnregisterEvent("CHAT_MSG_SYSTEM")
    self:UnregisterEvent("CHAT_MSG_LOOT")
    self:UnregisterEvent("MAIL_SHOW")
    self:UnregisterEvent("MAIL_INBOX_UPDATE")
    self:UnregisterEvent("MAIL_SEND_SUCCESS")
    self:UnregisterEvent("MAIL_CLOSED")

    if self.historyFrame then
        self.historyFrame:Hide()
    end

    self.isHistoryWindowOpen = false

    self.currentSearchTerm = nil
end

function StefansLootTracker:debugPrint(message)
    if self.db.profile.qDebug then
        print("Debug: " .. message)
    end
end

function StefansLootTracker:getRarity(str)
    if not str or type(str) ~= "string" or str == "" then
        print("Error: Received an invalid loot string.")
        return 0
    end

    self:debugPrint("Received loot string: " .. str)

    local rarityHex = string.match(str, "|c(%x+)|H")
    local rarity = self:getRarityFromHex(rarityHex)

    return rarity
end

function StefansLootTracker:getRarityFromHex(hex)
    local rarityMap = {
        ["ff9d9d9d"] = 0,
        ["ffffffff"] = 1,
        ["ff1eff00"] = 2,
        ["ff0070dd"] = 3,
        ["ffa335ee"] = 4,
        ["ffff8000"] = 5
    }
    return rarityMap[hex] or 0  -- Default to 0 (Poor) if hex is not found
end

function StefansLootTracker:matchExclude(itemName)
    local excludeItems = self.db.profile.excludeItems
    if excludeItems and excludeItems ~= "" then
        local excludedItemsList = {strsplit(",", excludeItems)}
        if #excludedItemsList == 0 then
            return false  -- List is empty, so no items to exclude.
        end
        for _, excludedItem in ipairs(excludedItemsList) do
            local trimmedExcludedItem = strtrim(excludedItem):lower()
            if itemName:lower():match(trimmedExcludedItem) then
                return true  -- Item matches something in the exclusion list.
            end
        end
    end
    return false  -- No match found or list is empty.
end

function StefansLootTracker:CHAT_MSG_PARTY(event, arg1, arg2)
    self:debugPrint("CHAT_MSG_PARTY event received")
    self:debugPrint("arg1: " .. tostring(arg1))
    self:debugPrint("arg2: " .. tostring(arg2))

    message = tostring(arg2) .. ": " .. tostring(arg1)

    if not self.db.profile.enabled then return end
    if self:matchExclude(message) then return end
    self:HandleChatMessage(message)
end

function StefansLootTracker:CHAT_MSG_PARTY_LEADER(event, arg1, arg2)
    self:debugPrint("CHAT_MSG_PARTY_LEADER event received")
    self:debugPrint("arg1: " .. tostring(arg1))
    self:debugPrint("arg2: " .. tostring(arg2))

    message = tostring(arg2) .. ": " .. tostring(arg1)

    if not self.db.profile.enabled then return end
    if self:matchExclude(message) then return end
    self:HandleChatMessage(message)
end

function StefansLootTracker:CHAT_MSG_RAID(event, arg1, arg2)
    self:debugPrint("CHAT_MSG_RAID event received")
    self:debugPrint("arg1: " .. tostring(arg1))
    self:debugPrint("arg2: " .. tostring(arg2))

    message = tostring(arg2) .. ": " .. tostring(arg1)

    if not self.db.profile.enabled then return end
    if self:matchExclude(message) then return end
    self:HandleChatMessage(message)
end

function StefansLootTracker:CHAT_MSG_RAID_LEADER(event, arg1, arg2)
    self:debugPrint("CHAT_MSG_RAID_LEADER event received")
    self:debugPrint("arg1: " .. tostring(arg1))
    self:debugPrint("arg2: " .. tostring(arg2))

    message = tostring(arg2) .. ": " .. tostring(arg1)

    if not self.db.profile.enabled then return end
    if self:matchExclude(message) then return end
    self:HandleChatMessage(message)
end

function StefansLootTracker:CHAT_MSG_RAID_WARNING(event, arg1, arg2)
    self:debugPrint("CHAT_MSG_RAID_WARNING event received")
    self:debugPrint("arg1: " .. tostring(arg1))
    self:debugPrint("arg2: " .. tostring(arg2))

    message = tostring(arg2) .. ": " .. tostring(arg1)

    if not self.db.profile.enabled then return end
    if self:matchExclude(message) then return end
    self:HandleChatMessage(message)
end

function StefansLootTracker:CHAT_MSG_SYSTEM(event, arg1)
    self:debugPrint("CHAT_MSG_SYSTEM event received")
    self:debugPrint("arg1: " .. tostring(arg1))

    if not self.db.profile.enabled then return end
    if self:matchExclude(arg1) then return end
    self:HandleChatRollMessage(arg1)
end

function StefansLootTracker:CHAT_MSG_LOOT(event, arg1)
    self:debugPrint("CHAT_MSG_LOOT event received")
    self:debugPrint("arg1: " .. tostring(arg1))

    if not self.db.profile.enabled then return end
    if self:matchExclude(arg1) then return end
    if self:matchLootMessage(arg1) then self:HandleLootMessage(arg1) end
    if self:matchRollMessage(arg1) then self:HandleRollMessage(arg1) end
end

function StefansLootTracker:MAIL_SHOW()
    self:debugPrint("Mailbox opened")
    if not self.db.profile.enabled then return end
    
    -- Wait a brief moment for mail data to be available
    C_Timer.After(0.5, function()
        self:ScanInbox()
    end)
end

function StefansLootTracker:MAIL_INBOX_UPDATE()
    self:debugPrint("Mail inbox updated")
    if not self.db.profile.enabled then return end
    
    -- Wait a brief moment for mail data to be available
    C_Timer.After(0.5, function()
        self:ScanInbox()
    end)
end

function StefansLootTracker:MAIL_SEND_SUCCESS()
    self:debugPrint("Mail sent successfully")
    if not self.db.profile.enabled then return end
    
    if not pendingMailInfo.recipient then 
        self:debugPrint("No pending mail information available")
        return 
    end
    
    local player = UnitName("player")
    local timestamp = date("%Y-%m-%d %H:%M:%S")
    
    -- Store main mail entry
    table.insert(self.db.profile.mailhist, {
        timestamp = timestamp,
        message = string.format("Mail sent to: %s from: %s", pendingMailInfo.recipient, player)
    })
    
    if pendingMailInfo.subject and pendingMailInfo.subject ~= "" then
        table.insert(self.db.profile.mailhist, {
            timestamp = timestamp,
            message = string.format(" - Subject: %s", pendingMailInfo.subject)
        })
    end
    
    if pendingMailInfo.body and pendingMailInfo.body ~= "" then
        table.insert(self.db.profile.mailhist, {
            timestamp = timestamp,
            message = string.format(" - Body: %s", pendingMailInfo.body)
        })
    end
    
    -- Add COD amount if present
    if pendingMailInfo.cod and pendingMailInfo.cod > 0 then
        local gold = math.floor(pendingMailInfo.cod / 10000)
        local silver = math.floor((pendingMailInfo.cod % 10000) / 100)
        local copper = pendingMailInfo.cod % 100
        table.insert(self.db.profile.mailhist, {
            timestamp = timestamp,
            message = string.format(" - COD Amount: %dg %ds %dc", gold, silver, copper)
        })
    end
    
    -- Add stored attachments with item links
    if #pendingMailInfo.items > 0 then
        for _, itemLink in ipairs(pendingMailInfo.items) do
            table.insert(self.db.profile.mailhist, {
                timestamp = timestamp,
                message = string.format(" - Item: %s", itemLink)
            })
        end
    end
    
    -- Add money amount if present
    if pendingMailInfo.money and pendingMailInfo.money > 0 then
        local gold = math.floor(pendingMailInfo.money / 10000)
        local silver = math.floor((pendingMailInfo.money % 10000) / 100)
        local copper = pendingMailInfo.money % 100
        table.insert(self.db.profile.mailhist, {
            timestamp = timestamp,
            message = string.format(" - Money: %dg %ds %dc", gold, silver, copper)
        })
    end
    
    -- Clear pending mail info
    wipe(pendingMailInfo)
    
    self:debugPrint("Logged sent mail without ID tracking")
end

function StefansLootTracker:MAIL_CLOSED()
    -- Reset all fields to their initial state
    pendingMailInfo.subject = nil
    pendingMailInfo.body = nil
    pendingMailInfo.recipient = nil
    pendingMailInfo.money = nil
    pendingMailInfo.cod = nil
    
    -- Ensure items table exists before wiping
    if pendingMailInfo.items then
        wipe(pendingMailInfo.items)
    else
        pendingMailInfo.items = {}
    end
    
    self:debugPrint("Cleared pending mail info")
end

function StefansLootTracker:ScanInbox()
    local numItems = GetInboxNumItems()
    if numItems == 0 then return end
    
    local playerName = UnitName("player")
    if not playerName then
        playerName = "UnknownRecipient"
    end
    
    for i = 1, numItems do
        -- Get mail header info
        local packageIcon, stationeryIcon, sender, subject, money, CODAmount, daysLeft, hasItem, wasRead, wasReturned, textCreated, canReply, isGM = GetInboxHeaderInfo(i)
        
        -- Only process unread mail
        if not wasRead then
            local bodyText = select(3, GetInboxText(i)) or ""
            
            -- Handle the case where bodyText is "136860" when it's actually empty
            if tostring(bodyText) == "136860" or bodyText == "136860" or bodyText == 13660 then
                bodyText = ""
            end
            
            local timestamp = date("%Y-%m-%d %H:%M:%S")
            
            -- Log the mail information
            table.insert(self.db.profile.mailhist, {
                timestamp = timestamp,
                message = string.format("Mail received to: %s from: %s", playerName, sender)
            })
            
            if subject and subject ~= "" then
                table.insert(self.db.profile.mailhist, {
                    timestamp = timestamp,
                    message = string.format(" - Subject: %s", subject)
                })
            end
            
            if bodyText ~= "" then
                table.insert(self.db.profile.mailhist, {
                    timestamp = timestamp,
                    message = string.format(" - Body: %s", bodyText)
                })
            end
            
            if CODAmount and CODAmount > 0 then
                local gold = math.floor(CODAmount / 10000)
                local silver = math.floor((CODAmount % 10000) / 100)
                local copper = CODAmount % 100
                table.insert(self.db.profile.mailhist, {
                    timestamp = timestamp,
                    message = string.format(" - COD Amount: %dg %ds %dc", gold, silver, copper)
                })
            end
            
            if hasItem then
                for j = 1, ATTACHMENTS_MAX_RECEIVE do
                    local name, itemID, texture, count, quality = GetInboxItem(i, j)
                    local itemLink = GetInboxItemLink(i, j)
                    
                    if itemLink or name then
                        local message = " - Item: " .. (itemLink or name)
                        if count and count > 1 then
                            message = message .. string.format(" x%d", count)
                        end
                        table.insert(self.db.profile.mailhist, {
                            timestamp = timestamp,
                            message = message
                        })
                    end
                end
            end
            
            if money and money > 0 then
                local gold = math.floor(money / 10000)
                local silver = math.floor((money % 10000) / 100)
                local copper = money % 100
                table.insert(self.db.profile.mailhist, {
                    timestamp = timestamp,
                    message = string.format(" - Money: %dg %ds %dc", gold, silver, copper)
                })
            end
            
            -- Mark the mail as read
            CheckInbox()  -- Ensure inbox data is current
            
            self:debugPrint("Logged new unread mail from: " .. (sender or "Unknown"))
        end
    end
end

function StefansLootTracker:PrepareMail()
    if not self.db.profile.enabled then return end
    
    -- Check if mail frame exists and is shown
    if not SendMailFrame or not SendMailFrame:IsShown() then return end
    
    -- Ensure items table exists
    if not pendingMailInfo.items then
        pendingMailInfo.items = {}
    end
    
    -- Use safe methods to get mail information
    pendingMailInfo.subject = SendMailSubjectEditBox and SendMailSubjectEditBox:GetText() or ""
    pendingMailInfo.body = SendMailBodyEditBox and SendMailBodyEditBox:GetText() or ""
    pendingMailInfo.recipient = SendMailNameEditBox and SendMailNameEditBox:GetText() or ""
    pendingMailInfo.money = GetSendMailMoney and GetSendMailMoney() or 0
    pendingMailInfo.cod = GetSendMailCOD and GetSendMailCOD() or 0
    
    -- Clear previous items safely
    wipe(pendingMailInfo.items)
    
    -- Store attachment information with item links
    if GetSendMailItem then
        for i = 1, ATTACHMENTS_MAX_SEND do
            local itemName, itemID, itemTexture, stackCount, quality = GetSendMailItem(i)
            if itemName then
                -- Get the item link instead of just the name
                local itemLink = GetSendMailItemLink(i)
                if itemLink then
                    table.insert(pendingMailInfo.items, itemLink)
                else
                    -- Fallback to name if link isn't available
                    table.insert(pendingMailInfo.items, itemName)
                end
            end
        end
    end
    
    self:debugPrint("Mail information prepared")
end

function StefansLootTracker:HasProcessedMail(mailID)
    for _, processedID in ipairs(self.db.profile.mailIDhist) do
        if processedID == mailID then
            return true
        end
    end
    return false
end

function StefansLootTracker:ChatCommand(input)
    if not input or input:trim() == "" then
        print("Stefans Loot Tracker commands:")
        print("/slt settings  - Open Settings")
        print("/slt history   - Open History Window")
        print("/slt lootclear - Clear Loot History")
        print("/slt rollclear - Clear Roll History")
        print("/slt chatclear - Clear Chat History")
        print("/slt mailclear - Clear Mail History")
    elseif input:trim() == "settings" then
        self:OpenOptions()
    elseif input:trim() == "history" then
        self:ToggleHistoryWindow()
    elseif input:trim() == "lootclear" then
        self:ClearLootHistory()
    elseif input:trim() == "rollclear" then
        self:ClearRollHistory()
    elseif input:trim() == "chatclear" then
        self:ClearChatHistory()
    elseif input:trim() == "mailclear" then
        self:ClearMailHistory()
    else
        LibStub("AceConfigCmd-3.0"):HandleCommand("slt", "StefansLootTracker", input)
    end
end

function StefansLootTracker:ShowMinimapIcon()
    DBIcon:Show("StefansLootTracker")
    self.db.profile.minimap.hide = false
end

function StefansLootTracker:HideMinimapIcon()
    DBIcon:Hide("StefansLootTracker")
    self.db.profile.minimap.hide = true
end

function StefansLootTracker:OpenOptions()
    if Settings and Settings.OpenToCategory then
        -- Open the addon's category in the Settings panel
        Settings.OpenToCategory(self.name)
        
        -- If there's still a need to "focus" the correct category, you might add a slight delay
        C_Timer.After(0.1, function()
            Settings.OpenToCategory(self.name)
        end)
    else
        -- Fallback for older versions or if Settings is not available
        print("StefansChatHistory: Unable to open settings. Please access addon options through the game's interface options.")
        
        -- Attempt to use the old method as a last resort
        if InterfaceOptionsFrame_OpenToCategory then
            InterfaceOptionsFrame_OpenToCategory(self.optionsFrame)
            InterfaceOptionsFrame_OpenToCategory(self.optionsFrame) -- Call twice to workaround a Blizzard UI bug
        end
    end
end

function StefansLootTracker:RefreshConfig()
    self:Print("Profile changed. Refreshing configuration.")

    -- Update minimap icon visibility
    if self.db.profile.minimap.hide then
        self:HideMinimapIcon()
    else
        self:ShowMinimapIcon()
    end
    
    self:UpdateHistoryIfOpen()

    -- Refresh any open config panels
    LibStub("AceConfigRegistry-3.0"):NotifyChange("StefansLootTracker")

    -- Re-apply settings from the new profile
    self:ApplySettings()
end

function StefansLootTracker:ApplySettings()
    -- Apply settings from the current profile
    if self.db.profile.enabled then
        self:OnEnable()
    else
        self:OnDisable()
    end
    
    -- Apply Loot History window position and size if the window exists
    if self.historyFrame then
        local pos = self.db.profile.historyWindowPosition
        local size = self.db.profile.historyWindowSize
        self.historyFrame:ClearAllPoints()
        self.historyFrame:SetPoint(pos.point, UIParent, pos.relativePoint, pos.x, pos.y)
        self.historyFrame:SetSize(size.width, size.height)
    end
end