-- Mend My Pet - TWW 11.2.5
-- Author: Steamstalker

------------------------------------------------------------
-- Spell info via C_Spell
------------------------------------------------------------
local REVIVE_ID, MEND_ID = 982, 136
local reviveInfo = C_Spell.GetSpellInfo(REVIVE_ID)
local mendInfo   = C_Spell.GetSpellInfo(MEND_ID)

local REVIVE_NAME = (reviveInfo and reviveInfo.name) or "Revive Pet"
local MEND_NAME   = (mendInfo   and mendInfo.name)   or "Mend Pet"
local REVIVE_ICON = (reviveInfo and reviveInfo.iconID) or 134400
local MEND_ICON   = (mendInfo   and mendInfo.iconID)   or 134400

-- Folder name of this addon (used to build file paths)
local ADDON_NAME = select(1, ...) or "MendMyPet"

------------------------------------------------------------
-- SavedVariables
------------------------------------------------------------
local db -- assigné dans ADDON_LOADED

------------------------------------------------------------
-- (Legacy) Sound options (not used directly anymore but kept for reference)
------------------------------------------------------------
local SOUND_OPTIONS = {
  { text = "Raid Warning", value = "RAID_WARNING" },
  { text = "Ready Check", value = "READYCHECK" },
  { text = "Tell Message", value = "TELL_MESSAGE" },
  { text = "Alarm Clock", value = "ALARM_CLOCK_WARNING_3" },
  { text = "PVP Enter Queue", value = "PVP_ENTER_QUEUE" },
}

------------------------------------------------------------
-- Utils
------------------------------------------------------------
local function HasMendPetHOT()
  if not UnitExists("pet") then return false end
  local aura = AuraUtil.FindAuraByName(MEND_NAME, "pet", "HELPFUL")
  return aura ~= nil
end

local function SetPercentFont(fs, px)
  px = math.max(12, math.floor(px))
  fs:SetFont("Fonts\\FRIZQT__.TTF", px, "OUTLINE")
  fs:SetShadowOffset(1, -1)
end

local function FitTextToBox(fs, text, maxWidth, basePx)
  local px = basePx
  fs:SetText(text)
  SetPercentFont(fs, px)
  local tries = 8
  while fs:GetStringWidth() > maxWidth and tries > 0 do
    px = px - 2
    if px < 10 then break end
    SetPercentFont(fs, px)
    fs:SetText(text)
    tries = tries - 1
  end
end

local function isPetDead()
  if not UnitExists("pet") then return false end
  return UnitIsDead("pet") or UnitIsDeadOrGhost("pet") or (UnitHealthMax("pet") == 0)
end

------------------------------------------------------------
-- Root frame & events
------------------------------------------------------------
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("ADDON_LOADED")
f:RegisterEvent("UNIT_PET")
f:RegisterEvent("UNIT_HEALTH")
f:RegisterEvent("UNIT_MAXHEALTH")
f:RegisterEvent("PLAYER_REGEN_ENABLED")
f:RegisterEvent("PLAYER_REGEN_DISABLED")
f:RegisterEvent("UNIT_SPELLCAST_START")
f:RegisterEvent("UNIT_SPELLCAST_STOP")
f:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
f:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
f:RegisterEvent("UNIT_SPELLCAST_FAILED")
f:RegisterEvent("SPELL_UPDATE_COOLDOWN")

local reviveInProgress = false
local petKnownDead = false

------------------------------------------------------------
-- Secure button
------------------------------------------------------------
local btn = CreateFrame("Button", "MMP_Button", UIParent, "SecureActionButtonTemplate")
btn:SetPoint("CENTER", UIParent, "CENTER", 0, -120)
btn:SetSize(64, 64)
btn:SetFrameStrata("MEDIUM")
btn:SetFrameLevel(20)
btn:EnableMouse(true)
btn:RegisterForClicks("AnyUp", "AnyDown")
btn:SetMovable(true)
btn:SetClampedToScreen(true)
btn:Hide()

