local PT = AhUI
if not PT or not PT.FarmTracker then return end

local FT = PT.FarmTracker
FT.Events = FT.Events or {}
local E = FT.Events
local L = PT.L or {}

local frame
local mailHooksInstalled = false
local pendingMailMoney = 0
local pendingMailItems = {}
local origTakeInboxItem
local origTakeInboxMoney

local function EscapePattern(s)
    return (s:gsub("([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1"))
end

local function BuildAmountPattern(fmt)
    if type(fmt) ~= "string" or fmt == "" then return nil end
    local p = EscapePattern(fmt)
    p = p:gsub("%%%%d", "(%%d+)")
    return p
end

local GOLD_PATTERN   = BuildAmountPattern(GOLD_AMOUNT or "%d Gold")
local SILVER_PATTERN = BuildAmountPattern(SILVER_AMOUNT or "%d Silver")
local COPPER_PATTERN = BuildAmountPattern(COPPER_AMOUNT or "%d Copper")

local function ParseMoney(msg)
    if type(msg) ~= "string" then return 0 end
    local gold = GOLD_PATTERN and tonumber(msg:match(GOLD_PATTERN)) or 0
    local silver = SILVER_PATTERN and tonumber(msg:match(SILVER_PATTERN)) or 0
    local copper = COPPER_PATTERN and tonumber(msg:match(COPPER_PATTERN)) or 0
    return (gold or 0) * 10000 + (silver or 0) * 100 + (copper or 0)
end

local function BuildLootPattern(fmt, withQty)
    if type(fmt) ~= "string" or fmt == "" then return nil end
    local p = EscapePattern(fmt)
    p = p:gsub("%%%%s", "(.+)")
    if withQty then
        p = p:gsub("%%%%d", "(%%d+)")
    end
    return p
end

local PATTERN_LOOT_ITEM_SELF = BuildLootPattern(LOOT_ITEM_SELF, false)
local PATTERN_LOOT_ITEM_SELF_MULTIPLE = BuildLootPattern(LOOT_ITEM_SELF_MULTIPLE, true)

local function ParseLootMessage(msg)
    if type(msg) ~= "string" then return nil end

    local ok, link, qty = pcall(string.match, msg, PATTERN_LOOT_ITEM_SELF_MULTIPLE or "")
    if ok and link and qty then
        return link, tonumber(qty) or 1
    end

    local ok2, linkSingle = pcall(string.match, msg, PATTERN_LOOT_ITEM_SELF or "")
    if ok2 and linkSingle then
        return linkSingle, 1
    end

    return nil
end

local function GetMailItemLinkSafe(index, attachment)
    local link = GetInboxItemLink(index, attachment)
    if link and link ~= "" then
        return link
    end

    local _, itemID = GetInboxItem(index, attachment)
    if itemID and itemID > 0 then
        local _, itemLink = GetItemInfo(itemID)
        return itemLink or ("item:" .. itemID)
    end

    return nil
end

function E:OnLoot(msg)
    local db = FT.Data:GetDB()
    if db.config.enabled == false then return end
    if not FT:IsSourceEnabled("loot") then return end

    if not FT.Data:GetActiveSession() and db.config.autoStartOnLoot then
        FT:StartSession()
    end

    local s = FT:GetActiveSession()
    if s and s.state == "paused" and s.pauseReason == "relog" and db.config.resumeUnfinishedSession ~= false then
        FT:ResumeSession()
    end

    if not FT:IsSessionActive() then return end

    local itemLink, qty = ParseLootMessage(msg)
    if not itemLink then return end
    FT:AddLootItem(itemLink, qty, "loot")
end

function E:OnMoney(msg)
    local db = FT.Data:GetDB()
    if db.config.enabled == false then return end
    if not FT:IsSourceEnabled("money") then return end
    if not FT:IsSessionActive() then return end
    local copper = ParseMoney(msg)
    if copper and copper > 0 then
        FT:AddMoney(copper)
    end
end

function E:InstallMailHooks()
    if mailHooksInstalled then return end
    mailHooksInstalled = true

    origTakeInboxItem = origTakeInboxItem or TakeInboxItem
    origTakeInboxMoney = origTakeInboxMoney or TakeInboxMoney

    TakeInboxItem = function(index, attachment)
        local db = FT.Data:GetDB()
        local trackMail = db.config.enabled ~= false and FT:IsSourceEnabled("mailItems")

        if trackMail and not FT.Data:GetActiveSession() and db.config.autoStartOnLoot == true then
            FT:StartSession()
        end

        local itemLink, itemCount

        if trackMail and FT:IsSessionActive() then
            itemLink = GetMailItemLinkSafe(index, attachment)
            itemCount = select(4, GetInboxItem(index, attachment)) or 1
            itemCount = tonumber(itemCount) or 1
            if itemCount < 1 then itemCount = 1 end
        end

        local result = origTakeInboxItem(index, attachment)

        if trackMail and FT:IsSessionActive() and itemLink then
            pendingMailItems[#pendingMailItems + 1] = {
                itemLink = itemLink,
                quantity = itemCount or 1,
            }
        end

        return result
    end

    TakeInboxMoney = function(index)
        local db = FT.Data:GetDB()
        local trackMail = db.config.enabled ~= false and FT:IsSourceEnabled("mailMoney")

        if trackMail and not FT.Data:GetActiveSession() and db.config.autoStartOnLoot == true then
            FT:StartSession()
        end

        local money = 0

        if trackMail and FT:IsSessionActive() then
            local _, _, _, _, mailMoney = GetInboxHeaderInfo(index)
            money = tonumber(mailMoney) or 0
        end

        local result = origTakeInboxMoney(index)

        if trackMail and FT:IsSessionActive() and money > 0 then
            pendingMailMoney = pendingMailMoney + money
        end

        return result
    end
