--[[
  SpellSmasher v4.0.0 - clean rewrite

  (c) Tracy Howard / SpellSmasher. Own product.
  OOC compile  >  Priority / Reverse Priority triangle  >  SecureActionButton one cast per mash  >  always advance.

  Iron rules:
  - Cast only what's on the sequence list
  - Easy helpers only if On
  - No secret code-injected spells
  - [nochanneling] on combat casts
  - Pack: !SpellSmasher!1! base64 JSON (site parity)

  /spellsmasher  /ssm
]]

local NS = SpellSmasherNS

print("|cff3dd68c[SpellSmasher]|r loading v" .. NS.VERSION .. " ...")

local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("PLAYER_LOGIN")
eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
eventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
eventFrame:RegisterEvent("PLAYER_TARGET_CHANGED")
eventFrame:RegisterEvent("PLAYER_LEVEL_UP")
eventFrame:RegisterEvent("PLAYER_LEVEL_CHANGED")
-- Classic: trainer / quest learn (ding already covered). Harmless no-op on Midnight.
pcall(function()
  eventFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
end)
pcall(function()
  eventFrame:RegisterEvent("SPELLS_CHANGED")
end)
-- Diagnose "blocked from an action only available to the Blizzard UI"
eventFrame:RegisterEvent("ADDON_ACTION_FORBIDDEN")
eventFrame:RegisterEvent("ADDON_ACTION_BLOCKED")

-- Ignore the login book-fill wave; then debounce noisy SPELLS_CHANGED.
local classicKnownReady = false
local classicKnownQueued = false
local classicKnownReason = "learn"

local function RunClassicKnownRefresh(reason)
  if not NS.IsClassicFlavor or not SpellSmasherRebuild or not SpellSmasherRebuild.RefreshClassicKnownPacks then
    return
  end
  if InCombatLockdown and InCombatLockdown() then
    NS.pendingClassicKnownRefresh = reason or "learn"
    return
  end
  local n = SpellSmasherRebuild.RefreshClassicKnownPacks()
  if n > 0 then
    local label = (reason == "ding") and "Level up" or "Learned a spell"
    NS.Print(label .. ": Classic pack(s) now only spells you know (" .. n .. ").")
    if NS.Secure and NS.Secure.Recompile and not InCombatLockdown() then
      NS.Secure.Recompile()
    else
      NS.pendingRecompile = true
    end
    if NS.UI and NS.UI.Refresh then
      NS.UI.Refresh()
    end
  end
end

local function ScheduleClassicKnownRefresh(reason)
  if not NS.IsClassicFlavor then
    return
  end
  if not classicKnownReady then
    return
  end
  classicKnownReason = reason or classicKnownReason or "learn"
  if InCombatLockdown and InCombatLockdown() then
    NS.pendingClassicKnownRefresh = classicKnownReason
    return
  end
  if classicKnownQueued then
    return
  end
  classicKnownQueued = true
  local function fire()
    classicKnownQueued = false
    RunClassicKnownRefresh(classicKnownReason)
  end
  if C_Timer and C_Timer.After then
    C_Timer.After(0.8, fire)
  else
    fire()
  end
end

eventFrame:SetScript("OnEvent", function(_, event, arg1, arg2)
  if event == "ADDON_ACTION_FORBIDDEN" or event == "ADDON_ACTION_BLOCKED" then
  -- Silent: printing these flooded chat and did not help. Blizzard dialog still shows;
  -- v4.3.27 no longer places mash macros on action bars (main SetShown/SetAttribute source).
  return
  end
  if event == "ADDON_LOADED" and arg1 == "SpellSmasher" then
  NS.MergeDB()
  elseif event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" then
  if not NS.db then
  NS.MergeDB()
  end
  NS.Secure.CreateButton()
  NS.Secure.ApplyLayout()
  -- One-time cleanup: older builds put a SpellSmasher macro on a bar slot (taints MultiBar).
  if not InCombatLockdown() and NS.db and NS.db.barSlot and NS.Secure.ClearMashFromSlot then
  NS.Secure.ClearMashFromSlot(NS.db.barSlot)
  NS.db.barSlot = nil
  NS.db.hidden = false
  if NS.Secure.SyncFloatingVisibility then
  NS.Secure.SyncFloatingVisibility()
  end
  end
  -- Account-wide pack list: prefer an Active pack for this character's class
  if NS.EnsureActivePackForPlayer and NS.EnsureActivePackForPlayer() then
  NS.Print("|cff3dd68cActive pack|r set to a list for your class.")
  end
  if not InCombatLockdown() then
  NS.Secure.Recompile()
  else
  NS.pendingRecompile = true
  end
  if NS.db and NS.db.firstRun then
  NS.db.firstRun = false
  NS.Print("v" .. NS.VERSION .. "  -  /spellsmasher  -  open UI for the in-game guide")
  NS.Print("Mash casts ONLY your sequence list. Easy AA/RT/DEF default Off. List = your class by default.")
  end
  if NS.UI and NS.UI.UpdateTalentApplyEnabled then
  NS.UI.UpdateTalentApplyEnabled()
  end
  -- Do NOT auto-overwrite Method pack talent strings with live CUk on login.
  -- Method packs keep Method loadouts (spell list + tree pair). Use From me / Export for live.
  if NS.IsClassicFlavor and not classicKnownReady then
    local function arm()
      classicKnownReady = true
    end
    if C_Timer and C_Timer.After then
      C_Timer.After(2, arm)
    else
      arm()
    end
  end
  elseif event == "PLAYER_LEVEL_UP" then
  if NS.UI and NS.UI.UpdateTalentApplyEnabled then
  NS.UI.UpdateTalentApplyEnabled()
  end
  classicKnownReady = true
  ScheduleClassicKnownRefresh("ding")
  elseif event == "PLAYER_LEVEL_CHANGED" then
  if NS.UI and NS.UI.UpdateTalentApplyEnabled then
  NS.UI.UpdateTalentApplyEnabled()
  end
  elseif event == "LEARNED_SPELL_IN_TAB" then
  ScheduleClassicKnownRefresh("learn")
  elseif event == "SPELLS_CHANGED" then
  ScheduleClassicKnownRefresh("learn")
  elseif event == "PLAYER_REGEN_ENABLED" then
  if NS.pendingClassicKnownRefresh then
    local reason = NS.pendingClassicKnownRefresh
    if reason == true then
      reason = "learn"
    end
    NS.pendingClassicKnownRefresh = nil
    RunClassicKnownRefresh(reason)
  end
  if NS.pendingLayout then
  NS.Secure.ApplyLayout()
  end
  if NS.pendingRecompile then
  NS.Secure.Recompile()
  else
  -- Leave combat: reset Once engagement + mash step to openers
  if NS.Secure.ResetOnceEngagement then
  NS.Secure.ResetOnceEngagement("leave-combat")
  elseif NS.Secure.seqButton and not InCombatLockdown() then
  NS.Secure.seqButton:SetAttribute("step", 1)
  NS.Secure.seqButton:SetAttribute("aoe_step", 1)
  end
  end
  if NS.pendingOnceReset and NS.Secure.ResetOnceEngagement then
  NS.Secure.ResetOnceEngagement("pending")
  end
  -- Icon poll is OOC-only; refresh once when combat ends.
  if NS.Secure and NS.Secure.UpdateIcon and NS.Secure.seqButton then
  NS.Secure.UpdateIcon(NS.Secure.seqButton)
  end
  elseif event == "PLAYER_TARGET_CHANGED" then
  -- OOC only (cannot SetAttribute from insecure code in combat).
  -- Mid-fight retarget: Once stays skipped until leave combat.
  if not InCombatLockdown() and NS.Secure.ResetOnceEngagement then
  NS.Secure.ResetOnceEngagement("target-changed-ooc")
  end
  end
end)

