-------------------------
-- AiB QoL Features
-- Zusammenstellung praktischer Komfort-Funktionen
-------------------------

AiBQOL = AiBQOL or {}
AiBQOL.misc = AiBQOL.misc or {
    fasterLoot          = true,
    suppressLootWarnings = true,
    autoFillDelete      = true,
    autoSlotKeystone    = true,
    ahCurrentExpansion  = false,
    ahCraftingExpansion = false,
    cancelProfBuffs     = false,
    ignoreDecor         = true,
    durabilityWarning   = true,
    durabilityThreshold = 50,
    autoRepair          = true,
    guildRepair         = true,
}

-------------------------
-- 1. FASTER LOOT
-- Lootet automatisch alle Items sobald ein Loot-Fenster öffnet
-------------------------
local lastLootTime = 0

local function IsLootEnabled()
    return AiBQOL.misc and AiBQOL.misc.fasterLoot
end

local function ShouldAutoLoot()
    local autoLootOn = GetCVarBool("autoLootDefault")
    local modifierHeld = IsModifiedClick("AUTOLOOTTOGGLE")
    if autoLootOn then
        return not modifierHeld
    else
        return modifierHeld
    end
end

local function CollectLoot()
    if not IsLootEnabled() then return end
    if not ShouldAutoLoot() then return end

    local throttle = 0.2
    local now = GetTime()
    if now - lastLootTime < throttle then return end
    lastLootTime = now

    if GetCursorInfo() then return end

    local slotCount = GetNumLootItems()
    for i = 1, slotCount do
        LootSlot(i)
    end
end

local lootFrame = CreateFrame("Frame", "AiBQOL_FasterLoot")
lootFrame:RegisterEvent("PLAYER_LOGIN")
lootFrame:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_LOGIN" then
        if IsLootEnabled() then
            self:RegisterEvent("LOOT_READY")
        end
        self:UnregisterEvent("PLAYER_LOGIN")
        return
    end
    if event == "LOOT_READY" then
        CollectLoot()
    end
end)

-------------------------
-- 2. SUPPRESS LOOT WARNINGS
-- Unterdrückt Loot-Bestätigungs-Dialoge automatisch
-------------------------
local CONFIRMATIONS = {
    CONFIRM_LOOT_ROLL = {
        confirm = "ConfirmLootRoll",
        popup   = "CONFIRM_LOOT_ROLL",
        forwardArgs = true,
    },
    CONFIRM_DISENCHANT_ROLL = {
        confirm = "ConfirmLootRoll",
        popup   = "CONFIRM_LOOT_ROLL",
        forwardArgs = true,
    },
    LOOT_BIND_CONFIRM = {
        confirm     = "ConfirmLootSlot",
        popup       = "LOOT_BIND",
        forwardArgs = true,
        spreadExtra = true,
    },
    MERCHANT_CONFIRM_TRADE_TIMER_REMOVAL = {
        confirm = "SellCursorItem",
    },
    MAIL_LOCK_SEND_ITEMS = {
        confirm     = "RespondMailLockSendItem",
        forwardArgs = true,
        appendTrue  = true,
    },
}

local function IsLootWarningsEnabled()
    return AiBQOL.misc and AiBQOL.misc.suppressLootWarnings
end

local function HandleConfirmation(event, arg1, arg2, ...)
    local entry = CONFIRMATIONS[event]
    if not entry then return end
    local fn = _G[entry.confirm]
    if not fn then return end

    if entry.appendTrue then
        fn(arg1, true)
    elseif entry.forwardArgs then
        fn(arg1, arg2)
    else
        fn()
    end

    if entry.popup then
        if entry.spreadExtra then
            StaticPopup_Hide(entry.popup, ...)
        else
            StaticPopup_Hide(entry.popup)
        end
    end
end