-- Fond
local bg = btn:CreateTexture(nil, "BACKGROUND")
bg:SetAllPoints(btn)
bg:SetColorTexture(0, 0, 0, 0.20)

-- Anneau Blizzard
local ring = btn:CreateTexture(nil, "BORDER")
ring:SetTexture("Interface/Buttons/UI-Quickslot2")
ring:SetAllPoints(btn)

-- Icône
local icon = btn:CreateTexture(nil, "ARTWORK")
icon:SetPoint("TOPLEFT", btn, "TOPLEFT", 4, -4)
icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -4, 4)
icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)

-- Cooldown spiral
local cooldown = CreateFrame("Cooldown", nil, btn, "CooldownFrameTemplate")
cooldown:SetAllPoints(icon)
cooldown:SetDrawEdge(false)
cooldown:SetDrawBling(false)

-- Highlight
local hl = btn:CreateTexture(nil, "HIGHLIGHT")
hl:SetTexture("Interface/Buttons/ButtonHilight-Square")
hl:SetBlendMode("ADD")
hl:SetAllPoints(btn)
btn:SetHighlightTexture(hl)

-- Pushed
local pushed = btn:CreateTexture(nil, "OVERLAY")
pushed:SetAllPoints(btn)
pushed:SetColorTexture(1, 1, 1, 0.10)
btn:SetPushedTexture(pushed)

-- Pourcentage
local pctText = btn:CreateFontString(nil, "OVERLAY")
pctText:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -5, 5)
pctText:SetJustifyH("RIGHT")
pctText:SetJustifyV("BOTTOM")
pctText:SetWordWrap(false)
pctText:SetNonSpaceWrap(false)
pctText:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
pctText:SetText("")

------------------------------------------------------------
-- Alerts
------------------------------------------------------------
local function SetAlert(on)
  if db and db.glow == false then on = false end
  if on then
    if ActionButton_ShowOverlayGlow then ActionButton_ShowOverlayGlow(btn) end
  else
    if ActionButton_HideOverlayGlow then ActionButton_HideOverlayGlow(btn) end
  end
end

local function FlashScreenOnce()
  if not (db and db.flash) then return end
  if not LowHealthFrame then return end
  UIFrameFlash(LowHealthFrame, 0.2, 0.5, 0.7, false, 0, 0)
end

-- Play sound using Blizzard kits or our 1..6.wav customs
local function PlayPetAlertSound()
  if not (db and db.sound) then return end
  local key = db.soundKit or "RAID_WARNING"
  local kit = SOUNDKIT[key]
  if kit then
    PlaySound(kit, "Master")
    return
  end
  local n = key:match("^CUSTOM([1-6])$")
  if n then
    local file = "Interface\\AddOns\\"..ADDON_NAME.."\\"..n..".wav"
    PlaySoundFile(file, "Master")
  end
end

------------------------------------------------------------
-- Macro
------------------------------------------------------------
local function ApplyMacro()
  btn:SetAttribute("type", "macro")
  btn:SetAttribute("macrotext",
    "/cast [@pet,dead][nopet] " .. REVIVE_NAME .. "\n" ..
    "/cast [@pet,exists] " .. MEND_NAME
  )
end

------------------------------------------------------------
-- Drag (ALT + Left drag)
------------------------------------------------------------
btn:SetMovable(true)

local function savePos(self)
  local point, _, relPoint, x, y = self:GetPoint()
  db.pos = { point, relPoint, x, y }
end

btn:HookScript("OnMouseDown", function(self, button)
  if button ~= "LeftButton" then return end
  if not IsAltKeyDown() then return end
  if db.locked then return end
  if InCombatLockdown() then
    print("|cff5fd38d[MMP]|r Can't move in combat.")
    return
  end
  self.__dragging = true
  self:StartMoving()
end)

