local _, ns = ...

------------------------------------------------------------
-- Mythic+ Utility Advisor
-- Shows per-dungeon utility advice with spell icons filtered
-- to the player's class. Hover any icon for a full tooltip.
--
-- Data is a starting seed — expand DUNGEONS[mapID].byClass as
-- you learn what to recommend for each spec/dungeon.
------------------------------------------------------------

ns.UtilityAdvisor = ns.UtilityAdvisor or {}
local UA = ns.UtilityAdvisor

------------------------------------------------------------
-- Data
-- DUNGEONS[instanceMapID] = {
--   header, description,
--   byClass = { CLASS = { spellID | { id=, talent=true, note="..." }, ... } },
-- }
------------------------------------------------------------

UA.DUNGEONS = {
    [2526] = { -- Algeth'ar Academy
        header      = "Algeth'ar Academy",
        lead        = "Caster-heavy with magic dispels and boss enrages.",
        description = "Interrupts are required on most casters; missing one often wipes the pack. Bring magic dispel for trash debuffs, stuns for mini-bosses, and a bleed/enrage cleanse (Shiv) for the final boss.",
        byClass = {
            ROGUE = {
                1766,                                                      -- Kick
                408,                                                       -- Kidney Shot
                2094,                                                      -- Blind
                31224,                                                     -- Cloak of Shadows
                1856,                                                      -- Vanish
                { id = 5938, talent = true, note = "Dispel enrage + purge magic buffs" }, -- Shiv
            },
        },
    },
    [2811] = { -- Magisters' Terrace
        header      = "Magisters' Terrace",
        description = "Magic debuffs and fears on trash. Priest of Rukhmar casts need hard interrupts.",
        byClass = {},
    },
    [2874] = { header = "Maisara Caverns",    description = "Pending notes.", byClass = {} },
    [2915] = { header = "Nexus-Point Xenas",  description = "Pending notes.", byClass = {} },
    [658]  = { header = "Pit of Saron",       description = "Plungegrip needs stun/incap/grip before immunity buff.", byClass = {} },
    [1753] = { header = "Seat of the Triumvirate", description = "Void debuffs — dispel priority.", byClass = {} },
    [1209] = { header = "Skyreach",           description = "Solar constructs — interrupts / kiting.", byClass = {} },
    [2805] = { header = "Windrunner Spire",   description = "Focus kill casters, purge/dispel elites.", byClass = {} },
}

------------------------------------------------------------
-- UI
------------------------------------------------------------

local win
local ICON_SIZE = 48
local ICON_PAD  = 6
local BORDER_SIZE = 2

local function styleBox(f, bgA)
    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
        insets   = { left = 0, right = 0, top = 0, bottom = 0 },
    })
    f:SetBackdropColor(0.04, 0.04, 0.04, bgA or 0.95)
    f:SetBackdropBorderColor(0.35, 0.35, 0.35, 1)
end