local warningFrame = CreateFrame("Frame", "AiBQOL_LootWarnings")
warningFrame:RegisterEvent("PLAYER_LOGIN")
warningFrame:SetScript("OnEvent", function(self, event, arg1, arg2, ...)
    if event == "PLAYER_LOGIN" then
        if IsLootWarningsEnabled() then
            for evtName in pairs(CONFIRMATIONS) do
                self:RegisterEvent(evtName)
            end
        end
        self:UnregisterEvent("PLAYER_LOGIN")
        return
    end
    HandleConfirmation(event, arg1, arg2, ...)
end)

-------------------------
-- 3. EASY ITEM DESTROY
-- Füllt den Lösch-Dialog automatisch aus
-------------------------
local DELETE_GOOD_ITEM_STR = DELETE_GOOD_ITEM

local function IsDestroyEnabled()
    return AiBQOL.misc and AiBQOL.misc.autoFillDelete
end

local function ResolveItemLinkFromCursor()
    local infoType, id, link = GetCursorInfo()
    if not infoType then return nil end

    if infoType == "battlepet" then
        local name = C_PetJournal and C_PetJournal.GetPetInfoBySpeciesID(id)
        if name and name ~= "" then
            return "|cff0070dd[" .. name .. "]|r"
        end
        return nil
    end

    if infoType == "item" and link then return link end
    return nil
end

local function StripDeleteInstruction(originalText)
    if not originalText then return originalText end
    local deleteGoodStr = DELETE_GOOD_ITEM_STR
    if not deleteGoodStr then return originalText end
    local boundary = string.find(deleteGoodStr, "\n")
    if not boundary then return originalText end
    local instructionBlock = string.sub(deleteGoodStr, boundary)
    local cleanedBlock = string.gsub(instructionBlock, "%%s", "")
    cleanedBlock = strtrim(cleanedBlock)
    if cleanedBlock == "" then return originalText end
    local matchStart = string.find(originalText, cleanedBlock, 1, true)
    if matchStart then
        return strtrim(string.sub(originalText, 1, matchStart - 1))
    end
    return originalText
end

local function OnDeleteHyperlinkEnter(self, linkData, text)
    if not linkData then return end
    local linkType = string.match(linkData, "^(%a+)")
    if linkType == "item" then
        GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
        GameTooltip:SetHyperlink(linkData)
        GameTooltip:Show()
    elseif linkType == "battlepet" and BattlePetToolTip_ShowLink then
        GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
        BattlePetToolTip_ShowLink(linkData)
    end
end

local function OnDeleteHyperlinkLeave(self)
    GameTooltip:Hide()
    if BattlePetTooltip then BattlePetTooltip:Hide() end
end

local DESTROY_DIALOG_NAMES = { "DELETE_ITEM", "DELETE_QUEST_ITEM", "DELETE_GOOD_QUEST_ITEM" }

local function PatchDeleteDialogs()
    for _, name in ipairs(DESTROY_DIALOG_NAMES) do
        local dlg = StaticPopupDialogs[name]
        if dlg then
            dlg.OnHyperlinkEnter = OnDeleteHyperlinkEnter
            dlg.OnHyperlinkLeave = OnDeleteHyperlinkLeave
        end
    end
end

local function HandleDeleteConfirm()
    if not IsDestroyEnabled() then return end

    local popupCount = STATICPOPUP_NUMDIALOGS or 4
    local activePopup, activeEditBox, activeButton

    for i = 1, popupCount do
        local popup = _G["StaticPopup" .. i]
        if popup and popup:IsShown() then
            local editBox = _G["StaticPopup" .. i .. "EditBox"]
            local btn     = _G["StaticPopup" .. i .. "Button1"]
            if editBox and btn then
                activePopup   = popup
                activeEditBox = editBox
                activeButton  = btn
                break
            end
        end
    end

    if not activePopup then return end

    local itemLink = ResolveItemLinkFromCursor()
    local requiresTyping = activeEditBox and activeEditBox:IsShown()

    if activeEditBox then activeEditBox:Hide() end
    if activeButton  then activeButton:Enable() end

    local popupName  = activePopup:GetName()
    local textRegion = popupName and _G[popupName .. "Text"]
    if textRegion and itemLink then
        local currentText = textRegion:GetText() or ""
        local cleanText   = StripDeleteInstruction(currentText)
        if not requiresTyping then
            local extraHeight = itemLink and 32 or 0
            activePopup:SetHeight(activePopup:GetHeight() + extraHeight)
        end
        textRegion:SetText(cleanText .. "\n\n" .. itemLink)
    end
