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
PT.config.enableMapPins = PT.config.enableMapPins ~= false

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 PT.config.enableMapPins == false then
        self:RemoveAllData()
        return
    end

    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 HideMapPinsMenu()
    if Dropdown and Dropdown.Hide then
        Dropdown:Hide()
    end

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

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

PT.HideMapPinsMenu = HideMapPinsMenu

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)

    PT.MapPinsSizeBox = box
end


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

    HideMapPinsMenu()

    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()
                        HideMapPinsMenu()
                        C_Timer.After(0.01, function()
                            if PT.MapPinsButton and PT.MapPinsButton:IsShown() then
                                ShowMapPinsMenu(PT.MapPinsButton)
                            end
                        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()
                        HideMapPinsMenu()
                        C_Timer.After(0.01, function()
                            if PT.MapPinsButton and PT.MapPinsButton:IsShown() then
                                ShowMapPinsMenu(PT.MapPinsButton)
                            end
                        end)
                    end)
                end,
            },
            { header = true, text = " " },
        }
    })

    local f = Dropdown.frame
    if not f then return end

    f:ClearAllPoints()

    local uiParent = UIParent
    local scale = uiParent and uiParent:GetEffectiveScale() or 1

    local left = owner:GetLeft()
    local right = owner:GetRight()
    local screenWidth = GetScreenWidth()

    local estimatedWidth = math.max((f:GetWidth() or 0), 180)
    local ownerLeft = left and (left / scale) or 0
    local ownerRight = right and (right / scale) or 0

    local spaceLeft = ownerLeft
    local spaceRight = screenWidth - ownerRight

    if spaceLeft >= estimatedWidth + 12 then
        f:SetPoint("BOTTOMRIGHT", owner, "BOTTOMLEFT", -8, 0)
    elseif spaceRight >= estimatedWidth + 12 then
        f:SetPoint("BOTTOMLEFT", owner, "BOTTOMRIGHT", 8, 0)
    else
        f:SetPoint("BOTTOMLEFT", owner, "TOPLEFT", 0, 8)
    end

    C_Timer.After(0, function()
        if not Dropdown.frame or not Dropdown.frame:IsShown() then return end

        local frame = Dropdown.frame
        local frameLeft = frame:GetLeft()
        local frameRight = frame:GetRight()

        if frameLeft and frameLeft < 4 then
            frame:ClearAllPoints()
            frame:SetPoint("BOTTOMLEFT", owner, "BOTTOMRIGHT", 8, 0)
        elseif frameRight and uiParent and frameRight > uiParent:GetRight() - 4 then
            frame:ClearAllPoints()
            frame:SetPoint("BOTTOMRIGHT", owner, "BOTTOMLEFT", -8, 0)
        end

        ShowMapPinsSizeInput(owner)
    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(24, 24)
    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)

        local uiParent = UIParent
        local left = self:GetLeft()
        local right = self:GetRight()

        if not left or not right or not uiParent then
            Tooltip:ShowAt(self, "BOTTOMRIGHT", -8, 34)
            return
        end

        local screenLeft = left
        local screenRight = right
        local uiRight = uiParent:GetRight() or GetScreenWidth()

        local spaceLeft = screenLeft
        local spaceRight = uiRight - screenRight

        if spaceLeft > 220 then
            Tooltip:ShowAt(self, "BOTTOMRIGHT", -8, 34)
        elseif spaceRight > 220 then
            Tooltip:ShowAt(self, "BOTTOMLEFT", 8, 34)
        else
            Tooltip:ShowAt(self, "TOPLEFT", 0, 8)
        end
    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)

    if PT.config.enableMapPins == false then
        btn:Hide()
    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 PT.config.enableMapPins == false then
        if PT.SkinningWorldMapDataProvider then
            PT.SkinningWorldMapDataProvider:RemoveAllData()
        end

        if PT.MapPinsButton then
            PT.MapPinsButton:Hide()
        end

        HideMapPinsMenu()
        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()
        if PT.config.enableMapPins == false then
            if PT.MapPinsButton then
                PT.MapPinsButton:Hide()
            end
            return
        end

        CreateMapPinsButton()
        MarkKnowledgeDirty()
        QueueRefreshMapPins()
    end)

    WorldMapFrame:HookScript("OnHide", function()
        HideMapPinsMenu()
        if Tooltip then
            Tooltip:Clear()
        end
    end)
end