local function build()
    if win then return win end
    win = CreateFrame("Frame", "YippYappUtilityAdvisor", UIParent, "BackdropTemplate")
    win:SetSize(420, 240)
    win:SetPoint("LEFT", UIParent, "LEFT", 40, -40)
    win:SetMovable(true)
    win:EnableMouse(true)
    win:RegisterForDrag("LeftButton")
    win:SetScript("OnDragStart", win.StartMoving)
    win:SetScript("OnDragStop", win.StopMovingOrSizing)
    win:SetFrameStrata("MEDIUM")
    styleBox(win)
    win:Hide()

    local title = win:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOP", 0, -8)
    win.title = title

    local close = CreateFrame("Button", nil, win, "BackdropTemplate")
    close:SetSize(18, 18)
    close:SetPoint("TOPRIGHT", -4, -4)
    styleBox(close, 0.9)
    local cl = close:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    cl:SetPoint("CENTER"); cl:SetText("x")
    close:SetScript("OnClick", function() win:Hide() end)

    local desc = win:CreateFontString(nil, "OVERLAY")
    desc:SetFont(STANDARD_TEXT_FONT, 12, "")
    desc:SetPoint("TOPLEFT", 14, -34)
    desc:SetPoint("TOPRIGHT", -14, -34)
    desc:SetJustifyH("LEFT")
    desc:SetSpacing(3)
    desc:SetWordWrap(true)
    desc:SetTextColor(0.92, 0.92, 0.92)
    win.desc = desc

    -- Subtle divider between description and icons
    local div = win:CreateTexture(nil, "OVERLAY")
    div:SetColorTexture(0.35, 0.35, 0.35, 0.6)
    div:SetHeight(1)
    win.div = div

    local legend = win:CreateFontString(nil, "OVERLAY")
    legend:SetFont(STANDARD_TEXT_FONT, 10, "")
    legend:SetJustifyH("LEFT")
    legend:SetTextColor(0.55, 0.55, 0.55)
    legend:SetText("|cff33ff33Green|r = baseline      |cffffd100Gold T|r = talent")
    win.legend = legend

    local iconRow = CreateFrame("Frame", nil, win)
    iconRow:SetPoint("BOTTOMLEFT", 14, 16)
    iconRow:SetPoint("BOTTOMRIGHT", -14, 16)
    iconRow:SetHeight(ICON_SIZE + 8)
    win.iconRow = iconRow

    local fallback = win:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    fallback:SetPoint("BOTTOMLEFT", 12, 20)
    fallback:SetPoint("BOTTOMRIGHT", -12, 20)
    fallback:SetJustifyH("LEFT")
    fallback:SetTextColor(0.55, 0.55, 0.55)
    fallback:SetWordWrap(true)
    win.fallback = fallback

    return win
end

local function clearIcons(row)
    for _, c in ipairs({ row:GetChildren() }) do c:Hide(); c:SetParent(nil) end
end

local function normalizeSpell(entry)
    if type(entry) == "number" then return { id = entry } end
    return entry
end

local function makeIcon(parent, entry, xOffset)
    local info = C_Spell and C_Spell.GetSpellInfo and C_Spell.GetSpellInfo(entry.id)
    local tex = info and info.iconID

    local btn = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    btn:SetSize(ICON_SIZE, ICON_SIZE)
    btn:SetPoint("LEFT", parent, "LEFT", xOffset, 4)
    btn:EnableMouse(true)

    -- Colored border via backdrop edge only (no fill overlay)
    btn:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = BORDER_SIZE,
        insets   = { left = 0, right = 0, top = 0, bottom = 0 },
    })
    btn:SetBackdropColor(0, 0, 0, 0)
    if entry.talent then
        btn:SetBackdropBorderColor(1.0, 0.82, 0.0, 1.0)
    else
        btn:SetBackdropBorderColor(0.15, 0.95, 0.35, 1.0)
    end

    local icon = btn:CreateTexture(nil, "ARTWORK")
    icon:SetPoint("TOPLEFT", BORDER_SIZE, -BORDER_SIZE)
    icon:SetPoint("BOTTOMRIGHT", -BORDER_SIZE, BORDER_SIZE)
    icon:SetTexture(tex or 134400)
    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)

    if entry.talent then
        local t = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalLargeOutline")
        t:SetPoint("BOTTOMRIGHT", 2, -2)
        t:SetText("T")
        t:SetTextColor(1, 0.82, 0)
    end

    btn:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetSpellByID(entry.id)
        if entry.note then
            GameTooltip:AddLine(" ")
            GameTooltip:AddLine(entry.note, 0.0, 0.8, 1.0, true)
        end
        if entry.talent then
            GameTooltip:AddLine("|cffffd100Talent — pick this up for this dungeon|r")
        end
        GameTooltip:Show()
    end)
    btn:SetScript("OnLeave", GameTooltip_Hide)

    return btn
end