end

local destroyFrame = CreateFrame("Frame", "AiBQOL_EasyItemDestroy")
destroyFrame:RegisterEvent("DELETE_ITEM_CONFIRM")
destroyFrame:RegisterEvent("PLAYER_LOGIN")
destroyFrame:SetScript("OnEvent", function(self, event, arg1)
    if event == "PLAYER_LOGIN" then
        if IsDestroyEnabled() then PatchDeleteDialogs() end
        self:UnregisterEvent("PLAYER_LOGIN")
        return
    end
    if event == "DELETE_ITEM_CONFIRM" then
        HandleDeleteConfirm()
    end
end)

-------------------------
-- 4. AUTO KEYSTONE
-- Setzt den Schlüsselstein automatisch ein
-------------------------
local function TrySlotKeystone()
    local reagentType  = Enum.ItemClass.Reagent
    local keystoneType = Enum.ItemReagentSubclass.Keystone

    for bag = 0, (NUM_BAG_FRAMES or 4) do
        local slots = C_Container.GetContainerNumSlots(bag)
        for slot = 1, slots do
            local itemID = C_Container.GetContainerItemID(bag, slot)
            if itemID then
                local _, _, _, _, _, _, _, _, _, _, _, itemClass, itemSub = C_Item.GetItemInfo(itemID)
                if itemClass == reagentType and itemSub == keystoneType then
                    C_Container.PickupContainerItem(bag, slot)
                    if C_Cursor.GetCursorItem() then
                        C_ChallengeMode.SlotKeystone()
                        return
                    end
                end
            end
        end
    end
end

local keystoneWatcher = CreateFrame("Frame", "AiBQOL_AutoKeystone")
keystoneWatcher:RegisterEvent("ADDON_LOADED")
keystoneWatcher:SetScript("OnEvent", function(self, event, loaded)
    if loaded ~= "Blizzard_ChallengesUI" then return end
    self:UnregisterEvent("ADDON_LOADED")

    local keystoneUI = ChallengesKeystoneFrame
    if not keystoneUI then return end

    keystoneUI:HookScript("OnShow", function()
        local db = AiBQOL.misc
        if not db or not db.autoSlotKeystone then return end
        if C_ChallengeMode.HasSlottedKeystone() then return end
        TrySlotKeystone()
    end)
end)

-------------------------
-- 5. AUCTION HOUSE FILTER
-- Filtert das AH automatisch auf die aktuelle Erweiterung
-------------------------
local ahFrame = CreateFrame("Frame", "AiBQOL_AHFilter")
ahFrame:RegisterEvent("AUCTION_HOUSE_SHOW")
ahFrame:SetScript("OnEvent", function(self, event)
    local db = AiBQOL and AiBQOL.misc
    if not db or not db.ahCurrentExpansion then return end

    C_Timer.After(0, function()
        if AuctionHouseFrame and AuctionHouseFrame.SearchBar then
            local filterBtn = AuctionHouseFrame.SearchBar.FilterButton
            if filterBtn and filterBtn.filters then
                filterBtn.filters[Enum.AuctionHouseFilter.CurrentExpansionOnly] = true
                AuctionHouseFrame.SearchBar:UpdateClearFiltersButton()
            end
        end
    end)
end)

