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

local SC = PT.SkinningCheck
local Tooltip = PT.Tooltip
local Tracker = PT.Tracker
local Dropdown = PT.Dropdown
local L = PT.L

local PIN_TEMPLATE = "AhUISkinningWorldMapPinTemplate"

PT.SkinningWorldMapDataProvider = PT.SkinningWorldMapDataProvider or CreateFromMixins(MapCanvasDataProviderMixin)

local knowledgeByMap = {}
local knowledgeDirty = true
local requestedItemData = {}
local refreshQueued = false

local CHECKBOX_ON  = "Interface\\Buttons\\UI-CheckBox-Check"
local CHECKBOX_OFF = "Interface\\Buttons\\UI-CheckBox-Up"

local function GetMapPinsSize()
    local size = tonumber(PT.config.mapPinsSize) or 30
    if size < 12 then size = 12 end
    if size > 40 then size = 40 end
    return size
end

local function QueueRefreshMapPins()
    if refreshQueued then return end
    refreshQueued = true

    C_Timer.After(0.1, function()
        refreshQueued = false
        if PT.SkinningWorldMapDataProvider and WorldMapFrame and WorldMapFrame:IsShown() then
            PT.SkinningWorldMapDataProvider:RefreshAllData()
        end
    end)
end

local function MarkKnowledgeDirty()
    knowledgeDirty = true
end

local function RebuildKnowledgeCache()
    if not knowledgeDirty then return end

    knowledgeByMap = {}

    if not PT.GetProfessions then
        knowledgeDirty = false
        return
    end

    local profs = PT.GetProfessions(true)
    if not profs then
        knowledgeDirty = false
        return
    end

    for _, profession in pairs(profs) do
        for _, entry in ipairs(profession.entries or {}) do
            local wp = entry.waypoint
            if wp and wp.map and wp.x and wp.y then
                knowledgeByMap[wp.map] = knowledgeByMap[wp.map] or {}
                table.insert(knowledgeByMap[wp.map], entry)
            end
        end
    end

    knowledgeDirty = false
end

local function IsSkinningTarget(data)
    return data and data.guide and data.guide.zone and SC.GetTargetName and SC:GetTargetName(data)
end

local function GetProfessionBadge(entry)
    if not entry or not entry.profession then return nil end
    return entry.profession.icon
end

function PT.SkinningWorldMapDataProvider:RemoveAllData()
    if self:GetMap() then
        self:GetMap():RemoveAllPinsByTemplate(PIN_TEMPLATE)
    end
end

local function AddKnowledgePins(provider, mapID)
    if PT.config.showMapKnowledge == false then return end

    RebuildKnowledgeCache()

    local entries = knowledgeByMap[mapID]
    if not entries then return end

    for _, entry in ipairs(entries) do
        if entry.itemId and not requestedItemData[entry.itemId] then
            requestedItemData[entry.itemId] = true
            C_Item.RequestLoadItemDataByID(entry.itemId)
        end

        local show = true

        if entry.GetRemainingKnowledgePoints then
            local kp = entry:GetRemainingKnowledgePoints()
            if not kp or kp <= 0 then
                show = false
            end
        end

        if show then
            provider:GetMap():AcquirePin(
                PIN_TEMPLATE,
                entry,
                entry.waypoint.x,
                entry.waypoint.y
            )
        end
    end
end

function PT.SkinningWorldMapDataProvider:RefreshAllData()
    if not self:GetMap() then return end

    self:RemoveAllData()

    local mapID = self:GetMap():GetMapID()
    if not mapID then return end

    if PT.config.showMapLures ~= false and PT.config.skinningCheckEnabled and SC:IsAvailable() then
        for _, target in ipairs(SC.TARGETS) do
            if SC:IsTargetAvailable(target)
            and SC:IsTargetEnabled(target)
            and not SC:IsTargetCompleted(target)
            then
                local zone = target.guide and target.guide.zone
                if zone and zone.map == mapID and zone.x and zone.y then
                    self:GetMap():AcquirePin(PIN_TEMPLATE, target, zone.x, zone.y)
                end
            end
        end
    end

    AddKnowledgePins(self, mapID)
end

AhUISkinningWorldMapPinMixin = CreateFromMixins(MapCanvasPinMixin)

function AhUISkinningWorldMapPinMixin:OnLoad()
    self:UseFrameLevelType("PIN_FRAME_LEVEL_TOPMOST")
    self:SetMovable(true)
    self:SetScalingLimits(1, 1.0, 1.2)

    if self.texture then
        self.texture:SetDrawLayer("ARTWORK", 0)
    end

    if self.badge then
        self.badge:SetSize(12, 12)
        self.badge:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 1, -1)
        self.badge:SetDrawLayer("OVERLAY", 7)
        self.badge:Hide()
    end
end