local function renderDungeon(mapID)
    local w = build()
    local data = UA.DUNGEONS[mapID]
    if not data then
        w.title:SetText("No notes")
        w.desc:SetText("|cffff8080No data for instanceMapID " .. tostring(mapID) .. "|r")
        w.fallback:SetText("")
        w.div:Hide(); w.legend:Hide()
        clearIcons(w.iconRow)
        return
    end

    w.title:SetText(data.header or "Utility")

    -- Description formatting: bold lead sentence in white, followed by the rest.
    local descText = data.description or ""
    local body = descText
    if data.lead then
        body = ("|cffffffff%s|r\n%s"):format(data.lead, descText)
    end
    w.desc:SetText(body)

    clearIcons(w.iconRow)
    local _, class = UnitClass("player")
    local list = data.byClass and data.byClass[class]

    -- Layout: place divider + legend just under the description.
    local descBottom = -34 - w.desc:GetStringHeight() - 8
    w.div:ClearAllPoints()
    w.div:SetPoint("TOPLEFT", w, "TOPLEFT", 14, descBottom)
    w.div:SetPoint("TOPRIGHT", w, "TOPRIGHT", -14, descBottom)
    w.div:Show()

    w.legend:ClearAllPoints()
    w.legend:SetPoint("TOPLEFT", w, "TOPLEFT", 14, descBottom - 6)
    w.legend:Show()

    if not list or #list == 0 then
        w.fallback:SetText(("|cff999999No recommendations seeded for %s yet — add entries to UA.DUNGEONS[%d].byClass.%s|r"):format(class or "your class", mapID, class or ""))
        w:SetHeight(180)
        return
    end

    w.fallback:SetText("")
    local x = 0
    for _, raw in ipairs(list) do
        local entry = normalizeSpell(raw)
        makeIcon(w.iconRow, entry, x)
        x = x + ICON_SIZE + ICON_PAD
    end
    -- Grow window height based on description length
    local baseH = 130
    local textH = w.desc:GetStringHeight() or 40
    w:SetHeight(baseH + textH)
end

------------------------------------------------------------
-- Public API
------------------------------------------------------------

UA._lastShownMapID = nil

function UA:ShowForCurrentInstance(force)
    local _, instType, difficultyID, _, _, _, _, instanceMapID = GetInstanceInfo()
    if not force then
        if instType ~= "party" then
            self._lastShownMapID = nil
            return
        end
        if difficultyID ~= 8 and difficultyID ~= 23 then return end
        if self._lastShownMapID == instanceMapID then return end
        self._lastShownMapID = instanceMapID
    end
    if not self.DUNGEONS[instanceMapID] then
        if force then
            build().title:SetText("No notes")
            build().desc:SetText("No data for instanceMapID " .. tostring(instanceMapID))
            clearIcons(build().iconRow)
            build().fallback:SetText("")
            build():Show()
        end
        return
    end
    renderDungeon(instanceMapID)
    build():Show()
end

function UA:ShowTest()
    renderDungeon(2526) -- Algeth'ar Academy as the sample dungeon
    build():Show()
end

function UA:Hide() if win then win:Hide() end end

------------------------------------------------------------
-- Events
------------------------------------------------------------

local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("CHALLENGE_MODE_START")
f:RegisterEvent("CHALLENGE_MODE_COMPLETED")
f:SetScript("OnEvent", function(_, event, isInitialLogin, isReloadingUi)
    if event == "PLAYER_ENTERING_WORLD" then
        if isInitialLogin or isReloadingUi then return end
        if ns.ModuleEnabled and not ns.ModuleEnabled("utilityAdvisor") then return end
        C_Timer.After(0.5, function() UA:ShowForCurrentInstance() end)
    elseif event == "CHALLENGE_MODE_START" or event == "CHALLENGE_MODE_COMPLETED" then
        UA:Hide()
    end
end)

SLASH_YYHUTILITY1 = "/yyhutility"
SlashCmdList.YYHUTILITY = function() UA:ShowTest() end