-------------------------
-- 5b. CRAFTING ORDER FILTER
-- Filtert das Crafting-AH automatisch auf die aktuelle Erweiterung
-------------------------
local craftingAhFrame = CreateFrame("Frame", "AiBQOL_CraftingAHFilter")
craftingAhFrame:RegisterEvent("ADDON_LOADED")
craftingAhFrame:SetScript("OnEvent", function(self, event, addonName)
    if addonName ~= "Blizzard_ProfessionsCustomerOrders" then return end
    self:UnregisterEvent("ADDON_LOADED")

    local frame = ProfessionsCustomerOrdersFrame
    if not frame then return end

    frame:HookScript("OnShow", function()
        local db = AiBQOL and AiBQOL.misc
        if not db or not db.ahCraftingExpansion then return end

        C_Timer.After(0, function()
            local b = frame.BrowseOrders
            if not b then return end
            local dropdown = b.SearchBar and b.SearchBar.FilterDropdown
            if not dropdown or not dropdown.filters then return end

            -- Index 3 = Current Expansion Only (via fstack + in-game testing ermittelt)
            dropdown.filters[3] = true
            -- Nur den Button-State aktualisieren, keine Suche auslösen
            b:InitFilterDropdown()
        end)
    end)
end)

-------------------------
-- 6. CANCEL PROFESSION BUFFS
-- Cancelt automatisch unerwünschte Berufs-Buffs und Kostüme
-------------------------
local CANCEL_SPELL_IDS = {
    [394005] = true,  -- Herbalism:      A Cultivator's Colors
    [394006] = true,  -- Mining:         Rockin' Mining Gear
    [394011] = true,  -- Skinning:       Dressed To Kill
    [394016] = true,  -- Inscription:    Artist's Duds
    [394003] = true,  -- Alchemy:        Spark of Madness
    [388658] = true,  -- Blacksmithing:  Suited For Smithing
    [394008] = true,  -- Enchanting:     A Looker's Charm
    [394007] = true,  -- Engineering:    Ready To Build
    [394015] = true,  -- Jewelcrafting:  An Eye For Shine
    [394001] = true,  -- Leatherworking: Sculpting Leather Finery
    [391312] = true,  -- Tailoring:      Wrapped Up In Weaving
    [391775] = true,  -- Cooking:        What's Cookin', Good Lookin'?
    -- [394009] = true,  -- Fishing: nicht aktiv (cancelt den Angelwurf)
}

local cancelFrame = CreateFrame("Frame", "AiBQOL_CancelBuffs")
cancelFrame:RegisterEvent("PLAYER_LOGIN")
cancelFrame:SetScript("OnEvent", function(self, event, unit)
    if event == "PLAYER_LOGIN" then
        if AiBQOL.misc and AiBQOL.misc.cancelProfBuffs then
            self:RegisterEvent("UNIT_AURA")
        end
        self:UnregisterEvent("PLAYER_LOGIN")
        return
    end

    if event == "UNIT_AURA" then
        if unit ~= "player" then return end
        if not (AiBQOL.misc and AiBQOL.misc.cancelProfBuffs) then return end

        -- Aura-Daten sind in Instanzen durch WoW gesperrt
        local _, instanceType = IsInInstance()
        if instanceType ~= "none" then return end

        pcall(function()
            for i = 1, 40 do
                local aura = C_UnitAuras.GetAuraDataByIndex("player", i)
                if not aura then break end
                local spellId = aura.spellId
                if spellId and CANCEL_SPELL_IDS[spellId] then
                    CancelUnitBuff("player", i)
                end
            end
        end)
    end
end)

-------------------------
-- 7. DURABILITY WARNING & AUTO REPAIR
-- Zeigt eine Warnung wenn die Haltbarkeit unter den Threshold fällt
-- Repariert automatisch beim Händler
-------------------------
local durWarnFrame = CreateFrame("Frame", "AiBQOL_DurabilityWarning", UIParent)
durWarnFrame:SetSize(400, 40)
durWarnFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 250)
durWarnFrame:Hide()

local durWarnText = durWarnFrame:CreateFontString(nil, "OVERLAY")
durWarnText:SetFont("Fonts\\FRIZQT__.TTF", 22, "OUTLINE")
durWarnText:SetPoint("CENTER")
durWarnText:SetTextColor(1, 0, 0, 1)  -- AiB Rot

