-- LidCouncil_Init.lua
-- ============================================================

-- NOTE: Load order: Core -> Panels -> Editor -> Init

local LogInfo = _G.LIDCOUNCIL_LogInfo
local LogError = _G.LIDCOUNCIL_LogError
local Compat = _G.LIDCOUNCIL_Compat
if LogInfo then LogInfo("Init.lua loaded") end

local function SafeCall(label, fn, ...)
  if type(fn) ~= "function" then return false end
  local args = { ... }
  local function OnError(err)
    if LogError then
      LogError("[" .. tostring(label or "Init") .. "] " .. tostring(err))
      if debugstack then
        LogError(debugstack(3, 8, 8))
      end
    end
  end
  return xpcall(function()
    return fn(unpack(args))
  end, OnError)
end

local function RequireApi(name, value)
  if value == nil then
    value = rawget(_G, name)
  end
  if value == nil then
    local api = rawget(_G, "LIDCOUNCIL_API")
    if type(api) == "table" then
      value = api[name]
    end
  end
  if value == nil then
    error("LidCouncil missing API: " .. tostring(name))
  end
  return value
end

local function ResolveOptionalApiFunction(name)
  local api = rawget(_G, "LIDCOUNCIL_API")
  if type(api) == "table" and type(api[name]) == "function" then
    return api[name]
  end
  local fallback = rawget(_G, name)
  if type(fallback) == "function" then
    return fallback
  end
  return nil
end

local ADDON_NAME = RequireApi("LIDCOUNCIL_ADDON_NAME", _G.LIDCOUNCIL_ADDON_NAME or "LidCouncil")
local RootDB = RequireApi("LIDCOUNCIL_RootDB", _G.LIDCOUNCIL_RootDB)
local DB = RequireApi("LIDCOUNCIL_DB", _G.LIDCOUNCIL_DB)
local Print = RequireApi("LIDCOUNCIL_Print", _G.LIDCOUNCIL_Print)
local EnsureDefaults = RequireApi("LIDCOUNCIL_EnsureDefaults", _G.LIDCOUNCIL_EnsureDefaults)
local GetSpecKey = RequireApi("LIDCOUNCIL_GetSpecKey", _G.LIDCOUNCIL_GetSpecKey)
local GetCurrentSpecID = RequireApi("LIDCOUNCIL_GetCurrentSpecID", _G.LIDCOUNCIL_GetCurrentSpecID)
local GetCharacterKey = RequireApi("LIDCOUNCIL_GetCharacterKey", _G.LIDCOUNCIL_GetCharacterKey)
local AddCharacterToList = RequireApi("LIDCOUNCIL_AddCharacterToList", _G.LIDCOUNCIL_AddCharacterToList)
local LoadLayoutFromSpec = RequireApi("LIDCOUNCIL_LoadLayoutFromSpec", _G.LIDCOUNCIL_LoadLayoutFromSpec)
local RebuildAllPanels = RequireApi("LIDCOUNCIL_RebuildAllPanels", _G.LIDCOUNCIL_RebuildAllPanels)
local SwitchProfile = ResolveOptionalApiFunction("LIDCOUNCIL_SwitchProfile")
local ApplyEditorState = ResolveOptionalApiFunction("LIDCOUNCIL_ApplyEditorState")
local CreateEditor = ResolveOptionalApiFunction("LIDCOUNCIL_CreateEditor")

local minimapButton
local hoverHooksInstalled = false
local hoverHideToken = 0
local MINIMAP_HOVER_HIDE_DELAY_SECONDS = 0.15
local MINIMAP_ACTIVE_LOGO = "Interface\\AddOns\\LidCouncil\\Media\\LidCouncil\\Logos\\LidCouncil_HeaderLogoGreen.tga"
local bootstrapLimitedNoticeShown = false

local function GetBootstrapIssueText()
  local resolver = rawget(_G, "LIDCOUNCIL_GetBootstrapIssueText")
  if type(resolver) == "function" then
    return tostring(resolver() or "")
  end
  return ""
end

