-- PugaHelper by Vaelhys
-- Affiche les symboles L'Ura en temps réel pour tout le raid
-- /puga pour afficher ou cacher le diagramme
-- /pugatemplate pour afficher le template des icônes

local addonName = ...

local symbolTextures = {
    TRI = "Interface\\AddOns\\PugaHelper\\media\\triangle.png",
    TEE = "Interface\\AddOns\\PugaHelper\\media\\t.png",
    DIA = "Interface\\AddOns\\PugaHelper\\media\\diamond.png",
    X   = "Interface\\AddOns\\PugaHelper\\media\\x.png",
    CIR = "Interface\\AddOns\\PugaHelper\\media\\circle.png",
}

local symbolColors = {
    TRI = { 0.4,  1.0,  0.5  },
    TEE = { 0.98, 0.98, 0.99 },
    DIA = { 0.48, 0.20, 0.96 },
    X   = { 1.0,  0.3,  0.3  },
    CIR = { 1.0,  0.64, 0.01 },
}

local slotPositionsHeroic = {
    { x =  55, y =  44 },
    { x =  55, y = -22 },
    { x =   0, y = -60 },
    { x = -55, y = -22 },
    { x = -55, y =  44 },
}

local slotPositionsNormal = {
    { x =  55, y = -22 },
    { x =   0, y = -60 },
    { x = -55, y = -22 },
}

local isHeroic        = true
local maxSymbols      = 5
local currentSequence = {}
local soloSequence    = {}
local displaySlots    = {}
local soloSlots       = {}
local mainFrame         = nil
local soloFrame         = nil
local templateFrame     = nil
local soloTemplateFrame = nil
local soloTemplateLabel = nil
local commandFrame      = nil
local needsUpdate       = false
local needsSoloUpdate   = false
local soloEnabled       = false

local encounterTimers = {}
local inputsOpen      = false

local WINDOWS = {
    { open = 6,    close = 24,   reset = 32,   sound = 36  },
    { open = 76,   close = 94,   reset = 102,  sound = 106 },
    { open = 146,  close = 164,  reset = 172,  sound = 176 },
}

local SOUND_FILE = "Interface/AddOns/PugaHelper/media/Dodge glaives.mp3"

local function playDodgeSound()
    PlaySoundFile(SOUND_FILE, "Master")
end

local function cancelAllTimers()
    for _, t in ipairs(encounterTimers) do
        if t then pcall(function() t:Cancel() end) end
    end
    wipe(encounterTimers)
end

local function hideAndReset()
    inputsOpen = false
    wipe(currentSequence)
    wipe(soloSequence)
    needsUpdate = true
    needsSoloUpdate = true
    if mainFrame then pcall(function() mainFrame:Hide() end) end
    if soloFrame and soloEnabled then pcall(function() soloFrame:Hide() end) end
end

local function lockInputs()
    inputsOpen = false
end

local function openWindow()
    inputsOpen = true
    if mainFrame then pcall(function() mainFrame:Show() end) end
    if soloFrame and soloEnabled then pcall(function() soloFrame:Show() end) end
end

local function startEncounterTimers()
    cancelAllTimers()
    wipe(currentSequence)
    wipe(soloSequence)
    inputsOpen = false
    needsUpdate = true
    needsSoloUpdate = true
    if mainFrame then pcall(function() mainFrame:Hide() end) end
    if soloFrame and soloEnabled then pcall(function() soloFrame:Hide() end) end
    if commandFrame then pcall(function() commandFrame:Hide() end) end
    for _, w in ipairs(WINDOWS) do
        table.insert(encounterTimers, C_Timer.NewTimer(w.open,  openWindow))
        table.insert(encounterTimers, C_Timer.NewTimer(w.close, lockInputs))
        table.insert(encounterTimers, C_Timer.NewTimer(w.reset, hideAndReset))
        table.insert(encounterTimers, C_Timer.NewTimer(w.sound, playDodgeSound))
    end
end

local function stopEncounter()
    cancelAllTimers()
    hideAndReset()
    if commandFrame then pcall(function() commandFrame:Show() end) end
end

local watchedEvents = {
    CHAT_MSG_YELL         = "YELL",
    CHAT_MSG_RAID_WARNING = "RW",
    CHAT_MSG_PING         = "PING",
}

local eventBuffer     = {}
local lastSymbolTime  = 0
local bufferTimer     = nil
local COMBO_WINDOW    = 0.3
local SYMBOL_COOLDOWN = 0.25