function AhUISkinningWorldMapPinMixin:OnAcquired(data, x, y)
    self.data = data
    self:SetPosition(x, y)

    local size = GetMapPinsSize()
    self:SetSize(size, size)

    self.texture:SetTexture("Interface\\AddOns\\AhUI\\Media\\logo.tga")

    if self.badge then
        if IsSkinningTarget(data) then
            self.badge:SetAtlas("Vehicle-Trap-Gold")
            self.badge:Show()
        else
            local badge = GetProfessionBadge(data)

            if badge then
                self.badge:SetTexture(badge)
                self.badge:Show()
            else
                self.badge:Hide()
            end
        end
    end
end

function AhUISkinningWorldMapPinMixin:OnMouseEnter()
    if not self.data then return end

    Tooltip:Clear()

    if IsSkinningTarget(self.data) then
        Tooltip:AddLine(SC:GetTargetName(self.data), 14, 1, 1, 1)

        local guide = SC:GetGuideTooltip(self.data)
        if guide then
            Tooltip:AddLine(guide, 12, 0.85, 0.85, 0.85)
        end
    else
        local name = self.data.GetName and self.data:GetName()
        if (not name or name == "") and self.data.itemId then
            name = C_Item.GetItemNameByID(self.data.itemId)
        end
        if not name or name == "" then
            name = L["MAP_PINS_UNKNOWN"]
        end

        Tooltip:AddLine(name, 14, 1, 1, 1)

        if self.data.kp then
            Tooltip:AddLine("+" .. self.data.kp, 12, 0, 1, 0)
        end

        local text = self.data.text
        if not text and self.data.GetDescription then
            text = self.data:GetDescription()
        end
        if not text and self.data.GetRequirementsText then
            text = self.data:GetRequirementsText()
        end
        if not text then
            text = self.data.note or self.data.description
        end

        if text and text ~= "" then
            Tooltip:AddLine(text, 12, 0.85, 0.85, 0.85)
        end
    end

    Tooltip:ShowSmart(self)
end

function AhUISkinningWorldMapPinMixin:OnMouseLeave()
    Tooltip:Clear()
end

function AhUISkinningWorldMapPinMixin:OnMouseDown(button)
end

function AhUISkinningWorldMapPinMixin:OnMouseUp(button)
    if button ~= "LeftButton" then return end
    if not self.data then return end

    if IsSkinningTarget(self.data) then
        SC:SetPendingWaypoint(self.data)
        return
    end

    if Tracker and Tracker.SetTrackedItem then
        Tracker:SetTrackedItem(self.data)
    elseif Tracker and Tracker.ToggleTrackedItem then
        Tracker:ToggleTrackedItem(self.data)
    end
end

AhUISkinningWorldMapPinMixin.SetPassThroughButtons = function() end

local function ShowMapPinsSizeInput(owner)
    if not owner then return end

    if PT.MapPinsSizeBox then
        PT.MapPinsSizeBox:Hide()
        PT.MapPinsSizeBox:SetParent(nil)
        PT.MapPinsSizeBox = nil
    end

    local parent = Dropdown and Dropdown.frame or WorldMapFrame
    if not parent then return end

    local box = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
    box:SetAutoFocus(false)
    box:SetNumeric(true)
    box:SetSize(44, 20)
    box:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -18, -75)
    box:SetText(tostring(GetMapPinsSize()))
    box:SetCursorPosition(0)
    box:HighlightText()

    box.label = parent:CreateFontString(nil, "OVERLAY")
    box.label:SetFont(AhUI.FONT, 11, "")
    box.label:SetPoint("RIGHT", box, "LEFT", -6, 0)
    box.label:SetText(L["MAP_PINS_SIZE"])

    local function ApplyValue()
        local v = tonumber(box:GetText())
        if not v then
            box:SetText(tostring(GetMapPinsSize()))
            return
        end

        v = math.floor(v + 0.5)
        if v < 12 then v = 12 end
        if v > 40 then v = 40 end

        PT.config.mapPinsSize = v
        box:SetText(tostring(v))
        QueueRefreshMapPins()
    end

    box:SetScript("OnEnterPressed", function(self)
        ApplyValue()
        self:ClearFocus()
    end)

    box:SetScript("OnEscapePressed", function(self)
        self:SetText(tostring(GetMapPinsSize()))
        self:ClearFocus()
    end)

    box:SetScript("OnEditFocusLost", function()
        ApplyValue()
    end)

    parent:HookScript("OnHide", function()
        if box.label then
            box.label:Hide()
            box.label:SetParent(nil)
            box.label = nil
        end
        if box then
            box:Hide()
            box:SetParent(nil)
        end
        if PT.MapPinsSizeBox == box then
            PT.MapPinsSizeBox = nil
        end
    end)

    PT.MapPinsSizeBox = box
end