btn:HookScript("OnMouseUp", function(self, button)
  if self.__dragging then
    self.__dragging = false
    self:StopMovingOrSizing()
    savePos(self)
  end
end)

btn:HookScript("OnHide", function(self)
  if self.__dragging then
    self.__dragging = false
    self:StopMovingOrSizing()
    savePos(self)
  end
end)

------------------------------------------------------------
-- Tooltip
------------------------------------------------------------
btn:SetScript("OnEnter", function(self)
  GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
  GameTooltip:AddLine("Mend My Pet", 1, 1, 1)
  GameTooltip:AddLine("Click: "..MEND_NAME.." (alive) / "..REVIVE_NAME.." (dead)", 0.8, 0.8, 0.8, true)
  GameTooltip:Show()
end)
btn:SetScript("OnLeave", function() GameTooltip:Hide() end)

------------------------------------------------------------
-- Layout
------------------------------------------------------------
local function layoutToSize()
  ring:SetAllPoints(btn)
  hl:SetAllPoints(btn)
  pushed:SetAllPoints(btn)
  bg:SetAllPoints(btn)
  cooldown:SetAllPoints(icon)

  local pad = math.max(4, math.floor(btn:GetWidth() * 0.06))
  icon:ClearAllPoints()
  icon:SetPoint("TOPLEFT", btn, "TOPLEFT", pad, -pad)
  icon:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -pad, pad)

  btn.__textMaxW  = math.max(20, btn:GetWidth() - (pad * 2 + 6))
  btn.__textBasePx = math.floor((db.size or 64) * 0.40)
end

------------------------------------------------------------
-- Cooldown updater
------------------------------------------------------------
local function UpdateCooldown(spellID)
  local a, b, c, d = C_Spell.GetSpellCooldown(spellID)
  local start, duration, enable, modRate

  if type(a) == "table" then
    start    = a.startTime or 0
    duration = a.duration or 0
    enable   = (a.isEnabled and 1 or 0)
    modRate  = a.modRate or 1
  else
    start, duration, enable, modRate = a, b, c, (d or 1)
    start    = start or 0
    duration = duration or 0
    enable   = enable or 0
  end

  if enable == 1 and start > 0 and duration > 0 then
    if modRate and cooldown.SetCooldown then
      cooldown:SetCooldown(start, duration, modRate)
    else
      cooldown:SetCooldown(start, duration)
    end
  else
    if cooldown.Clear then
      cooldown:Clear()
    elseif cooldown.ClearCooldown then
      cooldown:ClearCooldown()
    else
      cooldown:SetCooldown(0, 0)
    end
  end
end

------------------------------------------------------------
-- Visuels & logique
------------------------------------------------------------
local function UpdateVisuals()
  local exists = UnitExists("pet")
  local dead = exists and isPetDead() or false

  if exists then
    if dead then petKnownDead = true else petKnownDead = false end
  end

  if (petKnownDead or reviveInProgress) then
    icon:SetTexture(REVIVE_ICON)
    icon:SetDesaturated(false)
    FitTextToBox(pctText, "DEAD", btn.__textMaxW, btn.__textBasePx)
    SetAlert(true)
    UpdateCooldown(REVIVE_ID)
    return
  end

  if not exists then
    icon:SetTexture(MEND_ICON)
    icon:SetDesaturated(true)
    FitTextToBox(pctText, "", btn.__textMaxW, btn.__textBasePx)
    SetAlert(false)
    if cooldown.Clear then cooldown:Clear() end
    return
  end

  -- Vivant
  icon:SetTexture(MEND_ICON)
  icon:SetDesaturated(false)

  local hp, maxhp = UnitHealth("pet"), UnitHealthMax("pet")
  if maxhp and maxhp > 0 then
    local pct = math.floor((hp / maxhp) * 100 + 0.5)
    FitTextToBox(pctText, (pct .. "%"), btn.__textMaxW, btn.__textBasePx)
    local needMend = (pct <= db.threshold) and (not HasMendPetHOT())
    SetAlert(needMend)

    if needMend then
      if not btn.__flashed then
        FlashScreenOnce()
        PlayPetAlertSound()
        btn.__flashed = true
      end
    else
      btn.__flashed = false
    end
  else
    FitTextToBox(pctText, "", btn.__textMaxW, btn.__textBasePx)
    SetAlert(false)
  end

  UpdateCooldown(MEND_ID)