local slotSize     = 38
local slotBackdrop = {
    bgFile   = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Buttons\\WHITE8x8",
    edgeSize = 1,
}

local function updateDisplay()
    if not mainFrame then return end
    for i = 1, 5 do
        local slot = displaySlots[i]
        if not slot then break end
        local symbolId = currentSequence[i]
        if symbolId and symbolTextures[symbolId] then
            local color = symbolColors[symbolId]
            slot.icon:SetTexture(symbolTextures[symbolId])
            slot.icon:SetAlpha(1)
            slot.number:SetText(i)
            slot.number:SetAlpha(1)
            slot.frame:SetBackdropColor(color[1]*0.2, color[2]*0.2, color[3]*0.2, 1)
            slot.frame:SetBackdropBorderColor(color[1]*0.9, color[2]*0.9, color[3]*0.9, 1)
        else
            slot.icon:SetAlpha(0)
            slot.number:SetAlpha(0)
            slot.frame:SetBackdropColor(0.1, 0.1, 0.15, 1)
            slot.frame:SetBackdropBorderColor(0.3, 0.3, 0.4, 1)
        end
    end
end

local function updateSoloDisplay()
    if not soloFrame then return end
    for i = 1, 5 do
        local slot = soloSlots[i]
        if not slot then break end
        local symbolId = soloSequence[i]
        if symbolId and symbolTextures[symbolId] then
            local color = symbolColors[symbolId]
            slot.icon:SetTexture(symbolTextures[symbolId])
            slot.icon:SetAlpha(1)
            slot.number:SetText(i)
            slot.number:SetAlpha(1)
            slot.frame:SetBackdropColor(color[1]*0.2, color[2]*0.2, color[3]*0.2, 1)
            slot.frame:SetBackdropBorderColor(color[1]*0.9, color[2]*0.9, color[3]*0.9, 1)
        else
            slot.icon:SetAlpha(0)
            slot.number:SetAlpha(0)
            slot.frame:SetBackdropColor(0.1, 0.1, 0.15, 1)
            slot.frame:SetBackdropBorderColor(0.3, 0.3, 0.4, 1)
        end
    end
end

C_Timer.NewTicker(0.1, function()
    if needsUpdate then
        pcall(updateDisplay)
        needsUpdate = false
    end
    if needsSoloUpdate then
        pcall(updateSoloDisplay)
        needsSoloUpdate = false
    end
end)

local function addSymbol(symbolId)
    if not inputsOpen then return end
    if not symbolTextures[symbolId] then return end
    if #currentSequence >= maxSymbols then return end
    lastSymbolTime = GetTime()
    table.insert(currentSequence, symbolId)
    needsUpdate = true
end

local function addSoloSymbol(symbolId)
    if not soloFrame or not soloFrame:IsShown() then return end
    if not symbolTextures[symbolId] then return end
    if #soloSequence >= maxSymbols then return end
    table.insert(soloSequence, symbolId)
    needsSoloUpdate = true
end

local function removeSoloLastSymbol()
    if not soloFrame or not soloFrame:IsShown() then return end
    if #soloSequence == 0 then return end
    table.remove(soloSequence)
    needsSoloUpdate = true
end

local function analyzeBuffer()
    local hasYell = eventBuffer["YELL"]
    local hasPing = eventBuffer["PING"]
    local hasRW   = eventBuffer["RW"]
    local symbol  = nil
    if hasYell and hasPing then
        symbol = "TRI"
    elseif hasPing and hasRW then
        symbol = "DIA"
    elseif hasPing then
        symbol = "CIR"
    elseif hasRW then
        symbol = "X"
    elseif hasYell then
        symbol = "TEE"
    end
    wipe(eventBuffer)
    bufferTimer = nil
    if symbol then addSymbol(symbol) end
end

local function onEventReceived(eventType)
    local now = GetTime()
    if now - lastSymbolTime < SYMBOL_COOLDOWN then return end
    eventBuffer[eventType] = true
    if not bufferTimer then
        bufferTimer = C_Timer.NewTimer(COMBO_WINDOW, analyzeBuffer)
    end
end

local messageListener = CreateFrame("Frame")
for eventName in pairs(watchedEvents) do
    messageListener:RegisterEvent(eventName)
