-- LidPanels_Init.lua
-- ============================================================

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

local LogInfo = _G.LIDPANELS_LogInfo
local LogError = _G.LIDPANELS_LogError
local Compat = _G.LIDPANELS_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, "LIDPANELS_API")
    if type(api) == "table" then
      value = api[name]
    end
  end
  if value == nil then
    error("LidPanels missing API: " .. tostring(name))
  end
  return value
end

local function ResolveOptionalApiFunction(name)
  local api = rawget(_G, "LIDPANELS_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("LIDPANELS_ADDON_NAME", _G.LIDPANELS_ADDON_NAME or "LidPanels")
local RootDB = RequireApi("LIDPANELS_RootDB", _G.LIDPANELS_RootDB)
local DB = RequireApi("LIDPANELS_DB", _G.LIDPANELS_DB)
local Print = RequireApi("LIDPANELS_Print", _G.LIDPANELS_Print)
local EnsureDefaults = RequireApi("LIDPANELS_EnsureDefaults", _G.LIDPANELS_EnsureDefaults)
local GetSpecKey = RequireApi("LIDPANELS_GetSpecKey", _G.LIDPANELS_GetSpecKey)
local GetCurrentSpecID = RequireApi("LIDPANELS_GetCurrentSpecID", _G.LIDPANELS_GetCurrentSpecID)
local GetCharacterKey = RequireApi("LIDPANELS_GetCharacterKey", _G.LIDPANELS_GetCharacterKey)
local AddCharacterToList = RequireApi("LIDPANELS_AddCharacterToList", _G.LIDPANELS_AddCharacterToList)
local LoadLayoutFromSpec = RequireApi("LIDPANELS_LoadLayoutFromSpec", _G.LIDPANELS_LoadLayoutFromSpec)
local RebuildAllPanels = RequireApi("LIDPANELS_RebuildAllPanels", _G.LIDPANELS_RebuildAllPanels)
local SwitchProfile = RequireApi("LIDPANELS_SwitchProfile", _G.LIDPANELS_SwitchProfile)
local ApplyEditorState = RequireApi("LIDPANELS_ApplyEditorState", _G.LIDPANELS_ApplyEditorState)
local CreateEditor = RequireApi("LIDPANELS_CreateEditor", _G.LIDPANELS_CreateEditor)

local minimapButton
local hoverHooksInstalled = false
local hoverHideToken = 0
local MINIMAP_HOVER_HIDE_DELAY_SECONDS = 0.15

local function GetMinimapSettings()
  local ui = RootDB().ui
  ui.minimap = ui.minimap or { size = 28, 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.LIDPANELS_Editor
  if e and e.__lidPanelsEditor 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 = _G.LIDPANELS_CreateEditor or CreateEditor
  if createFn then createFn() end
  local e = GetPanelsEditor()
  if not e then return end
  if e:IsShown() then
    e:Hide()
  else
    e:Show()
    if e.Refresh then e:Refresh() end
    local onEditorOpen = ResolveOptionalApiFunction("LIDPANELS_UpdateNews_OnEditorOpen")
    if onEditorOpen then
      pcall(onEditorOpen)
    end
  end
end

local function UpdateMinimapButton()
  if not minimapButton then return end
  local mm = GetMinimapSettings()
  local size = tonumber(mm.size) or 28
  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)
  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)
end
_G.LIDPANELS_UpdateMinimapButton = UpdateMinimapButton

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

  local b = CreateFrame("Button", "LIDPANELS_MinimapButton", Minimap)
  _G.LIDPANELS_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\\LidPanels\\Media\\Lidpanels\\Logos\\Minimap-Logo.tga")
  b.icon = icon
  b:SetHighlightTexture("Interface\\AddOns\\LidPanels\\Media\\Lidpanels\\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 = _G.LIDPANELS_CreateEditor or CreateEditor
  if createFn then createFn() end
  local e = GetPanelsEditor()
  if not e then return end

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

SLASH_LIDPANELS_EDITOR1 = "/lidpanels"
SlashCmdList.LIDPANELS_EDITOR = function()
  SafeCall("Slash /lidpanels", function()
    local createFn = _G.LIDPANELS_CreateEditor or CreateEditor
    if createFn then createFn() end
    local e = GetPanelsEditor()
    if not e then return end
    e:Show()
    if e.Refresh then e:Refresh() end
    local onEditorOpen = ResolveOptionalApiFunction("LIDPANELS_UpdateNews_OnEditorOpen")
    if onEditorOpen then
      pcall(onEditorOpen)
    end
  end)
end

SLASH_LIDPANELS_RESET1 = "/lidpanelsreset"
SlashCmdList.LIDPANELS_RESET = function()
  SafeCall("Slash /lidpanelsreset", 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 /LidPanels to open the editor.")
    if LogInfo then LogInfo("Addon loaded") end
  elseif event == "PLAYER_LOGIN" then
    EnsureDefaults()
    local switchedOnLogin = TryAutoProfileSwitch()
    if not switchedOnLogin then
      RebuildAllPanels()
    end
    local createFn = _G.LIDPANELS_CreateEditor or CreateEditor
    if createFn then createFn() end
    local e = GetPanelsEditor()
    if e and e.ApplyTheme then e:ApplyTheme() end
    if e and e.UpdateUILayout then e:UpdateUILayout() end
    if Compat and Compat.After then
      Compat.After(0.2, function()
        local ee = GetPanelsEditor()
        if ee and ee.ApplyTheme then ee:ApplyTheme() end
      end)
    end
    CreateMinimapButton()
    if Compat and Compat.After then
      Compat.After(0.2, UpdateMinimapButton)
      Compat.After(1.0, UpdateMinimapButton)
    end
    if AddCharacterToList and GetCharacterKey then
      AddCharacterToList(GetCharacterKey())
    end
    local onLogin = ResolveOptionalApiFunction("LIDPANELS_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)
