-------------------------------------------------------------------------------
--  Profession Item Hotkey
--  Built-in secure item hover/click helper for Sin's Toolkit.
-------------------------------------------------------------------------------
local _, SinsToolkit = ...
local addon = SinsToolkit
addon.data = addon.data or {}

local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_LOGIN")
frame:SetScript("OnEvent", function(self)
    self:UnregisterEvent("PLAYER_LOGIN")
    SinsToolkitDB = SinsToolkitDB or {}

    local IsPlayerSpellKnown = C_SpellBook and C_SpellBook.IsSpellKnown or IsPlayerSpell
    local COLORS = {
        prospectable = CreateColor(1, 1 / 3, 1 / 3),
        millable = CreateColor(1 / 2, 1, 1 / 2),
        openable = CreateColor(1, 1, 1),
        disenchantable = CreateColor(1 / 2, 1 / 2, 1),
    }

    local button = CreateFrame("Button", "SinsToolkitProfessionItemHotkeyButton", UIParent,
        "SecureActionButtonTemplate,SecureHandlerAttributeTemplate,SecureHandlerEnterLeaveTemplate")
    button:SetFrameStrata("TOOLTIP")
    button:RegisterForClicks("AnyUp")
    button:EnableMouse(true)
    button:Hide()

    local glow = button:CreateTexture(nil, "ARTWORK")
    glow:SetPoint("CENTER")
    glow:SetAtlas("UI-HUD-ActionBar-Proc-Loop-Flipbook")
    glow:SetDesaturated(true)

    local animation = glow:CreateAnimationGroup()
    animation:SetLooping("REPEAT")
    local flipbook = animation:CreateAnimation("FlipBook")
    flipbook:SetTarget(glow)
    flipbook:SetDuration(1)
    flipbook:SetFlipBookColumns(5)
    flipbook:SetFlipBookRows(6)
    flipbook:SetFlipBookFrames(30)

    local cachedModifier = nil
    local function GetModifier()
        if cachedModifier then return cachedModifier end
        if not SinsToolkitDB then SinsToolkitDB = {} end
        local value = SinsToolkitDB.professionItemHotkeyModifier or "ALT"
        if value ~= "CTRL" and value ~= "SHIFT" then value = "ALT" end
        SinsToolkitDB.professionItemHotkeyModifier = value
        cachedModifier = value
        return value
    end

    local cachedEnabled = nil
    local function IsEnabled()
        if cachedEnabled == nil then
            cachedEnabled = SinsToolkit.IsFeatureActive and SinsToolkit.IsFeatureActive("professionItemHotkeyEnabled")
        end
        return cachedEnabled
    end

    local function UpdateClickDirection()
        if C_CVar and C_CVar.GetCVarBool and C_CVar.GetCVarBool("ActionButtonUseKeyDown") then
            button:RegisterForClicks("AnyDown")
        else
            button:RegisterForClicks("AnyUp")
        end
    end

    local function ModifierIsDown()
        if not IsEnabled() then return false end
        local modifier = GetModifier()
        if not IsAltKeyDown() then return false end
        if modifier == "CTRL" and not IsControlKeyDown() then return false end
        if modifier ~= "CTRL" and IsControlKeyDown() then return false end
        if modifier == "SHIFT" and not IsShiftKeyDown() then return false end
        if modifier ~= "SHIFT" and IsShiftKeyDown() then return false end
        return true
    end

    function SinsToolkit.ApplyProfessionItemHotkeyModifier(value)
        if not SinsToolkitDB then SinsToolkitDB = {} end
        if value ~= "CTRL" and value ~= "SHIFT" then value = "ALT" end
        SinsToolkitDB.professionItemHotkeyModifier = value
        cachedModifier = value

        if value == "CTRL" then
            RegisterAttributeDriver(button, "visibility", "[mod:alt,mod:ctrl] show; hide")
        elseif value == "SHIFT" then
            RegisterAttributeDriver(button, "visibility", "[mod:alt,mod:shift] show; hide")
        else
            RegisterAttributeDriver(button, "visibility", "[mod:alt] show; hide")
        end
    end

    function SinsToolkit.ApplyProfessionItemHotkeyEnabled()
        cachedEnabled = SinsToolkit.IsFeatureActive and SinsToolkit.IsFeatureActive("professionItemHotkeyEnabled")
        if not IsEnabled() and button:IsShown() and not InCombatLockdown() then
            button:Hide()
        end
    end

    button:RegisterEvent("CVAR_UPDATE")
    button:SetScript("OnEvent", function(_, _, cvar)
        if cvar == "ActionButtonUseKeyDown" then
            UpdateClickDirection()
        end
    end)
    UpdateClickDirection()

    button:HookScript("OnAttributeChanged", function(self, name)
        if name ~= "spell" and name ~= "macrotext" then return end
        if not self:GetAttribute(name) then return end

        local actionType = name == "macrotext" and "macro" or name
        local modifier = GetModifier()
        self:SetAttribute("type1", actionType)
        self:SetAttribute("type", actionType)
        self:SetAttribute("alt-type1", nil)
        self:SetAttribute("alt-ctrl-type1", nil)
        self:SetAttribute("alt-shift-type1", nil)
        if modifier == "CTRL" then
            self:SetAttribute("alt-ctrl-type1", actionType)
        elseif modifier == "SHIFT" then
            self:SetAttribute("alt-shift-type1", actionType)
        else
            self:SetAttribute("alt-type1", actionType)
        end
    end)

    button:HookScript("OnShow", function(self)
        local owner = GameTooltip and GameTooltip:GetOwner()
        if not owner or not owner.GetScaledRect then return end

        local left, bottom, width, height = owner:GetScaledRect()
        if not left then return end

        local scaleMultiplier = 1 / UIParent:GetScale()
        self:ClearAllPoints()
        self:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left * scaleMultiplier, bottom * scaleMultiplier)
        self:SetSize(width * scaleMultiplier, height * scaleMultiplier)
        glow:SetSize(width * scaleMultiplier * 1.4, height * scaleMultiplier * 1.4)
        animation:Play()
        self:SetAttribute("_entered", true)
    end)

    button:SetAttribute("_onleave", "self:ClearAllPoints();self:Hide()")
    button:SetAttribute("_onattributechanged", [[
        if name == 'visibility' and value == 'hide' and self:IsShown() then
            self:ClearAllPoints()
            self:Hide()
        end
    ]])

    button:HookScript("OnHide", function(self)
        animation:Stop()
        if InCombatLockdown and InCombatLockdown() then return end
        self:SetAttribute("target-bag", nil)
        self:SetAttribute("target-slot", nil)
        self:SetAttribute("type", nil)
        self:SetAttribute("type1", nil)
        self:SetAttribute("alt-type1", nil)
        self:SetAttribute("alt-ctrl-type1", nil)
        self:SetAttribute("alt-shift-type1", nil)
        self:SetAttribute("spell", nil)
        self:SetAttribute("macrotext", nil)
        self:SetAttribute("_entered", false)
        self._euiSpellID = nil
        self._euiBagID = nil
        self._euiSlotID = nil
        self.spellID = nil
    end)

    local function AddTooltipLine(spellID)
        if not GameTooltip or not spellID then return end
        local spellName = C_Spell and C_Spell.GetSpellName and C_Spell.GetSpellName(spellID)
        if not spellName then return end
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine("Click to use " .. spellName, 0.85, 0.66, 0.30)
        GameTooltip:Show()
    end

    local function CanDisenchant(itemID)
        if not IsPlayerSpellKnown or not IsPlayerSpellKnown(13262) then return end
        if addon.data.disenchantable and addon.data.disenchantable[itemID] then
            return 13262, COLORS.disenchantable
        end
        if not C_Item or not C_Item.GetItemInfo then return end

        local _, _, quality, _, _, _, _, _, _, _, _, class, subClass = C_Item.GetItemInfo(itemID)
        if not quality or quality < Enum.ItemQuality.Uncommon or quality > Enum.ItemQuality.Epic then return end
        if class ~= Enum.ItemClass.Weapon
            and class ~= Enum.ItemClass.Armor
            and class ~= Enum.ItemClass.Profession
            and not (class == Enum.ItemClass.Gem and subClass == Enum.ItemGemSubclass.Artifactrelic) then
            return
        end
        if C_Item.GetItemInventoryTypeByID and C_Item.GetItemInventoryTypeByID(itemID) == Enum.InventoryType.IndexBodyType then return end
        if C_Item.IsCosmeticItem and C_Item.IsCosmeticItem(itemID) then return end

        return 13262, COLORS.disenchantable
    end

    local function GetActionForItem(item)
        local itemID = item and item:GetItemID()
        if not itemID then return end

        local location = item:GetItemLocation()
        local stack = 0
        if C_Item then
            if location and C_Item.GetStackCount then
                stack = C_Item.GetStackCount(location) or 0
            elseif C_Item.GetItemCount then
                stack = C_Item.GetItemCount(itemID) or 0
            end
        end

        local prospectInfo = addon.data.prospectable and addon.data.prospectable[itemID]
        if prospectInfo and IsPlayerSpellKnown and IsPlayerSpellKnown(prospectInfo[1]) and stack >= (prospectInfo[2] or 1) then
            return prospectInfo[1], COLORS.prospectable, prospectInfo[2]
        end

        local millInfo = addon.data.millable and addon.data.millable[itemID]
        if millInfo and IsPlayerSpellKnown and IsPlayerSpellKnown(millInfo[1]) and stack >= (millInfo[2] or 1) then
            return millInfo[1], COLORS.millable, millInfo[2]
        end

        local requiredLevel = addon.data.openable and addon.data.openable[itemID]
        if requiredLevel then
            if IsPlayerSpellKnown and IsPlayerSpellKnown(1804) and requiredLevel <= UnitLevel("player") then
                return 1804, COLORS.openable
            elseif IsPlayerSpellKnown and IsPlayerSpellKnown(312890) and requiredLevel <= UnitLevel("player") then
                return 312890, COLORS.openable
            elseif IsPlayerSpellKnown and IsPlayerSpellKnown(323427) and requiredLevel <= 60 then
                return 323427, COLORS.openable
            end
        end

        return CanDisenchant(itemID)
    end

    local function ApplyAction(item, spellID, color)
        local location = item and item:GetItemLocation()
        if not location or not location:IsBagAndSlot() then return end

        local bagID, slotID = location:GetBagAndSlot()
        button:SetAttribute("target-bag", bagID)
        button:SetAttribute("target-slot", slotID)
        button:SetAttribute("spell", nil)
        button:SetAttribute("macrotext", nil)

        if spellID == 13262 or spellID == 1804 or spellID == 312890 or spellID == 323427 then
            button:SetAttribute("spell", spellID)
        elseif FindSpellBookSlotBySpellID and FindSpellBookSlotBySpellID(spellID) then
            button:SetAttribute("spell", spellID)
        else
            button:SetAttribute("macrotext", ("/run C_TradeSkillUI.CraftSalvage(%d, 1, ItemLocation:CreateFromBagAndSlot(%d, %d))"):format(spellID, bagID, slotID))
        end

        button.spellID = spellID
        button._euiSpellID = spellID
        button._euiBagID = bagID
        button._euiSlotID = slotID
        glow:SetVertexColor(color:GetRGB())
        button:Show()
        AddTooltipLine(spellID)
    end

    local function TooltipHook(tooltip, item)
        if tooltip ~= GameTooltip then return end
        if tooltip:GetOwner() == button then return end
        if not IsEnabled() then return end
        if InCombatLockdown and InCombatLockdown() then return end
        if UnitHasVehicleUI and UnitHasVehicleUI("player") then return end
        if not ModifierIsDown() then return end

        local spellID, color = GetActionForItem(item)
        if spellID then
            ApplyAction(item, spellID, color)
        end
    end

    if TooltipDataProcessor and C_TooltipInfo then
        TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, function(tooltip, data)
            if not tooltip or tooltip:IsForbidden() or not tooltip:GetOwner() then return end
            if data and data.guid then
                local location = C_Item and C_Item.GetItemLocation and C_Item.GetItemLocation(data.guid)
                if location and location:IsBagAndSlot() then
                    local bagID = location:GetBagAndSlot()
                    if bagID and bagID >= 0 and bagID <= 5 then
                        TooltipHook(tooltip, Item:CreateFromItemLocation(location))
                    end
                end
            end
        end)
    else
        hooksecurefunc(GameTooltip, "SetBagItem", function(tooltip, bagID, slotID)
            if bagID and slotID and bagID >= 0 and bagID <= 5 then
                TooltipHook(tooltip, Item:CreateFromItemLocation(ItemLocation:CreateFromBagAndSlot(bagID, slotID)))
            end
        end)
    end

    local eventFrame = CreateFrame("Frame")
    eventFrame:RegisterEvent("BAG_UPDATE_DELAYED")
    eventFrame:RegisterEvent("MODIFIER_STATE_CHANGED")
    eventFrame:SetScript("OnEvent", function(_, event)
        if event == "BAG_UPDATE_DELAYED" then
            if button:IsShown() and not InCombatLockdown() then button:Hide() end
        elseif event == "MODIFIER_STATE_CHANGED" then
            if button:IsShown() and not ModifierIsDown() and not InCombatLockdown() then
                button:Hide()
            elseif GameTooltip and GameTooltip:IsShown() and GameTooltip:GetOwner() and GameTooltip:GetOwner():IsMouseOver() then
                local owner = GameTooltip:GetOwner()
                if owner.GetSlotAndBagID then
                    local slotID, bagID = owner:GetSlotAndBagID()
                    if slotID and bagID then
                        TooltipHook(GameTooltip, Item:CreateFromBagAndSlot(bagID, slotID))
                    end
                end
            end
        end
    end)

    SinsToolkit.ApplyProfessionItemHotkeyModifier(GetModifier())
    SinsToolkit.ApplyProfessionItemHotkeyEnabled()
end)