end
messageListener:SetScript("OnEvent", function(self, event)
    local eventType = watchedEvents[event]
    if eventType then onEventReceived(eventType) end
end)

local function applyMode()
    wipe(currentSequence)
    wipe(soloSequence)
    needsUpdate = true
    needsSoloUpdate = true
    local positions = isHeroic and slotPositionsHeroic or slotPositionsNormal
    maxSymbols = isHeroic and 5 or 3

    -- mise à jour mainFrame slots
    for i = 1, 5 do
        if displaySlots[i] then displaySlots[i].frame:Hide() end
    end
    for i = 1, maxSymbols do
        local slot = displaySlots[i]
        local pos  = positions[i]
        if slot and pos then
            slot.frame:ClearAllPoints()
            slot.frame:SetPoint("CENTER", mainFrame, "CENTER", pos.x, pos.y)
            slot.frame:Show()
        end
    end

    -- mise à jour soloFrame slots
    for i = 1, 5 do
        if soloSlots[i] then soloSlots[i].frame:Hide() end
    end
    for i = 1, maxSymbols do
        local slot = soloSlots[i]
        local pos  = positions[i]
        if slot and pos then
            slot.frame:ClearAllPoints()
            slot.frame:SetPoint("CENTER", soloFrame, "CENTER", pos.x, pos.y)
            slot.frame:Show()
        end
    end
end

-- template raid : 5 icônes fixes, clickthrough, figé en combat
local TEMPLATE_ORDER = { "TRI", "DIA", "CIR", "X", "TEE" }
local ICON_SIZE      = 44
local ICON_GAP       = 6

local function applyHighlight(iconBg, symbolId, isHovered)
    local color = symbolColors[symbolId]
    if isHovered then
        iconBg:SetBackdropColor(color[1]*0.4, color[2]*0.4, color[3]*0.4, 1)
        iconBg:SetBackdropBorderColor(color[1], color[2], color[3], 1)
    else
        iconBg:SetBackdropColor(color[1]*0.2, color[2]*0.2, color[3]*0.2, 1)
        iconBg:SetBackdropBorderColor(color[1]*0.8, color[2]*0.8, color[3]*0.8, 1)
    end
end

local function buildTemplateFrame()
    local totalWidth = #TEMPLATE_ORDER * ICON_SIZE + (#TEMPLATE_ORDER - 1) * ICON_GAP
    local HANDLE_W   = 10

    local f = CreateFrame("Frame", "PugaHelperTemplateFrame", UIParent)
    f:SetSize(totalWidth + 16 + HANDLE_W, ICON_SIZE + 16)
    f:SetPoint("CENTER", UIParent, "CENTER", 0, -200)
    f:SetMovable(true)
    f:SetClampedToScreen(true)
    f:SetFrameStrata("HIGH")

    local bg = f:CreateTexture(nil, "BACKGROUND")
    bg:SetPoint("TOPLEFT",     f, "TOPLEFT",     0,        0)
    bg:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -HANDLE_W, 0)
    bg:SetColorTexture(0.05, 0.05, 0.05, 1)

    local label = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label:SetPoint("BOTTOMLEFT", f, "TOPLEFT", 4, 2)
    label:SetText("|cff8888ffDrag over your macros (click-through)|r")

    for i, symbolId in ipairs(TEMPLATE_ORDER) do
        local color = symbolColors[symbolId]
        local xPos  = 8 + (i - 1) * (ICON_SIZE + ICON_GAP)

        local iconBg = CreateFrame("Frame", nil, f, "BackdropTemplate")
        iconBg:SetSize(ICON_SIZE, ICON_SIZE)
        iconBg:SetPoint("TOPLEFT", f, "TOPLEFT", xPos, -8)
        iconBg:SetBackdrop(slotBackdrop)
        iconBg:SetBackdropColor(color[1]*0.2, color[2]*0.2, color[3]*0.2, 1)
        iconBg:SetBackdropBorderColor(color[1]*0.8, color[2]*0.8, color[3]*0.8, 1)
        iconBg:EnableMouse(false)

        local tex = iconBg:CreateTexture(nil, "ARTWORK")
        tex:SetSize(ICON_SIZE - 8, ICON_SIZE - 8)
        tex:SetPoint("CENTER", 0, 0)
        tex:SetTexture(symbolTextures[symbolId])
    end

    -- poignée de drag (désactivée en combat)
    local handle = CreateFrame("Frame", nil, f, "BackdropTemplate")
    handle:SetSize(HANDLE_W, ICON_SIZE + 16)
    handle:SetPoint("RIGHT", f, "RIGHT", 0, 0)
    handle:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    handle:SetBackdropColor(0.5, 0.5, 0.5, 0.4)
    handle:SetBackdropBorderColor(0.6, 0.6, 0.6, 0.5)
    handle:EnableMouse(true)
    handle:RegisterForDrag("LeftButton")
    handle:SetScript("OnDragStart", function()
        if InCombatLockdown() then return end
        f:StartMoving()
    end)
    handle:SetScript("OnDragStop", function()
        f:StopMovingOrSizing()
        local point, _, relativePoint, x, y = f:GetPoint()
        PugaHelperDB = PugaHelperDB or {}
        PugaHelperDB.tplPoint         = point
        PugaHelperDB.tplRelativePoint = relativePoint
        PugaHelperDB.tplX             = x
        PugaHelperDB.tplY             = y
    end)

    f:Hide()
    templateFrame = f