end

local function RestorePositionAndSize()
  btn:ClearAllPoints()
  if db.pos then
    btn:SetPoint(db.pos[1], UIParent, db.pos[2], db.pos[3], db.pos[4])
  else
    btn:SetPoint("CENTER", UIParent, "CENTER", 0, -120)
  end
  btn:SetSize(db.size or 64, db.size or 64)
  layoutToSize()
  UpdateVisuals()
end

------------------------------------------------------------
-- Options panel
------------------------------------------------------------
local function CreateOptionsPanel()
  local category, layout = Settings.RegisterVerticalLayoutCategory("Mend My Pet")
  _G.MMP_SettingsCategory = category  -- ← mémorise l’objet catégorie pour OpenToCategory()

  -- Taille
  local sizeSetting = Settings.RegisterProxySetting(
    category, "mmp_size", Settings.VarType.Number, "Button Size",
    (db and db.size) or 64,
    function() return (db and db.size) or 64 end,
    function(value) db.size = math.floor(value); RestorePositionAndSize() end
  )
  local sizeOpts = Settings.CreateSliderOptions(28, 128, 1)
  Settings.CreateSlider(category, sizeSetting, sizeOpts)

  -- Seuil
  local thSetting = Settings.RegisterProxySetting(
    category, "mmp_threshold", Settings.VarType.Number, "Alert Threshold (%)",
    (db and db.threshold) or 35,
    function() return (db and db.threshold) or 35 end,
    function(value) db.threshold = math.floor(value); UpdateVisuals() end
  )
  local thOpts = Settings.CreateSliderOptions(0, 100, 1)
  Settings.CreateSlider(category, thSetting, thOpts)

  -- Lock
  local lockSetting = Settings.RegisterProxySetting(
    category, "mmp_locked", Settings.VarType.Boolean, "Lock Button Position",
    (db and db.locked) or false,
    function() return (db and db.locked) or false end,
    function(value) db.locked = value end
  )
  Settings.CreateCheckbox(category, lockSetting, "Lock")

  -- Glow
  local glowSetting = Settings.RegisterProxySetting(
    category, "mmp_glow", Settings.VarType.Boolean, "Enable Alert Glow",
    (db and db.glow) ~= false,
    function() return (db and db.glow) ~= false end,
    function(value) db.glow = value; UpdateVisuals() end
  )
  Settings.CreateCheckbox(category, glowSetting, "Show glow around the button when pet needs healing")

  -- Flash
  local flashSetting = Settings.RegisterProxySetting(
    category, "mmp_flash", Settings.VarType.Boolean, "Flash Screen Alert",
    (db and db.flash) ~= false,
    function() return (db and db.flash) ~= false end,
    function(value) db.flash = value end
  )
  Settings.CreateCheckbox(category, flashSetting, "Flash screen once in red when pet HP falls under threshold")

  -- Sound ON/OFF
  local soundSetting = Settings.RegisterProxySetting(
    category, "mmp_sound", Settings.VarType.Boolean, "Play Sound Alert",
    (db and db.sound) ~= false,
    function() return (db and db.sound) ~= false end,
    function(value) db.sound = value end
  )
  Settings.CreateCheckbox(category, soundSetting, "Play a warning sound when pet HP falls under threshold")

  -- === Sound Alert Type (exclusive radios driven by Settings) ===
  if not db.soundKit then db.soundKit = "RAID_WARNING" end

  -- Header de section
  do
    local header = Settings.CreateSettingInitializer("SettingsListSectionHeaderTemplate", { name = "Sound Alert Type" })
    layout:AddInitializer(header)
  end

  local basePath = "Interface\\AddOns\\"..ADDON_NAME.."\\"

  local soundChoices = {
    -- Quelques sons Blizzard utiles (tu peux en rajouter)
    { key = "RAID_WARNING",          label = "Raid Warning",          kit = SOUNDKIT.RAID_WARNING },
    { key = "READY_CHECK",           label = "Ready Check",           kit = SOUNDKIT.READY_CHECK },
    { key = "TELL_MESSAGE",          label = "Tell (Whisper)",        kit = SOUNDKIT.TELL_MESSAGE or SOUNDKIT.IG_MAINMENU_OPEN },
    { key = "ALARM_CLOCK_WARNING_3", label = "Alarm Clock 3",         kit = SOUNDKIT.ALARM_CLOCK_WARNING_3 },

    -- Sons custom à la racine du dossier de l’addon (1.wav .. 6.wav)
    { key = "CUSTOM1", label = "Custom Sound 1", file = basePath.."1.wav" },
    { key = "CUSTOM2", label = "Custom Sound 2", file = basePath.."2.wav" },
    { key = "CUSTOM3", label = "Custom Sound 3", file = basePath.."3.wav" },
    { key = "CUSTOM4", label = "Custom Sound 4", file = basePath.."4.wav" },
    { key = "CUSTOM5", label = "Custom Sound 5", file = basePath.."5.wav" },
    { key = "CUSTOM6", label = "Custom Sound 6", file = basePath.."6.wav" },
  }

  -- Radios exclusifs (anti-boucle via garde)
  local soundSettings = {}
  local _bulkUpdating = false

  local function selectSound(key, preview)
    if _bulkUpdating then return end
    _bulkUpdating = true

    db.soundKit = key

    for k, s in pairs(soundSettings) do
      s:SetValue(k == key)
    end

    _bulkUpdating = false

    if preview and (db.sound ~= false) then
      for _, o in ipairs(soundChoices) do
        if o.key == key then
          if o.kit then
            PlaySound(o.kit, "Master")
          elseif o.file then
            PlaySoundFile(o.file, "Master")
          end
          break
        end
      end
    end
  end

  for _, opt in ipairs(soundChoices) do
    local setting = Settings.RegisterProxySetting(
      category,
      "mmp_sound_"..opt.key,
      Settings.VarType.Boolean,
      opt.label,
      db.soundKit == opt.key,
      function() return db.soundKit == opt.key end,
      function(value)
        if _bulkUpdating then return end
        if value then
          selectSound(opt.key, true)
        else
          if db.soundKit == opt.key then
            _bulkUpdating = true
            setting:SetValue(true)
            _bulkUpdating = false
          end
        end
      end
    )

    soundSettings[opt.key] = setting
    Settings.CreateCheckbox(category, setting, opt.label)
  end

  -- Synchronise l'état au chargement de la page (sans preview)
  C_Timer.After(0, function() selectSound(db.soundKit or "RAID_WARNING", false) end)

  -- Enregistrer la catégorie (unique)
  Settings.RegisterAddOnCategory(category)
