local addonName, ns = ...
local L = ns.L or {}

local BACKDROP_TEMPLATE = BackdropTemplateMixin and "BackdropTemplate" or nil
local WHITE_TEXTURE = "Interface\\Buttons\\WHITE8X8"
local BUTTON_ICON = "Interface\\Icons\\Inv_10_gearupgrade_drakesshadowflameenhancedcrest"
local MASK_TEXTURE = "Interface\\CHARACTERFRAME\\TempPortraitAlphaMask"

local BUTTON_SIZE = 32
local MENU_WIDTH = 242
local MENU_ROW_HEIGHT = 28
local MENU_PADDING = 10

local MAP_FEATURES = {
    { id = "midnight_rare_map_pins", fallback = "Midnight Rare Map Pins" },
    { id = "midnight_treasure_map_pins", fallback = "Midnight Treasures" },
    { id = "gathering_nodes", fallback = "Gathering Nodes" },
}

local Runtime = {
    button = nil,
    menu = nil,
    rows = {},
    emptyText = nil,
    hookedWorldMap = false,
    eventFrame = nil,
}

local function playClickSound()
    if type(PlaySound) == "function" and SOUNDKIT then
        PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
    end
end

local function tryGetWorldMap()
    return _G.WorldMapFrame
end

local function getCanvasContainer()
    local worldMap = tryGetWorldMap()
    if not worldMap then
        return nil
    end

    if type(worldMap.GetCanvasContainer) == "function" then
        local ok, container = pcall(worldMap.GetCanvasContainer, worldMap)
        if ok and container then
            return container
        end
    end

    return worldMap.ScrollContainer or worldMap
end

local function applyPanelBackdrop(frame)
    if not frame or type(frame.SetBackdrop) ~= "function" then
        return
    end

    frame:SetBackdrop({
        bgFile = WHITE_TEXTURE,
        edgeFile = WHITE_TEXTURE,
        edgeSize = 1,
        insets = { left = 1, right = 1, top = 1, bottom = 1 },
    })
    frame:SetBackdropColor(0.025, 0.023, 0.026, 0.96)
    frame:SetBackdropBorderColor(0.96, 0.76, 0.28, 0.62)
end