local function SlashHandler(msg)
  msg = NS.Trim(msg or ""):lower()
  if msg == "" or msg == "open" or msg == "ui" then
  NS.UI.Toggle()
  return
  end
  if msg == "recompile" or msg == "compile" then
  NS.Secure.verboseCompile = true
  NS.Secure.Recompile()
  return
  end
  if msg == "quiet" then
  NS.Secure.verboseCompile = false
  NS.Print("Compile chat spam off (default). Use |cff3dd68c/ssm recompile|r when you want one summary.")
  return
  end
  if msg == "secure" or msg == "dump" then
  NS.Secure.DumpSample(15)
  return
  end
  if msg == "reset" then
  if NS.Secure.seqButton and not InCombatLockdown() then
  NS.Secure.seqButton:SetAttribute("step", 1)
  NS.Secure.seqButton:SetAttribute("aoe_step", 1)
  NS.Print("Step reset to 1.")
  end
  return
  end
  if msg == "hide" then
  if NS.db then
  NS.db.hidden = true
  NS.Secure.ApplyLayout()
  NS.Print("Floating button hidden. |cff3dd68c/ssm show|r to bring it back.")
  end
  return
  end
  if msg == "show" then
  if NS.db then
  NS.db.hidden = false
  NS.db.hideFloatingWhenBar = false
  NS.Secure.ApplyLayout()
  NS.Print("Floating button shown. Drag the green |cff3dd68c| drag|r tab to move.")
  end
  return
  end
  if msg == "lock" or msg == "unlock" then
  if NS.db then
  if msg == "unlock" then
  NS.db.locked = false
  else
  NS.db.locked = not NS.db.locked
  end
  NS.Secure.ApplyLayout()
  if NS.db.locked then
  NS.Print("Button locked. |cff3dd68c/ssm unlock|r to move it.")
  else
  NS.Print("Button unlocked - drag the green |cff3dd68c| drag|r tab above the icon.")
  end
  end
  return
  end
  if msg == "version" or msg == "ver" then
  NS.Print("v" .. NS.VERSION .. "  -  priority-triangle  -  reverse-pri  -  sequence-only")
  return
  end
  if msg == "lost" or msg == "restore" or msg == "missing" then
  if NS.UI and NS.UI.ShowLostSequenceHelp then
  NS.UI.Open()
  NS.UI.ShowLostSequenceHelp()
  else
  NS.Print("Open /ssm  -  Help  -  Lost sequence?")
  end
  return
  end
  if msg == "help" or msg == "guide" or msg == "coach" or msg == "tutorial" then
  if NS.UI and NS.UI.ShowCoach then
  NS.UI.Open()
  NS.UI.ShowCoach(1)
  else
  NS.Print("/ssm - open UI")
  NS.Print("/ssm help - full spotlight tour (Tutorial)")
  NS.Print("/ssm lost - Lost sequence? recovery steps")
  NS.Print("/ssm recompile - rebuild secure step list")
  NS.Print("/ssm secure - dump first macrotexts (must be list spells only)")
  NS.Print("/ssm reset - step 1")
  NS.Print("/ssm show|hide|lock")
  end
  return
  end
  NS.UI.Toggle()
end

SLASH_SPELLSMASHER1 = "/spellsmasher"
SLASH_SPELLSMASHER2 = "/ssm"
SlashCmdList["SPELLSMASHER"] = SlashHandler

-- Export a few globals for debugging
SpellSmasher = NS