end

------------------------------------------------------------
-- Events handler
------------------------------------------------------------
f:SetScript("OnEvent", function(self, event, arg1, arg2, arg3)
  if event == "ADDON_LOADED" and arg1 == "MendMyPet" then
    MendMyPetDB = MendMyPetDB or {}
    db = MendMyPetDB
    if not db.size then db.size = 64 end
    if not db.threshold then db.threshold = 35 end
    if db.locked == nil then db.locked = false end
    if db.glow == nil then db.glow = true end
    if db.flash == nil then db.flash = true end
    if db.sound == nil then db.sound = true end
    if not db.soundKit then db.soundKit = "RAID_WARNING" end

    RestorePositionAndSize()
    UpdateVisuals()
    CreateOptionsPanel()

  elseif event == "PLAYER_LOGIN" then
    ApplyMacro()
    btn:Show()
    print("|cff5fd38d[MMP]|r loaded. Use /mmp or check AddOns menu.")

  elseif event == "UNIT_PET" and arg1 == "player" then
    C_Timer.After(0.05, UpdateVisuals)

  elseif (event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH") and arg1 == "pet" then
    UpdateVisuals()

  elseif event == "PLAYER_REGEN_ENABLED" or event == "PLAYER_REGEN_DISABLED" then
    UpdateVisuals()

  elseif event == "UNIT_SPELLCAST_START" and arg1 == "player" then
    if arg3 == REVIVE_ID then
      reviveInProgress = true
      petKnownDead = true
      UpdateVisuals()
    end

  elseif (event == "UNIT_SPELLCAST_INTERRUPTED" or event == "UNIT_SPELLCAST_FAILED") and arg1 == "player" then
    if arg3 == REVIVE_ID then
      reviveInProgress = false
      UpdateVisuals()
    end

  elseif (event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_SUCCEEDED") and arg1 == "player" then
    if arg3 == REVIVE_ID then
      reviveInProgress = false
      C_Timer.After(0.05, UpdateVisuals)
    end

  elseif event == "SPELL_UPDATE_COOLDOWN" then
    if petKnownDead or reviveInProgress then
      UpdateCooldown(REVIVE_ID)
    else
      UpdateCooldown(MEND_ID)
    end
  end
end)

------------------------------------------------------------
-- Slash commands
------------------------------------------------------------
SLASH_MENDMYPET1 = "/mmp"
SlashCmdList["MENDMYPET"] = function(msg)
  local cmd, arg = msg:match("^(%S+)%s*(.*)$")
  cmd = cmd and cmd:lower() or ""

  if cmd == "lock" then
    db.locked = not db.locked
    print("|cff5fd38d[MMP]|r Lock:", db.locked and "ON" or "OFF")

  elseif cmd == "size" then
    local sz = tonumber(arg)
    if sz and sz >= 28 and sz <= 128 then
      db.size = sz
      RestorePositionAndSize()
      print("|cff5fd38d[MMP]|r Size:", db.size)
    else
      print("|cff5fd38d[MMP]|r Usage: /mmp size 64 (28-128)")
    end

  elseif cmd == "threshold" then
    local th = tonumber(arg)
    if th and th >= 0 and th <= 100 then
      db.threshold = th
      UpdateVisuals()
      print("|cff5fd38d[MMP]|r Threshold:", db.threshold.."%")
    else
      print("|cff5fd38d[MMP]|r Usage: /mmp threshold 35")
    end

  elseif cmd == "reset" then
    db.pos = nil
    db.size = 64
    db.locked = false
    db.threshold = 35
    db.glow = true
    db.flash = true
    db.sound = true
    db.soundKit = "RAID_WARNING"
    RestorePositionAndSize()
    UpdateVisuals()
    print("|cff5fd38d[MMP]|r Reset done.")

  else
    print("|cff5fd38d[MMP]|r Commands: /mmp lock | /mmp size <px> | /mmp threshold <0-100> | /mmp reset")
  end
end

-- Ouvrir rapidement la page d’options depuis l’Addon Compartment
function MendMyPet_OnAddonCompartmentClick()
  if _G.MMP_SettingsCategory and _G.MMP_SettingsCategory.ID then
    Settings.OpenToCategory(_G.MMP_SettingsCategory.ID)
  else
    Settings.OpenToCategory("Mend My Pet")
  end
end

function MendMyPet_OnAddonCompartmentEnter()
    GameTooltip:SetOwner(AddonCompartmentFrame, "ANCHOR_RIGHT")
    GameTooltip:SetText("Mend My Pet", 1, 1, 1)
    GameTooltip:AddLine("Click to open settings", 0.8, 0.8, 0.8)
    GameTooltip:Show()
end

function MendMyPet_OnAddonCompartmentLeave()
    GameTooltip:Hide()
end