local function addIconMask(owner, texture)
    if not owner or not texture or owner.vlMask or type(owner.CreateMaskTexture) ~= "function" or type(texture.AddMaskTexture) ~= "function" then
        return
    end

    local mask = owner:CreateMaskTexture(nil, "ARTWORK")
    mask:SetTexture(MASK_TEXTURE, "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
    mask:SetAllPoints(texture)
    texture:AddMaskTexture(mask)
    owner.vlMask = mask
end

local function getFeatureDefinition(featureID)
    if not ns.GetFeatureDefinition then
        return nil
    end

    local ok, definition = pcall(ns.GetFeatureDefinition, ns, featureID)
    if ok and type(definition) == "table" then
        return definition
    end

    return nil
end

local function getFeatureTitle(config)
    local definition = getFeatureDefinition(config.id)
    if definition and type(definition.title) == "string" and definition.title ~= "" then
        return definition.title
    end

    return config.fallback or config.id
end

local function isFeatureAvailable(featureID)
    return getFeatureDefinition(featureID) ~= nil
end

local function isFeatureEnabled(featureID)
    return ns.IsFeatureEnabled and ns:IsFeatureEnabled(featureID) == true
end

function Runtime:RefreshButton()
    local button = self.button
    if not button then
        return
    end

    local enabledCount = 0
    local availableCount = 0
    for _, config in ipairs(MAP_FEATURES) do
        if isFeatureAvailable(config.id) then
            availableCount = availableCount + 1
            if isFeatureEnabled(config.id) then
                enabledCount = enabledCount + 1
            end
        end
    end

    button:SetAlpha(availableCount > 0 and 1 or 0.62)
    if button.icon and type(button.icon.SetDesaturated) == "function" then
        button.icon:SetDesaturated(enabledCount == 0)
    end
    if button.icon then
        button.icon:SetAlpha(enabledCount > 0 and 1 or 0.42)
    end
end

function Runtime:PositionMenu()
    if not self.menu or not self.button then
        return
    end

    self.menu:ClearAllPoints()
    self.menu:SetPoint("TOPRIGHT", self.button, "BOTTOMRIGHT", 0, -8)
end

function Runtime:PositionButton()
    local button = self.button
    local anchor = getCanvasContainer()
    if not button or not anchor then
        return
    end

    button:ClearAllPoints()
    button:SetPoint("TOPRIGHT", anchor, "TOPRIGHT", -4, -66)
    self:PositionMenu()
end

function Runtime:RefreshMenu()
    local menu = self.menu
    if not menu then
        return
    end

    local visibleIndex = 0
    for _, config in ipairs(MAP_FEATURES) do
        if isFeatureAvailable(config.id) then
            visibleIndex = visibleIndex + 1
            local row = self.rows[visibleIndex]
            if not row then
                row = self:CreateMenuRow(menu, visibleIndex)
                self.rows[visibleIndex] = row
            end

            local enabled = isFeatureEnabled(config.id)
            row.featureID = config.id
            row.label:SetText(getFeatureTitle(config))
            row.check:SetShown(enabled)
            row.status:SetText(enabled and (L.FEATURES_STATUS_ON or "Enabled") or (L.FEATURES_STATUS_OFF or "Disabled"))
            row.status:SetTextColor(enabled and 0.58 or 0.78, enabled and 1.00 or 0.76, enabled and 0.68 or 0.68, 1)
            row:Show()
        end
    end

    for index = visibleIndex + 1, #self.rows do
        self.rows[index]:Hide()
        self.rows[index].featureID = nil
    end

    if self.emptyText then
        self.emptyText:SetShown(visibleIndex == 0)
    end

    menu:SetHeight(math.max(64, 38 + math.max(1, visibleIndex) * MENU_ROW_HEIGHT + MENU_PADDING))
    self:PositionMenu()
    self:RefreshButton()
end

function Runtime:ToggleFeature(featureID)
    if type(featureID) ~= "string" or featureID == "" or not ns.SetFeatureEnabled then
        return
    end

    ns:SetFeatureEnabled(featureID, not isFeatureEnabled(featureID))
    self:RefreshMenu()
    playClickSound()
end

function Runtime:CreateMenuRow(menu, index)
    local row = CreateFrame("Button", nil, menu, BACKDROP_TEMPLATE)
    row:SetHeight(MENU_ROW_HEIGHT)
    row:SetPoint("TOPLEFT", menu, "TOPLEFT", MENU_PADDING, -30 - ((index - 1) * MENU_ROW_HEIGHT))
    row:SetPoint("TOPRIGHT", menu, "TOPRIGHT", -MENU_PADDING, 0)
    row:EnableMouse(true)

    row.box = CreateFrame("Frame", nil, row, BACKDROP_TEMPLATE)
    row.box:SetSize(18, 18)
    row.box:SetPoint("LEFT", 2, 0)
    applyPanelBackdrop(row.box)
    if row.box.SetBackdropColor then
        row.box:SetBackdropColor(0.08, 0.075, 0.08, 0.94)
        row.box:SetBackdropBorderColor(0.96, 0.76, 0.28, 0.72)
    end

    row.check = row.box:CreateTexture(nil, "ARTWORK")
    row.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    row.check:SetSize(24, 24)
    row.check:SetPoint("CENTER", 0, 0)
    row.check:SetVertexColor(0.15, 1.00, 0.46, 1)
    row.check:Hide()

    row.label = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.label:SetPoint("LEFT", row.box, "RIGHT", 8, 0)
    row.label:SetPoint("RIGHT", -72, 0)
    row.label:SetJustifyH("LEFT")

    row.status = row:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    row.status:SetPoint("RIGHT", -4, 0)
    row.status:SetWidth(64)
    row.status:SetJustifyH("RIGHT")

    row.highlight = row:CreateTexture(nil, "BACKGROUND")
    row.highlight:SetTexture(WHITE_TEXTURE)
    row.highlight:SetAllPoints()
    row.highlight:SetColorTexture(1, 0.82, 0.16, 0.08)
    row.highlight:Hide()

    row:SetScript("OnEnter", function(selfRow)
        selfRow.highlight:Show()
    end)
    row:SetScript("OnLeave", function(selfRow)
        selfRow.highlight:Hide()
    end)
    row:SetScript("OnClick", function(selfRow)
        Runtime:ToggleFeature(selfRow.featureID)
    end)

    return row
end

function Runtime:CreateMenu(parent)
    if self.menu then
        return self.menu
    end

    local menu = CreateFrame("Frame", "VaultloomWorldMapFeatureMenu", parent, BACKDROP_TEMPLATE)
    menu:SetSize(MENU_WIDTH, 128)
    menu:SetFrameStrata("DIALOG")
    menu:SetFrameLevel((parent:GetFrameLevel() or 0) + 60)
    menu:EnableMouse(true)
    applyPanelBackdrop(menu)
    menu:Hide()

    menu.title = menu:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    menu.title:SetPoint("TOPLEFT", MENU_PADDING, -9)
    menu.title:SetPoint("TOPRIGHT", -MENU_PADDING, -9)
    menu.title:SetJustifyH("LEFT")
    menu.title:SetText(L.WORLD_MAP_PIN_BUTTON_MENU_TITLE or "Vaultloom Map Pins")

    self.emptyText = menu:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    self.emptyText:SetPoint("TOPLEFT", MENU_PADDING, -36)
    self.emptyText:SetPoint("TOPRIGHT", -MENU_PADDING, -36)
    self.emptyText:SetJustifyH("LEFT")
    self.emptyText:SetText(L.WORLD_MAP_PIN_BUTTON_EMPTY or "No map pin features available.")
    self.emptyText:Hide()

    self.menu = menu
    if type(UISpecialFrames) == "table" then
        UISpecialFrames[#UISpecialFrames + 1] = "VaultloomWorldMapFeatureMenu"
    end

    return menu
end

function Runtime:ToggleMenu()
    if not self.menu then
        return
    end

    if self.menu:IsShown() then
        self.menu:Hide()
    else
        self:RefreshMenu()
        self.menu:Show()
    end
end

function Runtime:CreateButton(parent)
    if self.button then
        return self.button
    end

    local button = CreateFrame("Button", "VaultloomWorldMapFeatureButton", parent)
    button:SetSize(BUTTON_SIZE, BUTTON_SIZE)
    button:SetFrameStrata("HIGH")
    button:SetFrameLevel((parent:GetFrameLevel() or 0) + 40)
    button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
    button:EnableMouse(true)

    button.background = button:CreateTexture(nil, "BACKGROUND")
    button.background:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
    button.background:SetSize(25, 25)
    button.background:SetPoint("TOPLEFT", 2, -4)

    button.icon = button:CreateTexture(nil, "ARTWORK")
    button.icon:SetTexture(BUTTON_ICON)
    button.icon:SetSize(20, 20)
    button.icon:SetPoint("TOPLEFT", 6, -6)
    button.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    button.icon:SetAlpha(0.36)
    addIconMask(button, button.icon)

    button.border = button:CreateTexture(nil, "OVERLAY")
    button.border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
    button.border:SetSize(54, 54)
    button.border:SetPoint("TOPLEFT")

    button.highlight = button:CreateTexture(nil, "HIGHLIGHT")
    button.highlight:SetTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
    button.highlight:SetBlendMode("ADD")
    button.highlight:SetSize(46, 46)
    button.highlight:SetPoint("CENTER", 0, 1)

    button:SetScript("OnClick", function()
        Runtime:ToggleMenu()
    end)
    button:SetScript("OnMouseDown", function(selfButton)
        selfButton.icon:SetPoint("TOPLEFT", 7, -7)
    end)
    button:SetScript("OnMouseUp", function(selfButton)
        selfButton.icon:SetPoint("TOPLEFT", 6, -6)
    end)
    button:SetScript("OnEnter", function(selfButton)
        GameTooltip:SetOwner(selfButton, "ANCHOR_RIGHT")
        GameTooltip:AddLine(L.WORLD_MAP_PIN_BUTTON_TITLE or "Vaultloom Map Pins", 1.00, 0.82, 0.18, true)
        GameTooltip:AddLine(L.WORLD_MAP_PIN_BUTTON_TOOLTIP or "Toggle Vaultloom world map pin features.", 0.88, 0.86, 0.82, true)
        GameTooltip:Show()
    end)
    button:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    self.button = button
    self:CreateMenu(parent)
    self:RefreshButton()
    self:PositionButton()

    return button
end

function Runtime:HookWorldMap()
    local worldMap = tryGetWorldMap()
    if not worldMap or self.hookedWorldMap == true then
        return
    end

    self.hookedWorldMap = true
    if worldMap.HookScript then
        worldMap:HookScript("OnShow", function()
            Runtime:Ensure()
            Runtime:RefreshButton()
            Runtime:PositionButton()
        end)
        worldMap:HookScript("OnHide", function()
            if Runtime.menu then
                Runtime.menu:Hide()
            end
        end)
    end

    if type(hooksecurefunc) == "function" then
        pcall(hooksecurefunc, worldMap, "RefreshOverlayFrames", function()
            Runtime:RefreshButton()
            Runtime:PositionButton()
        end)
        pcall(hooksecurefunc, worldMap, "OnMapChanged", function()
            Runtime:RefreshButton()
            Runtime:PositionButton()
        end)
        pcall(hooksecurefunc, worldMap, "OnFrameSizeChanged", function()
            Runtime:PositionButton()
        end)
    end
end

function Runtime:Ensure()
    local worldMap = tryGetWorldMap()
    if not worldMap then
        return false
    end

    self:CreateButton(worldMap)
    self:HookWorldMap()
    self:RefreshButton()
    self:PositionButton()
    return true
end

function ns:EnsureWorldMapFeatureButton()
    return Runtime:Ensure()
end

function ns:RefreshWorldMapFeatureButton()
    if Runtime.button then
        Runtime:RefreshButton()
    end
    if Runtime.menu and Runtime.menu:IsShown() then
        Runtime:RefreshMenu()
    end
    Runtime:PositionButton()
end

Runtime.eventFrame = CreateFrame("Frame")
Runtime.eventFrame:RegisterEvent("ADDON_LOADED")
Runtime.eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
Runtime.eventFrame:SetScript("OnEvent", function(_, event, loadedName)
    if event == "ADDON_LOADED" and loadedName ~= addonName and loadedName ~= "Blizzard_WorldMap" then
        return
    end

    if C_Timer and type(C_Timer.After) == "function" then
        C_Timer.After(0, function()
            Runtime:Ensure()
        end)
    else
        Runtime:Ensure()
    end
end)