local function ShowMapPinsMenu(owner)
    if not Dropdown then return end

    Dropdown:Show({
        owner = nil,
        keepOpen = true,
        items = {
            { header = true, text = L["MAP_PINS_MENU_TITLE"] },
            {
                text = L["MAP_PINS_KNOWLEDGE"],
                icon = (PT.config.showMapKnowledge ~= false) and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    PT.config.showMapKnowledge = not (PT.config.showMapKnowledge ~= false)
                    QueueRefreshMapPins()
                    C_Timer.After(0.01, function()
                        if Dropdown and Dropdown.Hide then Dropdown:Hide() end
                        C_Timer.After(0.01, function()
                            ShowMapPinsMenu(PT.MapPinsButton)
                        end)
                    end)
                end,
            },
            {
                text = L["MAP_PINS_LURES"],
                icon = (PT.config.showMapLures ~= false) and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    PT.config.showMapLures = not (PT.config.showMapLures ~= false)
                    QueueRefreshMapPins()
                    C_Timer.After(0.01, function()
                        if Dropdown and Dropdown.Hide then Dropdown:Hide() end
                        C_Timer.After(0.01, function()
                            ShowMapPinsMenu(PT.MapPinsButton)
                        end)
                    end)
                end,
            },
            { header = true, text = " " },
        }
    })

    local f = Dropdown.frame
    if not f or not owner then return end
    f:ClearAllPoints()
    f:SetPoint("BOTTOMRIGHT", owner, "BOTTOMLEFT", -8, 0)

    C_Timer.After(0, function()
        if Dropdown.frame and Dropdown.frame:IsShown() then
            ShowMapPinsSizeInput(owner)
        end
    end)
end

local function CreateMapPinsButton()
    if PT.MapPinsButton then return end
    if not WorldMapFrame then return end

    local btn = CreateFrame("Button", nil, WorldMapFrame)
    btn:SetSize(30, 30)
    btn:SetPoint("BOTTOMLEFT", WorldMapFrame, "BOTTOMLEFT", 10, 40)
    btn:SetFrameStrata("HIGH")
    btn:RegisterForClicks("LeftButtonUp")

    btn.icon = btn:CreateTexture(nil, "ARTWORK")
    btn.icon:SetAllPoints()
    btn.icon:SetTexture("Interface\\AddOns\\AhUI\\Media\\logo.tga")

    btn:SetScript("OnEnter", function(self)
        if not Tooltip then return end
        Tooltip:Clear()
        Tooltip:AddLine(L["MAP_PINS_TITLE"], 14, 1, 1, 1)
        Tooltip:AddLine(" ")
        Tooltip:AddLine(L["MAP_PINS_SETTINGS"], 12, 0.85, 0.85, 0.85)
        Tooltip:ShowAt(self, "BOTTOMRIGHT", -8, 34)
    end)

    btn:SetScript("OnLeave", function()
        if Tooltip then
            Tooltip:Clear()
        end
    end)

    btn:SetScript("OnClick", function(self)
        if Tooltip then
            Tooltip:Clear()
        end
        ShowMapPinsMenu(self)
    end)

    PT.MapPinsButton = btn
end

local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("QUEST_LOG_UPDATE")
f:RegisterEvent("SKILL_LINES_CHANGED")
f:RegisterEvent("TRADE_SKILL_SHOW")
f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("ITEM_DATA_LOAD_RESULT")
f:RegisterEvent("TRADE_SKILL_DATA_SOURCE_CHANGED")

f:SetScript("OnEvent", function(_, event, itemID, success)
    if event == "ITEM_DATA_LOAD_RESULT" then
        if success then
            QueueRefreshMapPins()
        end
        return
    end

    if event == "PLAYER_LOGIN"
    or event == "PLAYER_ENTERING_WORLD"
    or event == "QUEST_LOG_UPDATE"
    or event == "SKILL_LINES_CHANGED"
    or event == "TRADE_SKILL_SHOW"
    or event == "TRADE_SKILL_DATA_SOURCE_CHANGED"
    then
        MarkKnowledgeDirty()
    end

    if not WorldMapFrame then return end

    if not WorldMapFrame.dataProviders or not WorldMapFrame.dataProviders[PT.SkinningWorldMapDataProvider] then
        WorldMapFrame:AddDataProvider(PT.SkinningWorldMapDataProvider)
    end

    CreateMapPinsButton()
    QueueRefreshMapPins()
end)

if PT.RefreshUI then
    hooksecurefunc(PT, "RefreshUI", function()
        MarkKnowledgeDirty()
        QueueRefreshMapPins()
    end)
end

if Tracker then
    if Tracker.ToggleTrackedItem then
        hooksecurefunc(Tracker, "ToggleTrackedItem", function()
            MarkKnowledgeDirty()
            QueueRefreshMapPins()
        end)
    end

    if Tracker.SetTrackedItem then
        hooksecurefunc(Tracker, "SetTrackedItem", function()
            MarkKnowledgeDirty()
            QueueRefreshMapPins()
        end)
    end
end

if WorldMapFrame then
    WorldMapFrame:HookScript("OnShow", function()
        CreateMapPinsButton()
        MarkKnowledgeDirty()
        QueueRefreshMapPins()
    end)
end