end

-- solo template : 5 icônes cliquables, clic gauche = ajouter, clic droit = retirer le dernier
local function buildSoloTemplateFrame()
    local totalWidth = #TEMPLATE_ORDER * ICON_SIZE + (#TEMPLATE_ORDER - 1) * ICON_GAP
    local HANDLE_W   = 10

    local f = CreateFrame("Frame", "PugaHelperSoloTemplateFrame", UIParent)
    f:SetSize(totalWidth + 16 + HANDLE_W, ICON_SIZE + 16)
    f:SetPoint("CENTER", UIParent, "CENTER", 0, -240)
    f:SetMovable(true)
    f:SetClampedToScreen(true)
    f:SetFrameStrata("HIGH")

    local bg = f:CreateTexture(nil, "BACKGROUND")
    bg:SetPoint("TOPLEFT",     f, "TOPLEFT",     0,        0)
    bg:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -HANDLE_W, 0)
    bg:SetColorTexture(0.05, 0.05, 0.10, 1)

    -- label solo, mis à jour par le bouton toggle
    soloTemplateLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    soloTemplateLabel:SetPoint("BOTTOMLEFT", f, "TOPLEFT", 4, 2)
	soloTemplateLabel:SetText("|cff8888ffSOLO|r |cffff4444(disabled during encounter)|r")

    for i, symbolId in ipairs(TEMPLATE_ORDER) do
        local color = symbolColors[symbolId]
        local xPos  = 8 + (i - 1) * (ICON_SIZE + ICON_GAP)

        local iconBg = CreateFrame("Button", nil, f, "BackdropTemplate")
        iconBg:SetSize(ICON_SIZE, ICON_SIZE)
        iconBg:SetPoint("TOPLEFT", f, "TOPLEFT", xPos, -8)
        iconBg:SetBackdrop(slotBackdrop)
        iconBg:SetBackdropColor(color[1]*0.2, color[2]*0.2, color[3]*0.2, 1)
        iconBg:SetBackdropBorderColor(color[1]*0.8, color[2]*0.8, color[3]*0.8, 1)
        iconBg:EnableMouse(true)
        iconBg:RegisterForClicks("LeftButtonUp", "RightButtonUp")

        iconBg:SetScript("OnEnter", function(self)
            applyHighlight(self, symbolId, true)
        end)
        iconBg:SetScript("OnLeave", function(self)
            applyHighlight(self, symbolId, false)
        end)
        iconBg:SetScript("OnClick", function(self, button)
            if button == "LeftButton" then
                addSoloSymbol(symbolId)
            elseif button == "RightButton" then
                removeSoloLastSymbol()
            end
        end)

        local tex = iconBg:CreateTexture(nil, "ARTWORK")
        tex:SetSize(ICON_SIZE - 8, ICON_SIZE - 8)
        tex:SetPoint("CENTER", 0, 0)
        tex:SetTexture(symbolTextures[symbolId])
    end

    -- poignée de drag
    local handle = CreateFrame("Frame", nil, f, "BackdropTemplate")
    handle:SetSize(HANDLE_W, ICON_SIZE + 16)
    handle:SetPoint("RIGHT", f, "RIGHT", 0, 0)
    handle:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    handle:SetBackdropColor(0.4, 0.4, 0.6, 0.4)
    handle:SetBackdropBorderColor(0.5, 0.5, 0.7, 0.5)
    handle:EnableMouse(true)
    handle:RegisterForDrag("LeftButton")
    handle:SetScript("OnDragStart", function()
        if InCombatLockdown() then return end
        f:StartMoving()
    end)
    handle:SetScript("OnDragStop", function()
        f:StopMovingOrSizing()
        local point, _, relativePoint, x, y = f:GetPoint()
        PugaHelperDB = PugaHelperDB or {}
        PugaHelperDB.soloTplPoint         = point
        PugaHelperDB.soloTplRelativePoint = relativePoint
        PugaHelperDB.soloTplX             = x
        PugaHelperDB.soloTplY             = y
    end)

    f:Hide()
    soloTemplateFrame = f