local function WarnBootstrapLimited(context)
  if bootstrapLimitedNoticeShown then
    return
  end
  bootstrapLimitedNoticeShown = true
  local details = GetBootstrapIssueText()
  local message = "LidCouncil editor is running in limited mode because one or more files are missing, outdated, or failed to load."
  if details ~= "" then
    message = message .. " " .. details
  elseif context and context ~= "" then
    message = message .. " " .. tostring(context)
  end
  if type(Print) == "function" then
    Print(message)
  end
end

local function GetMinimapSettings()
  local ui = RootDB().ui
  ui.minimap = ui.minimap or { size = 37, angle = 45 }
  return ui.minimap
end

local function ResolveMinimapAngle(dx, dy)
  if math.atan2 then
    return math.deg(math.atan2(dy, dx))
  end
  if dx == 0 then
    if dy >= 0 then return 90 end
    return -90
  end
  local a = math.deg(math.atan(dy / dx))
  if dx < 0 then a = a + 180 end
  return a
end

local function GetPanelsEditor()
  local e = _G.LIDCOUNCIL_Editor
  if e and e.__lidCouncilEditor then return e end
  return nil
end

local function ApplyMinimapButtonAlpha(alpha)
  if not minimapButton then return end
  local resolved = tonumber(alpha)
  if resolved == nil then resolved = 1 end
  if resolved < 0 then resolved = 0 end
  if resolved > 1 then resolved = 1 end
  minimapButton:SetAlpha(resolved)
end

local function IsMinimapOrButtonHovered()
  if type(MouseIsOver) ~= "function" then
    return false
  end
  if Minimap and MouseIsOver(Minimap) then
    return true
  end
  if minimapButton and MouseIsOver(minimapButton) then
    return true
  end
  return false
end

local function NextHoverHideToken()
  hoverHideToken = hoverHideToken + 1
  return hoverHideToken
end

local function RefreshMinimapHoverVisibility()
  if not minimapButton then return end
  local mm = GetMinimapSettings()
  minimapButton:Show()
  if not (mm and mm.onlyOnHover) then
    NextHoverHideToken()
    ApplyMinimapButtonAlpha(minimapButton.__alpha or 1)
    return
  end

  if IsMinimapOrButtonHovered() then
    NextHoverHideToken()
    ApplyMinimapButtonAlpha(minimapButton.__alpha or 1)
    return
  end

  ApplyMinimapButtonAlpha(0)
end

local function ScheduleMinimapHoverHideCheck()
  if not minimapButton then return end
  local token = NextHoverHideToken()
  if Compat and type(Compat.After) == "function" then
    Compat.After(MINIMAP_HOVER_HIDE_DELAY_SECONDS, function()
      if token ~= hoverHideToken then return end
      RefreshMinimapHoverVisibility()
    end)
    return
  end
  RefreshMinimapHoverVisibility()
end

local function EnsureMinimapHoverHooks()
  if hoverHooksInstalled then
    return
  end
  if not minimapButton or not Minimap then
    return
  end
  if type(Minimap.HookScript) == "function" then
    Minimap:HookScript("OnEnter", RefreshMinimapHoverVisibility)
    Minimap:HookScript("OnLeave", ScheduleMinimapHoverHideCheck)
  end
  if type(minimapButton.HookScript) == "function" then
    minimapButton:HookScript("OnEnter", RefreshMinimapHoverVisibility)
    minimapButton:HookScript("OnLeave", ScheduleMinimapHoverHideCheck)
  end
  hoverHooksInstalled = true
end

local function OpenPanelsEditor()
  local createFn = CreateEditor or ResolveOptionalApiFunction("LIDCOUNCIL_CreateEditor")
  if not createFn then
    WarnBootstrapLimited("Editor bootstrap is unavailable.")
    return
  end
  createFn()
  local e = GetPanelsEditor()
  if not e then
    WarnBootstrapLimited("Editor window could not be created.")
    return
  end
  if e:IsShown() then
    e:Hide()
  else
    e:Show()
    if e.RefreshVisiblePage then
      e:RefreshVisiblePage()
    elseif e.Refresh then
      e:Refresh()
    end
    local onEditorOpen = ResolveOptionalApiFunction("LIDCOUNCIL_UpdateNews_OnEditorOpen")
    if onEditorOpen then
      pcall(onEditorOpen)
    end
  end