end

function E:FlushPendingMail()
    local db = FT.Data:GetDB()

    if db.config.enabled == false then
        pendingMailMoney = 0
        wipe(pendingMailItems)
        return
    end

    if not FT:IsSessionActive() then
        pendingMailMoney = 0
        wipe(pendingMailItems)
        return
    end

    if FT:IsSourceEnabled("mailMoney") then
        if pendingMailMoney > 0 then
            FT:AddMailMoney(pendingMailMoney)
            pendingMailMoney = 0
        end
    else
        pendingMailMoney = 0
    end

    if FT:IsSourceEnabled("mailItems") then
        if #pendingMailItems > 0 then
            for _, entry in ipairs(pendingMailItems) do
                FT:AddLootItem(entry.itemLink, entry.quantity, "mail")
            end
            wipe(pendingMailItems)
        end
    else
        wipe(pendingMailItems)
    end
end

function E:OnMailInboxUpdate()
    self:FlushPendingMail()
end

function E:OnCraftResult(data)
    local db = FT.Data:GetDB()
    if db.config.enabled == false then return end
    if not FT:IsSourceEnabled("crafting") then return end
    if not FT:IsSessionActive() then return end
    if type(data) ~= "table" then return end

    local itemID = tonumber(data.itemID) or 0
    local qty = tonumber(data.quantity) or 0
    local link = data.hyperlink

    if itemID <= 0 and (not link or link == "") then
        return
    end

    if qty <= 0 then
        qty = 1
    end

    if (not link or link == "") and itemID > 0 then
        local _, itemLink = GetItemInfo(itemID)
        link = itemLink or ("item:" .. itemID)
    end

    if not link or link == "" then return end

    FT:AddLootItem(link, qty, "crafting")
end

function E:OnLogin()
    local db = FT.Data:GetDB()
    FT.Data:GetCharDB()

    local currentCharKey = FT:GetCurrentCharKey()
    local s = FT:GetActiveSession()

    if s then
        if s.charKey and currentCharKey and s.charKey ~= currentCharKey then
            FT.Data:StopSession()
        elseif db.config.resumeUnfinishedSession ~= false and s.state == "paused" and s.pauseReason == "relog" then
            FT.Data:ResumeSession()
        end
    end

    C_Timer.After(2, function()
        local active = FT:GetActiveSession()
        if active then
            local changed = FT.Data:RefreshPendingPrices(active)
            if changed then
                FT:RefreshUI()
            end
        end
    end)

    FT:RefreshUI()
end

function E:OnLogout()
    local s = FT:GetActiveSession()
    if not s then return end

    if s.state == "active" then
        FT.Data:PauseSession("relog")
    end
end

function E:OnZoneChanged()
    FT.Data:UpdateZone()
    FT:RefreshUI()
end

function E:Register()
    if frame then return end

    frame = CreateFrame("Frame")
    if not frame then return end

    E:InstallMailHooks()

    frame:RegisterEvent("PLAYER_LOGIN")
    frame:RegisterEvent("PLAYER_LOGOUT")
    frame:RegisterEvent("CHAT_MSG_LOOT")
    frame:RegisterEvent("CHAT_MSG_MONEY")
    frame:RegisterEvent("TRADE_SKILL_ITEM_CRAFTED_RESULT")
    frame:RegisterEvent("MAIL_INBOX_UPDATE")
    frame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
    frame:RegisterEvent("ZONE_CHANGED")

    frame:SetScript("OnEvent", function(_, event, ...)
        if event == "PLAYER_LOGIN" then
            E:OnLogin(...)
        elseif event == "PLAYER_LOGOUT" then
            E:OnLogout(...)
        elseif event == "CHAT_MSG_LOOT" then
            E:OnLoot(...)
        elseif event == "CHAT_MSG_MONEY" then
            E:OnMoney(...)
        elseif event == "TRADE_SKILL_ITEM_CRAFTED_RESULT" then
            E:OnCraftResult(...)
        elseif event == "MAIL_INBOX_UPDATE" then
            E:OnMailInboxUpdate(...)
        elseif event == "ZONE_CHANGED_NEW_AREA" or event == "ZONE_CHANGED" then
            E:OnZoneChanged(...)
        end
    end)
end