end

-- frame de commandes : draggable, visible hors combat, cachée en combat
local function buildCommandFrame()
    local f = CreateFrame("Frame", "PugaHelperCommandFrame", UIParent, "BackdropTemplate")
    f:SetSize(160, 192)
    f:SetPoint("CENTER", UIParent, "CENTER", 150, 150)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetClampedToScreen(true)
    f:SetFrameStrata("HIGH")
    f:SetBackdrop({
        bgFile   = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    f:SetBackdropColor(0.08, 0.08, 0.08, 0.95)
    f:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.8)

    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        local point, _, relativePoint, x, y = self:GetPoint()
        PugaHelperDB = PugaHelperDB or {}
        PugaHelperDB.cmdPoint         = point
        PugaHelperDB.cmdRelativePoint = relativePoint
        PugaHelperDB.cmdX             = x
        PugaHelperDB.cmdY             = y
    end)

    local function makeBtn(label, yOffset, onClick)
        local btn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
        btn:SetSize(144, 24)
        btn:SetPoint("TOP", f, "TOP", 0, yOffset)
        btn:SetText(label)
        btn:SetScript("OnClick", onClick)

        -- highlight au survol
        btn:SetScript("OnEnter", function(self)
            self:SetAlpha(0.85)
        end)
        btn:SetScript("OnLeave", function(self)
            self:SetAlpha(1)
        end)

        return btn
    end

    makeBtn("Show/Hide diagram", -8, function()
        if not mainFrame then return end
        if mainFrame:IsShown() then mainFrame:Hide() else mainFrame:Show() end
    end)

    makeBtn("Enable Heroic version", -34, function()
        if isHeroic then return end
        isHeroic = true
        applyMode()
    end)

    makeBtn("Enable Normal version", -60, function()
        if not isHeroic then return end
        isHeroic = false
        applyMode()
    end)

    makeBtn("Create macros", -86, function()
        local macros = {
            { name = "PugaTriangle", icon = "INV_Mask_07",                  text = "/yell .\n/ping assist" },
            { name = "PugaDiamond",  icon = "INV_Mask_10",                  text = "/rw .\n/ping assist" },
            { name = "PugaCircle",   icon = "INV_Mask_08",                  text = "/ping assist" },
            { name = "PugaX",        icon = "INV_Mask_09",                  text = "/rw ." },
            { name = "PugaT",        icon = "INV_Helm_Mask_MetalBand_A_01", text = "/yell ." },
        }
        for _, m in ipairs(macros) do
            local index = GetMacroIndexByName(m.name)
            if index == 0 then
                CreateMacro(m.name, m.icon, m.text, nil)
            else
                EditMacro(index, m.name, m.icon, m.text)
            end
        end
        print("|cff00ff00PugaHelper:|r Macros créées !")
    end)

    makeBtn("Show/Hide template", -112, function()
        if not templateFrame then return end
        if templateFrame:IsShown() then templateFrame:Hide() else templateFrame:Show() end
    end)

    makeBtn("Show/Hide solo mode", -138, function()
        if not soloFrame or not soloTemplateFrame then return end
        if soloFrame:IsShown() then
            soloFrame:Hide()
            soloTemplateFrame:Hide()
        else
            soloFrame:Show()
            soloTemplateFrame:Show()
        end
    end)

    -- bouton toggle : active ou désactive le solo en combat
    local toggleBtn = makeBtn("", -164, function()
        soloEnabled = not soloEnabled
    end)
    local function updateToggleBtn()
        if soloEnabled then
            toggleBtn:SetText("|cff66ff66Solo mode enabled|r")
            if soloTemplateLabel then
				soloTemplateLabel:SetText("|cff8888ffSOLO (|r|cff66ff66enabled during encounter|r|cff8888ff)|r")
            end
        else
            toggleBtn:SetText("|cffff4444Solo mode disabled|r")
            if soloTemplateLabel then
                soloTemplateLabel:SetText("|cff8888ffSOLO (|r|cffff4444disabled during encounter|r|cff8888ff)|r")
            end
        end
    end
    updateToggleBtn()
    -- on accroche la mise à jour au clic
    toggleBtn:HookScript("OnClick", updateToggleBtn)

    commandFrame = f