local function ShowDurWarning(pct)
    -- Farbverlauf Rot (<=15%) bis Orange (threshold)
    local threshold = (AiBQOL.misc and AiBQOL.misc.durabilityThreshold) or 50
    local t = math.max(0, math.min(1, (pct - 15) / math.max(1, threshold - 15)))
    local r = 1.0
    local g = t * 0.4
    local b = 0.0
    durWarnText:SetTextColor(r, g, b, 1)
    durWarnText:SetText(string.format("|cFFFF0000[AiB]|r Haltbarkeit: %d%%", pct))
    durWarnFrame:Show()
end

local function HideDurWarning()
    durWarnFrame:Hide()
end

local function GetLowestDurability()
    local lowest = nil
    for slot = 1, 19 do
        local current, maximum = GetInventoryItemDurability(slot)
        if current and maximum and maximum > 0 then
            local pct = (current / maximum) * 100
            if not lowest or pct < lowest then
                lowest = pct
            end
        end
    end
    return lowest
end

local durPollTimer = nil

local function StopDurPolling()
    if durPollTimer then
        durPollTimer:Cancel()
        durPollTimer = nil
    end
end

local function CheckDurability()
    local db = AiBQOL.misc
    if not db or not db.durabilityWarning then HideDurWarning() return end
    local lowest = GetLowestDurability()
    local threshold = db.durabilityThreshold or 50
    if lowest and lowest < threshold then
        ShowDurWarning(math.floor(lowest))
    else
        HideDurWarning()
    end
end

local function StartDurPolling()
    StopDurPolling()
    durPollTimer = C_Timer.NewTicker(3, CheckDurability)
end

local function HandleAutoRepair()
    local db = AiBQOL.misc
    if not db or not db.autoRepair then return end
    if IsShiftKeyDown() then return end
    if not CanMerchantRepair() then return end
    local cost = GetRepairAllCost()
    if not cost or cost <= 0 then return end

    if db.guildRepair and IsInGuild() and CanGuildBankRepair and CanGuildBankRepair() then
        RepairAllItems(true)
        C_Timer.After(0.5, function() RepairAllItems(false) end)
        print("|cFFFF0000[AiB]|r Ausrüstung über Gildenbank repariert.")
        return
    end

    RepairAllItems(false)
    print("|cFFFF0000[AiB]|r Ausrüstung repariert.")
end

local durEvents = CreateFrame("Frame", "AiBQOL_DurabilityEvents")
durEvents:RegisterEvent("PLAYER_LOGIN")
durEvents:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_LOGIN" then
        self:RegisterEvent("PLAYER_REGEN_DISABLED")
        self:RegisterEvent("PLAYER_REGEN_ENABLED")
        self:RegisterEvent("MERCHANT_SHOW")
        self:UnregisterEvent("PLAYER_LOGIN")
        CheckDurability()
        StartDurPolling()
        return
    end
    if event == "PLAYER_REGEN_DISABLED" then
        HideDurWarning()
        StopDurPolling()
        return
    end
    if event == "PLAYER_REGEN_ENABLED" then
        CheckDurability()
        StartDurPolling()
        return
    end
    if event == "MERCHANT_SHOW" then
        HandleAutoRepair()
        return
    end
end)

-------------------------
-- 8. IGNORE DECOR
-- Passt automatisch bei Dekorations-Items im Loot-Wurf
-------------------------
local decorFrame = CreateFrame("Frame", "AiBQOL_IgnoreDecor")
decorFrame:RegisterEvent("START_LOOT_ROLL")
decorFrame:SetScript("OnEvent", function(self, event, rollID)
    if not (AiBQOL.misc and AiBQOL.misc.ignoreDecor) then return end
    if not C_Item.IsDecorItem then return end

    local itemLink = GetLootRollItemLink(rollID)
    if not itemLink then return end

    local itemID = C_Item.GetItemInfoInstant(itemLink)
    if not itemID then return end

    if C_Item.IsDecorItem(itemID) then
        RollOnLoot(rollID, 0)  -- 0 = Pass
    end
end)

AiB.Config:Log("QoL", "Features geladen")