end

local function EnsureMinimapSessionVisuals()
  local button = minimapButton
  if not button or button.__lidSessionVisualsReady then
    return
  end
  button.__lidSessionVisualsReady = true

  local glow = button:CreateTexture(nil, "BACKGROUND")
  glow:SetPoint("CENTER", button, "CENTER", 0, 0)
  glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
  glow:SetBlendMode("ADD")
  glow:SetVertexColor(0.22, 1.00, 0.38, 0.75)
  glow:SetAlpha(0.34)
  glow:Hide()
  button.sessionGlow = glow

  local activeIcon = button:CreateTexture(nil, "ARTWORK", nil, 2)
  activeIcon:SetPoint("CENTER", button, "CENTER", 0, 0)
  activeIcon:SetTexture(MINIMAP_ACTIVE_LOGO)
  activeIcon:Hide()
  button.activeSessionIcon = activeIcon

  local pulse = glow:CreateAnimationGroup()
  local pulseIn = pulse:CreateAnimation("Alpha")
  pulseIn:SetFromAlpha(0.24)
  pulseIn:SetToAlpha(0.82)
  pulseIn:SetDuration(0.22)
  pulseIn:SetOrder(1)
  local pulseOut = pulse:CreateAnimation("Alpha")
  pulseOut:SetFromAlpha(0.82)
  pulseOut:SetToAlpha(0.34)
  pulseOut:SetDuration(0.45)
  pulseOut:SetOrder(2)
  pulse:SetScript("OnFinished", function()
    if glow:IsShown() then
      glow:SetAlpha(0.34)
    end
  end)
  button.sessionPulse = pulse

  button:HookScript("OnEnter", function(self)
    if not GameTooltip then
      return
    end
    GameTooltip:SetOwner(self, "ANCHOR_LEFT")
    GameTooltip:SetText("LidCouncil", 1.00, 0.82, 0.28)
    GameTooltip:AddLine(
      tostring(self.__lidSessionTooltip or "No active session."),
      0.84,
      0.90,
      0.96,
      true
    )
    GameTooltip:Show()
  end)
  button:HookScript("OnLeave", function()
    if GameTooltip then
      GameTooltip:Hide()
    end
  end)
end