end

-- construction du mainFrame (broadcast)
local function buildMainFrame(frameName, slotTable, isMain)
    local f = CreateFrame("Frame", frameName, UIParent)
    f:SetSize(210, 210)
    f:SetPoint("CENTER", UIParent, "CENTER", isMain and 0 or 250, 150)
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        local point, _, relativePoint, x, y = self:GetPoint()
        PugaHelperDB = PugaHelperDB or {}
        local prefix = isMain and "" or "solo"
        PugaHelperDB[prefix.."point"]         = point
        PugaHelperDB[prefix.."relativePoint"] = relativePoint
        PugaHelperDB[prefix.."x"]             = x
        PugaHelperDB[prefix.."y"]             = y
    end)
    f:SetClampedToScreen(true)
    f:SetFrameStrata("HIGH")

    local background = f:CreateTexture(nil, "BACKGROUND")
    background:SetPoint("CENTER", 0, 0)
    background:SetSize(220, 220)
    background:SetColorTexture(0.314, 0.306, 0.306, 0.92)

    local backgroundMask = f:CreateMaskTexture()
    backgroundMask:SetAllPoints(background)
    backgroundMask:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
    background:AddMaskTexture(backgroundMask)

    local bossImage = f:CreateTexture(nil, "ARTWORK")
    bossImage:SetSize(96, 96)
    bossImage:SetPoint("CENTER", 0, 8)
    bossImage:SetTexture("Interface\\AddOns\\PugaHelper\\media\\Lura.png")

    local bossMask = f:CreateMaskTexture()
    bossMask:SetAllPoints(bossImage)
    bossMask:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
    bossImage:AddMaskTexture(bossMask)

    local tankImage = f:CreateTexture(nil, "ARTWORK")
    tankImage:SetSize(24, 24)
    tankImage:SetPoint("BOTTOM", bossImage, "TOP", 0, 2)
    tankImage:SetTexture("Interface\\AddOns\\PugaHelper\\media\\tank.png")

    -- label "SOLO" pour différencier visuellement
    if not isMain then
        local soloLabel = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        soloLabel:SetPoint("TOP", f, "TOP", 0, -6)
        soloLabel:SetText("|cff8888ffSOLO|r")
    end

    for i = 1, 5 do
        local pos = slotPositionsHeroic[i]

        local slotFrame = CreateFrame("Frame", nil, f, "BackdropTemplate")
        slotFrame:SetSize(slotSize, slotSize)
        slotFrame:SetPoint("CENTER", f, "CENTER", pos.x, pos.y)
        slotFrame:SetBackdrop(slotBackdrop)
        slotFrame:SetBackdropColor(0.1, 0.1, 0.15, 1)
        slotFrame:SetBackdropBorderColor(0.3, 0.3, 0.4, 1)

        local iconTexture = slotFrame:CreateTexture(nil, "ARTWORK")
        iconTexture:SetSize(slotSize - 8, slotSize - 8)
        iconTexture:SetPoint("CENTER", 0, 0)
        iconTexture:SetAlpha(0)
        iconTexture:Show()

        local numberLabel = slotFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        numberLabel:SetPoint("CENTER", 0, -28)
        numberLabel:SetText("")
        numberLabel:SetTextColor(1, 1, 1, 1)
        numberLabel:SetAlpha(0)
        numberLabel:SetScale(1.2)
        numberLabel:Show()

        slotTable[i] = {
            frame  = slotFrame,
            icon   = iconTexture,
            number = numberLabel,
        }
    end

    f:Show()
    return f
end

local function buildFrame()
    mainFrame = buildMainFrame("PugaHelperMainFrame", displaySlots, true)
end

local function buildSoloFrame()
    soloFrame = buildMainFrame("PugaHelperSoloFrame", soloSlots, false)
    soloFrame:Hide()
end

SLASH_PUGAMACRO1 = "/pugamacro"
SlashCmdList["PUGAMACRO"] = function()
    local macros = {
        { name = "PugaTriangle", icon = "INV_Mask_07",                  text = "/yell .\n/ping assist" },
        { name = "PugaDiamond",  icon = "INV_Mask_10",                  text = "/rw .\n/ping assist" },
        { name = "PugaCircle",   icon = "INV_Mask_08",                  text = "/ping assist" },
        { name = "PugaX",        icon = "INV_Mask_09",                  text = "/rw ." },
        { name = "PugaT",        icon = "INV_Helm_Mask_MetalBand_A_01", text = "/yell ." },
    }
    for _, m in ipairs(macros) do
        local index = GetMacroIndexByName(m.name)
        if index == 0 then
            CreateMacro(m.name, m.icon, m.text, nil)
            print("|cff66ccffPugaHelper:|r Macro créée -> " .. m.name)
        else
            EditMacro(index, m.name, m.icon, m.text)
            print("|cff66ccffPugaHelper:|r Macro mise à jour -> " .. m.name)
        end
    end
    print("|cff00ff00PugaHelper:|r Toutes les macros sont prêtes !")
end

local loader = CreateFrame("Frame")
loader:RegisterEvent("ADDON_LOADED")
loader:RegisterEvent("PLAYER_LOGIN")
loader:RegisterEvent("ENCOUNTER_START")
loader:RegisterEvent("ENCOUNTER_END")

-- boss autorisés
local WATCHED_ENCOUNTERS = {
    [3183] = true,  -- Midnight Falls
    [1853] = true,  -- Nythendra (tests)
}

loader:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local name = ...
        if name == addonName then
            PugaHelperDB = PugaHelperDB or {}
            buildFrame()
            buildSoloFrame()
            buildTemplateFrame()
            buildSoloTemplateFrame()
            buildCommandFrame()
            applyMode()

            -- restauration positions mainFrame
            if PugaHelperDB.x then
                mainFrame:ClearAllPoints()
                mainFrame:SetPoint(PugaHelperDB.point, UIParent, PugaHelperDB.relativePoint, PugaHelperDB.x, PugaHelperDB.y)
            end
            -- restauration positions soloFrame
            if PugaHelperDB.solox then
                soloFrame:ClearAllPoints()
                soloFrame:SetPoint(PugaHelperDB.solopoint, UIParent, PugaHelperDB.solorelativePoint, PugaHelperDB.solox, PugaHelperDB.soloy)
            end
            -- restauration positions templateFrame
            if PugaHelperDB.tplX then
                templateFrame:ClearAllPoints()
                templateFrame:SetPoint(PugaHelperDB.tplPoint, UIParent, PugaHelperDB.tplRelativePoint, PugaHelperDB.tplX, PugaHelperDB.tplY)
            end
            -- restauration positions soloTemplateFrame
            if PugaHelperDB.soloTplX then
                soloTemplateFrame:ClearAllPoints()
                soloTemplateFrame:SetPoint(PugaHelperDB.soloTplPoint, UIParent, PugaHelperDB.soloTplRelativePoint, PugaHelperDB.soloTplX, PugaHelperDB.soloTplY)
            end
            -- restauration positions commandFrame
            if PugaHelperDB.cmdX then
                commandFrame:ClearAllPoints()
                commandFrame:SetPoint(PugaHelperDB.cmdPoint, UIParent, PugaHelperDB.cmdRelativePoint, PugaHelperDB.cmdX, PugaHelperDB.cmdY)
            end
            DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffPugaHelper:|r Loaded. /puga pour afficher.")
        end
    elseif event == "PLAYER_LOGIN" then
        SLASH_PUGAHELPER1 = "/puga"
        SlashCmdList["PUGAHELPER"] = function()
            if not mainFrame then return end
            if mainFrame:IsShown() then mainFrame:Hide() else mainFrame:Show() end
        end
        SLASH_PUGATEMPLATE1 = "/pugatemplate"
        SlashCmdList["PUGATEMPLATE"] = function()
            if not templateFrame then return end
            if templateFrame:IsShown() then templateFrame:Hide() else templateFrame:Show() end
        end
    elseif event == "ENCOUNTER_START" then
        local encounterID = ...
        if WATCHED_ENCOUNTERS[encounterID] then
            startEncounterTimers()
        end
    elseif event == "ENCOUNTER_END" then
        stopEncounter()
    end
end)