local function UpdateMinimapSessionVisual()
  if not minimapButton then
    return
  end
  if type(InCombatLockdown) == "function" and InCombatLockdown() then
    minimapButton.__lidSessionVisualDeferred = true
    return
  end
  minimapButton.__lidSessionVisualDeferred = nil
  EnsureMinimapSessionVisuals()

  local status = {}
  local getSessionStatus = ResolveOptionalApiFunction("LIDCOUNCIL_GetSessionPresentationStatus")
  if type(getSessionStatus) == "function" then
    local ok, result = pcall(getSessionStatus)
    if ok and type(result) == "table" then
      status = result
    end
  end
  local state = tostring(status.state or "inactive")
  local participating = status.participating == true
  local previousState = minimapButton.__lidSessionState
  minimapButton.__lidSessionState = state

  local detailParts = { tostring(status.label or "NO SESSION") }
  if tostring(status.host or "") ~= "" then
    detailParts[#detailParts + 1] = "Host: " .. tostring(status.host)
  end
  if state ~= "inactive" then
    local memberCount = math.max(0, tonumber(status.memberCount) or 0)
    detailParts[#detailParts + 1] = tostring(memberCount) .. (memberCount == 1 and " member" or " members")
  end
  minimapButton.__lidSessionTooltip = table.concat(detailParts, " | ")

  if minimapButton.activeSessionIcon then
    minimapButton.activeSessionIcon:SetShown(participating)
  end
  if minimapButton.sessionGlow then
    minimapButton.sessionGlow:SetShown(participating)
  end
  if participating
    and previousState ~= state
    and minimapButton.sessionPulse
    and not minimapButton.sessionPulse:IsPlaying()
  then
    minimapButton.sessionPulse:Play()
  end
end

local function UpdateMinimapButton()
  if not minimapButton then return end
  EnsureMinimapSessionVisuals()
  local mm = GetMinimapSettings()
  local size = tonumber(mm.size) or 37
  if size < 16 then size = 16 end
  if size > 64 then size = 64 end

  minimapButton:SetSize(size, size)
  if minimapButton.icon then
    local iconSize = math.max(8, size - 6)
    minimapButton.icon:SetSize(iconSize, iconSize)
    if minimapButton.activeSessionIcon then
      minimapButton.activeSessionIcon:SetSize(iconSize, iconSize)
    end
  end
  if minimapButton.sessionGlow then
    minimapButton.sessionGlow:SetSize(size + 12, size + 12)
  end

  local alpha = tonumber(mm.alpha)
  if alpha == nil then alpha = 1 end
  if alpha < 0 then alpha = 0 end
  if alpha > 1 then alpha = 1 end
  minimapButton.__alpha = alpha
  minimapButton:Show()
  EnsureMinimapHoverHooks()
  RefreshMinimapHoverVisibility()

  local angle = tonumber(mm.angle) or 45
  minimapButton.__angle = angle
  local rad = math.rad(angle)
  local radius = (Minimap:GetWidth() / 2) + 6
  minimapButton:ClearAllPoints()
  minimapButton:SetPoint("CENTER", Minimap, "CENTER", math.cos(rad) * radius, math.sin(rad) * radius)
  UpdateMinimapSessionVisual()
end
_G.LIDCOUNCIL_UpdateMinimapButton = UpdateMinimapButton

local function CreateMinimapButton()
  if _G.LIDCOUNCIL_MinimapButton then
    minimapButton = _G.LIDCOUNCIL_MinimapButton
    _G.LIDCOUNCIL_MinimapButton = minimapButton
    EnsureMinimapHoverHooks()
    UpdateMinimapButton()
    return
  end
  if not Minimap then return end

  local b = CreateFrame("Button", "LIDCOUNCIL_MinimapButton", Minimap)
  _G.LIDCOUNCIL_MinimapButton = b
  b:SetFrameStrata("MEDIUM")
  b:SetFrameLevel((Minimap.GetFrameLevel and Minimap:GetFrameLevel() or 0) + 8)
  b:RegisterForDrag("LeftButton")
  b:RegisterForClicks("LeftButtonUp", "RightButtonUp")

  local icon = b:CreateTexture(nil, "ARTWORK")
  icon:SetPoint("CENTER")
  icon:SetTexture("Interface\\AddOns\\LidCouncil\\Media\\LidCouncil\\Logos\\Minimap-Logo.tga")
  b.icon = icon
  b:SetHighlightTexture("Interface\\AddOns\\LidCouncil\\Media\\LidCouncil\\Logos\\Minimap-Logo.tga", "ADD")
  local ht = b:GetHighlightTexture()
  if ht then
    ht:SetAllPoints(icon)
    ht:SetVertexColor(1, 1, 1, 0.28)
  end

  b:SetScript("OnClick", function()
    SafeCall("Minimap OnClick", OpenPanelsEditor)
  end)

  local function UpdateMinimapDrag(self)
    local mx, my = Minimap:GetCenter()
    if not mx or not my then return end
    local cx, cy = GetCursorPosition()
    local scale = Minimap:GetEffectiveScale() or 1
    if scale == 0 then scale = 1 end
    cx, cy = cx / scale, cy / scale
    local dx, dy = cx - mx, cy - my
    local angle = ResolveMinimapAngle(dx, dy)
    self.__angle = angle

    local mm = GetMinimapSettings()
    mm.angle = angle

    local radius = (Minimap:GetWidth() / 2) + 6
    local rad = math.rad(angle)
    self:ClearAllPoints()
    self:SetPoint("CENTER", Minimap, "CENTER", math.cos(rad) * radius, math.sin(rad) * radius)
  end

  b:SetScript("OnDragStart", function(self)
    self.__dragging = true
    self:SetScript("OnUpdate", UpdateMinimapDrag)
  end)
  b:SetScript("OnDragStop", function(self)
    self.__dragging = false
    self:SetScript("OnUpdate", nil)
    if UpdateMinimapButton then UpdateMinimapButton() end
  end)

  minimapButton = b
  EnsureMinimapHoverHooks()
  UpdateMinimapButton()
end

local function ResetEditorWindow()
  -- Reset only editor window geometry/state, not user panels or profiles.
  local ui = RootDB().ui
  ui.editor = {}

  local createFn = CreateEditor or ResolveOptionalApiFunction("LIDCOUNCIL_CreateEditor")
  if not createFn then
    WarnBootstrapLimited("Editor bootstrap is unavailable.")
    return
  end
  createFn()
  local e = GetPanelsEditor()
  if not e then
    WarnBootstrapLimited("Editor window could not be created.")
    return
  end

  if ApplyEditorState then
    ApplyEditorState(e)
  end
  e:Show()
  if e.RefreshVisiblePage then
    e:RefreshVisiblePage()
  elseif e.Refresh then
    e:Refresh()
  end
  Print("Editor window reset to default position and size.")
end

SLASH_LIDCOUNCIL_EDITOR1 = "/lidcouncil"
SlashCmdList.LIDCOUNCIL_EDITOR = function()
  SafeCall("Slash /lidcouncil", function()
    local createFn = CreateEditor or ResolveOptionalApiFunction("LIDCOUNCIL_CreateEditor")
    if not createFn then
      WarnBootstrapLimited("Editor bootstrap is unavailable.")
      return
    end
    createFn()
    local e = GetPanelsEditor()
    if not e then
      WarnBootstrapLimited("Editor window could not be created.")
      return
    end
    e:Show()
    if e.RefreshVisiblePage then
      e:RefreshVisiblePage()
    elseif e.Refresh then
      e:Refresh()
    end
    local onEditorOpen = ResolveOptionalApiFunction("LIDCOUNCIL_UpdateNews_OnEditorOpen")
    if onEditorOpen then
      pcall(onEditorOpen)
    end
  end)
end

SLASH_LIDCOUNCIL_RESET1 = "/lidcouncilreset"
SlashCmdList.LIDCOUNCIL_RESET = function()
  SafeCall("Slash /lidcouncilreset", ResetEditorWindow)
end


-- ============================================================
-- ##--------------------------SPEC AUTO SWITCH------------------
-- ============================================================

local function TryAutoProfileSwitch()
  local root = RootDB()
  local curChar = GetCharacterKey and GetCharacterKey()
  local specID = GetCurrentSpecID()
  local target = nil
  local names = {}

  for name in pairs(root.profiles or {}) do
    names[#names + 1] = name
  end
  table.sort(names)

  for _, name in ipairs(names) do
    local prof = root.profiles[name]
    local ps = prof and prof.profileSpec
    local mode = (ps and ps.mode == "CHARACTER") and "CHARACTER" or "SPEC"
    if ps and ps.enabled and mode == "CHARACTER" and ps.character and ps.character == curChar then
      target = name
      break
    end
  end

  if not target and specID then
    local fallbackTarget = nil
    for _, name in ipairs(names) do
      local prof = root.profiles[name]
      local ps = prof and prof.profileSpec
      local mode = (ps and ps.mode == "CHARACTER") and "CHARACTER" or "SPEC"
      if ps and ps.enabled and mode == "SPEC" and ps.specID == specID then
        if ps.character and ps.character == curChar then
          target = name
          break
        end
        if ps.character == nil and not fallbackTarget then
          fallbackTarget = name
        end
      end
    end
    target = target or fallbackTarget
  end

  if target and target ~= root.currentProfile and SwitchProfile then
    SwitchProfile(target)
    EnsureDefaults()
    RebuildAllPanels()
    local e = GetPanelsEditor()
    if e and e.Refresh then e:Refresh() end
    Print("Auto-switched profile: " .. target)
    return true
  end

  return false
end

local lastObservedSpecID = nil

local function OnSpecChanged()
  local currentSpecID = GetCurrentSpecID()
  if currentSpecID and lastObservedSpecID and currentSpecID == lastObservedSpecID then
    return
  end
  lastObservedSpecID = currentSpecID

  TryAutoProfileSwitch()
  local db = DB()
  if not db.spec.auto then return end
  local specKey = GetSpecKey()
  if db.spec.layouts[specKey] then
    local ok = LoadLayoutFromSpec(specKey)
    if ok then
      RebuildAllPanels()
      local e = GetPanelsEditor()
      if e and e:IsShown() and e.Refresh then e:Refresh() end
    end
  end
end

-- ============================================================
-- ##--------------------------EVENTS----------------------------
-- ============================================================

local ev = CreateFrame("Frame")
ev:RegisterEvent("ADDON_LOADED")
ev:RegisterEvent("PLAYER_LOGIN")
ev:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")

local function HandleEvent(event, arg1)
  if event == "ADDON_LOADED" and arg1 == ADDON_NAME then
    RootDB()
    DB()
    EnsureDefaults()
    Print("Loaded. Use /lidcouncil to open the editor.")
    if GetBootstrapIssueText() ~= "" then
      WarnBootstrapLimited()
    end
    if LogInfo then LogInfo("Addon loaded") end
  elseif event == "PLAYER_LOGIN" then
    EnsureDefaults()
    local switchedOnLogin = TryAutoProfileSwitch()
    if not switchedOnLogin then
      RebuildAllPanels()
    end
    if AddCharacterToList and GetCharacterKey then
      SafeCall("PLAYER_LOGIN character list", AddCharacterToList, GetCharacterKey())
    end
    SafeCall("PLAYER_LOGIN minimap", CreateMinimapButton)
    if Compat and Compat.After then
      Compat.After(0.2, function()
        SafeCall("PLAYER_LOGIN minimap refresh short", UpdateMinimapButton)
      end)
      Compat.After(1.0, function()
        SafeCall("PLAYER_LOGIN minimap refresh long", UpdateMinimapButton)
      end)
    end
    SafeCall("PLAYER_LOGIN editor bootstrap", function()
      local createFn = CreateEditor or ResolveOptionalApiFunction("LIDCOUNCIL_CreateEditor")
      if not createFn then
        WarnBootstrapLimited("Editor bootstrap is unavailable.")
        return
      end
      createFn()
      local e = GetPanelsEditor()
      if not e then
        WarnBootstrapLimited("Editor window could not be created.")
        return
      end
      if e.ApplyTheme then e:ApplyTheme() end
      if e.UpdateUILayout then e:UpdateUILayout() end
      if Compat and Compat.After then
        Compat.After(0.2, function()
          SafeCall("PLAYER_LOGIN delayed editor theme", function()
            local ee = GetPanelsEditor()
            if ee and ee.ApplyTheme then ee:ApplyTheme() end
          end)
        end)
      end
    end)
    local onLogin = ResolveOptionalApiFunction("LIDCOUNCIL_UpdateNews_OnLogin")
    if onLogin then
      pcall(onLogin)
    end
    lastObservedSpecID = GetCurrentSpecID()
    if LogInfo then
      LogInfo("Panels built")
      LogInfo("Editor created")
      LogInfo("Minimap button created")
    end
  elseif event == "PLAYER_SPECIALIZATION_CHANGED" then
    if arg1 == "player" then
      OnSpecChanged()
    end
  end
end

ev:SetScript("OnEvent", function(_, event, arg1)
  SafeCall("Event " .. tostring(event), HandleEvent, event, arg1)
end)

local minimapSessionTicker = CreateFrame("Frame")
minimapSessionTicker:SetScript("OnUpdate", function(self, elapsed)
  self.__lidElapsed = (tonumber(self.__lidElapsed) or 0) + math.max(0, tonumber(elapsed) or 0)
  if self.__lidElapsed < 0.50 then
    return
  end
  self.__lidElapsed = 0
  if minimapButton then
    SafeCall("Minimap session visual", UpdateMinimapSessionVisual)
  end
end)
