
-- rChat: core
-- zork, 2016
-- Updated for WoW Midnight (12.0.0) - 2026

-----------------------------
-- Variables
-----------------------------

local A, L = ...

local cfg = {}
cfg.dropshadow = {}
cfg.dropshadow.offset = {1,-2}
cfg.dropshadow.color = {0,0,0,0.25}
cfg.editbox = {}
cfg.editbox.font = {STANDARD_TEXT_FONT, 13, "THINOUTLINE"}
cfg.chat = {}
cfg.chat.font = {STANDARD_TEXT_FONT, 13, "THINOUTLINE"} --{STANDARD_TEXT_FONT, 12, "OUTLINE"}

-----------------------------
-- Copy Window
-----------------------------

local copyFrame = nil

-- Modern themed palette (class-colored accents, neutral dark shell) shared with rChat GUI
local CC = {
  primary    = {0.686, 0.098, 0.090},
  primaryHi  = {1.00,  0.30,  0.28},
  bg         = {0.045, 0.040, 0.040, 0.985},
  header     = {0.030, 0.027, 0.027, 1.0},
  panel      = {0.020, 0.018, 0.018, 1.0},
  border     = {0.25,  0.10,  0.10,  1.0},
  text       = {0.95,  0.90,  0.90},
  textDim    = {0.58,  0.52,  0.52},
  textBright = {1.00,  1.00,  1.00},
}

local function RefreshCopyPalette()
  if rChatGuiTheme and rChatGuiTheme.ApplyPalette then
    pcall(rChatGuiTheme.ApplyPalette, CC)
  end
end

local function SelectAllCopyText(frame)
  if not frame or not frame.editBox then return end
  frame.editBox:SetFocus()
  frame.editBox:HighlightText()
end

local function CopyAllFromFrame(frame)
  if not frame then return end
  local text = frame.copyText or frame.editBox:GetText() or ""
  if text == "" then return end

  SelectAllCopyText(frame)
  if frame.instruction then
    frame.instruction:SetText(L.Strings["CHAT_COPY_SELECTED"])
    frame.instruction:SetTextColor(CC.primaryHi[1], CC.primaryHi[2], CC.primaryHi[3], 1)
  end
  print("|cff33ff99rChat:|r " .. L.Strings["CHAT_COPY_SELECTED"])
end

local SOLID = "Interface\\Buttons\\WHITE8X8"

local function CreateCopyFrame()
  if copyFrame then return copyFrame end

  RefreshCopyPalette()

  local frame = CreateFrame("Frame", "rChatCopyFrame", UIParent, "BackdropTemplate")
  frame:SetSize(620, 440)
  frame:SetPoint("CENTER")
  frame:SetMovable(true)
  frame:SetResizable(true)
  frame:EnableMouse(true)
  frame:SetClampedToScreen(true)
  frame:SetFrameStrata("DIALOG")
  if frame.SetResizeBounds then frame:SetResizeBounds(420, 280) end
  frame:SetBackdrop({
    bgFile   = SOLID,
    edgeFile = SOLID,
    tile = false, edgeSize = 1,
    insets = {left = 1, right = 1, top = 1, bottom = 1},
  })
  frame:SetBackdropColor(unpack(CC.bg))
  frame:SetBackdropBorderColor(CC.border[1], CC.border[2], CC.border[3], 1)
  frame:Hide()

  -- Soft outer drop shadow for depth
  local shadow = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  shadow:SetPoint("TOPLEFT", -6, 6)
  shadow:SetPoint("BOTTOMRIGHT", 6, -6)
  shadow:SetFrameLevel(math.max(0, frame:GetFrameLevel() - 1))
  shadow:SetBackdrop({ edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 6 })
  shadow:SetBackdropBorderColor(0, 0, 0, 0.35)

  -- ---------------- Header ----------------
  local HEADER_H = 40
  local header = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  header:SetPoint("TOPLEFT", 1, -1)
  header:SetPoint("TOPRIGHT", -1, -1)
  header:SetHeight(HEADER_H)
  header:SetBackdrop({ bgFile = SOLID })
  header:SetBackdropColor(unpack(CC.header))
  header:EnableMouse(true)
  header:RegisterForDrag("LeftButton")
  header:SetScript("OnDragStart", function() frame:StartMoving() end)
  header:SetScript("OnDragStop", function() frame:StopMovingOrSizing() end)
  frame.header = header

  -- Accent underline below header
  local accent = header:CreateTexture(nil, "OVERLAY")
  accent:SetTexture(SOLID)
  accent:SetPoint("BOTTOMLEFT", header, "BOTTOMLEFT", 0, 0)
  accent:SetPoint("BOTTOMRIGHT", header, "BOTTOMRIGHT", 0, 0)
  accent:SetHeight(2)
  accent:SetVertexColor(CC.primary[1], CC.primary[2], CC.primary[3], 1)
  frame.accent = accent

  -- Header icon (addon logo)
  local icon = header:CreateTexture(nil, "ARTWORK")
  icon:SetSize(20, 20)
  icon:SetPoint("LEFT", header, "LEFT", 12, 0)
  icon:SetTexture("Interface\\AddOns\\rChat\\core\\logo")
  frame.icon = icon

  -- Title
  local title = header:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  title:SetPoint("LEFT", icon, "RIGHT", 8, 0)
  title:SetText(L.Strings["CHAT_COPY_WINDOW_TITLE"])
  title:SetTextColor(unpack(CC.textBright))
  frame.title = title

  -- Close button (custom)
  local close = CreateFrame("Button", nil, header)
  close:SetSize(26, 26)
  close:SetPoint("RIGHT", header, "RIGHT", -8, 0)
  local closeText = close:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
  closeText:SetPoint("CENTER")
  closeText:SetText("\195\151") -- multiplication sign as a clean "x"
  closeText:SetTextColor(unpack(CC.textDim))
  close:SetScript("OnEnter", function() closeText:SetTextColor(CC.primaryHi[1], CC.primaryHi[2], CC.primaryHi[3], 1) end)
  close:SetScript("OnLeave", function() closeText:SetTextColor(unpack(CC.textDim)) end)
  close:SetScript("OnClick", function() frame:Hide() end)
  frame.closeBtn = close

  -- ---------------- Footer ----------------
  local FOOTER_H = 46
  local footer = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  footer:SetPoint("BOTTOMLEFT", 1, 1)
  footer:SetPoint("BOTTOMRIGHT", -1, 1)
  footer:SetHeight(FOOTER_H)
  footer:SetBackdrop({ bgFile = SOLID })
  footer:SetBackdropColor(unpack(CC.header))
  frame.footer = footer

  local footerLine = footer:CreateTexture(nil, "OVERLAY")
  footerLine:SetTexture(SOLID)
  footerLine:SetPoint("TOPLEFT", footer, "TOPLEFT", 0, 0)
  footerLine:SetPoint("TOPRIGHT", footer, "TOPRIGHT", 0, 0)
  footerLine:SetHeight(1)
  footerLine:SetVertexColor(CC.border[1], CC.border[2], CC.border[3], 1)

  -- Instruction text
  local instruction = footer:CreateFontString(nil, "OVERLAY", "GameFontNormal")
  instruction:SetPoint("LEFT", footer, "LEFT", 14, 0)
  instruction:SetText(L.Strings["CHAT_COPY_INSTRUCTION"])
  instruction:SetTextColor(unpack(CC.textDim))
  frame.instruction = instruction

  -- "Copy All" button (custom themed)
  local copyAllBtn = CreateFrame("Button", nil, footer, "BackdropTemplate")
  copyAllBtn:SetSize(132, 26)
  copyAllBtn:SetPoint("RIGHT", footer, "RIGHT", -12, 0)
  copyAllBtn:SetBackdrop({
    bgFile = SOLID, edgeFile = SOLID, tile = false, edgeSize = 1,
    insets = {left = 1, right = 1, top = 1, bottom = 1},
  })
  local function StyleBtnIdle()
    copyAllBtn:SetBackdropColor(
      math.min(1, CC.primary[1] * 0.30 + 0.04),
      math.min(1, CC.primary[2] * 0.30 + 0.04),
      math.min(1, CC.primary[3] * 0.30 + 0.04), 1)
    copyAllBtn:SetBackdropBorderColor(
      math.min(1, CC.primary[1] * 0.55 + 0.10),
      math.min(1, CC.primary[2] * 0.55 + 0.10),
      math.min(1, CC.primary[3] * 0.55 + 0.10), 1)
  end
  StyleBtnIdle()
  local copyAllText = copyAllBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
  copyAllText:SetPoint("CENTER")
  copyAllText:SetText(L.Strings["CHAT_COPY_ALL_BUTTON"])
  copyAllText:SetTextColor(unpack(CC.text))
  copyAllBtn:SetScript("OnEnter", function()
    copyAllBtn:SetBackdropColor(
      math.min(1, CC.primary[1] * 0.50 + 0.06),
      math.min(1, CC.primary[2] * 0.50 + 0.06),
      math.min(1, CC.primary[3] * 0.50 + 0.06), 1)
    copyAllBtn:SetBackdropBorderColor(CC.primaryHi[1], CC.primaryHi[2], CC.primaryHi[3], 1)
    copyAllText:SetTextColor(unpack(CC.textBright))
  end)
  copyAllBtn:SetScript("OnLeave", function()
    StyleBtnIdle()
    copyAllText:SetTextColor(unpack(CC.text))
  end)
  copyAllBtn:SetScript("OnClick", function()
    CopyAllFromFrame(frame)
  end)
  frame.copyAllBtn = copyAllBtn

  -- ---------------- Content panel ----------------
  local panel = CreateFrame("Frame", nil, frame, "BackdropTemplate")
  panel:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 12, -12)
  panel:SetPoint("BOTTOMRIGHT", footer, "TOPRIGHT", -12, 12)
  panel:SetBackdrop({
    bgFile = SOLID, edgeFile = SOLID, tile = false, edgeSize = 1,
    insets = {left = 1, right = 1, top = 1, bottom = 1},
  })
  panel:SetBackdropColor(unpack(CC.panel))
  panel:SetBackdropBorderColor(CC.border[1], CC.border[2], CC.border[3], 1)
  frame.panel = panel

  -- Scroll frame for the editbox
  local scrollFrame = CreateFrame("ScrollFrame", "rChatCopyScrollFrame", panel, "UIPanelScrollFrameTemplate")
  scrollFrame:SetPoint("TOPLEFT", panel, "TOPLEFT", 8, -8)
  scrollFrame:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -26, 8)
  frame.scrollFrame = scrollFrame

  -- EditBox for text - displays colored chat text that can be selected
  local editBox = CreateFrame("EditBox", nil, scrollFrame)
  editBox:SetMultiLine(true)
  editBox:SetAutoFocus(false)
  editBox:SetFontObject("ChatFontNormal")
  editBox:SetWidth(550)
  editBox:SetHeight(1000)
  editBox:SetScript("OnEscapePressed", function() frame:Hide() end)
  scrollFrame:SetScrollChild(editBox)
  frame.editBox = editBox

  scrollFrame:SetScript("OnSizeChanged", function(self, w)
    if w and w > 0 then editBox:SetWidth(w) end
  end)

  -- ---------------- Resize grip ----------------
  local grip = CreateFrame("Button", nil, frame)
  grip:SetSize(16, 16)
  grip:SetPoint("BOTTOMRIGHT", -3, 3)
  grip:SetFrameLevel(footer:GetFrameLevel() + 5)
  local gtex = grip:CreateTexture(nil, "OVERLAY")
  gtex:SetAllPoints()
  gtex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
  gtex:SetVertexColor(CC.textDim[1], CC.textDim[2], CC.textDim[3], 0.8)
  grip:SetScript("OnMouseDown", function() frame:StartSizing("BOTTOMRIGHT") end)
  grip:SetScript("OnMouseUp", function() frame:StopMovingOrSizing() end)
  grip:SetScript("OnEnter", function() gtex:SetVertexColor(CC.primaryHi[1], CC.primaryHi[2], CC.primaryHi[3], 1) end)
  grip:SetScript("OnLeave", function() gtex:SetVertexColor(CC.textDim[1], CC.textDim[2], CC.textDim[3], 0.8) end)
  frame.grip = grip

  -- Re-apply class palette each time the frame is shown (covers theme changes)
  frame:SetScript("OnShow", function(self)
    RefreshCopyPalette()
    self:SetBackdropColor(unpack(CC.bg))
    self:SetBackdropBorderColor(CC.border[1], CC.border[2], CC.border[3], 1)
    accent:SetVertexColor(CC.primary[1], CC.primary[2], CC.primary[3], 1)
    StyleBtnIdle()
  end)

  copyFrame = frame
  return frame
end

-- Show copy window with text
local function ShowCopyWindow(text)
  if not text or text == "" then return end

  local frame = CreateCopyFrame()

  frame.copyText = text
  frame.editBox:SetText(frame.copyText)
  frame:Show()
  SelectAllCopyText(frame)
  if frame.instruction then
    frame.instruction:SetText(L.Strings["CHAT_COPY_SELECTED"])
    frame.instruction:SetTextColor(CC.primaryHi[1], CC.primaryHi[2], CC.primaryHi[3], 1)
  end
end

-----------------------------
-- Functions
-----------------------------

--SkinChat
local function SkinChat(self)
  if not self then return end
  local name = self:GetName()
  
  -- Font settings
  self:SetFont(unpack(cfg.chat.font))
  self:SetShadowOffset(unpack(cfg.dropshadow.offset))
  self:SetShadowColor(unpack(cfg.dropshadow.color))
  
  --chat fading
  self:SetFading(true)
  
  -- Set fade time from settings
  local fadeTime = 20 -- default
  if rChatDB and rChatDB.chat and rChatDB.chat.chatFadeTime then
    fadeTime = rChatDB.chat.chatFadeTime
  end
  self:SetTimeVisible(fadeTime)
  self:SetFadeDuration(1) -- 1 second fade animation
  
  --hide button frame
  local bf = _G[name.."ButtonFrame"]
  if bf then
    bf:HookScript("OnShow", bf.Hide)
    bf:Hide()
  end
  
  --modernize chat tab - remove textures, white text only
  local tab = _G[name.."Tab"]
  if tab then
    -- COMPLETE texture removal for WoW 12.0
    -- Kill all textures by setting them to nil and alpha 0
    local function KillTexture(texture)
      if texture then
        texture:SetTexture(nil)
        texture:SetAlpha(0)
        texture:Hide()
      end
    end
    
    -- Old naming convention (pre-12.0)
    local oldTextures = {
      "Left", "Middle", "Right",
      "SelectedLeft", "SelectedMiddle", "SelectedRight",
      "HighlightLeft", "HighlightMiddle", "HighlightRight",
      "ActiveLeft", "ActiveMiddle", "ActiveRight",
    }
    for _, texName in pairs(oldTextures) do
      KillTexture(_G[name.."Tab"..texName])
    end
    
    -- New naming convention (12.0+)
    KillTexture(tab.leftTexture)
    KillTexture(tab.middleTexture)
    KillTexture(tab.rightTexture)
    KillTexture(tab.leftSelectedTexture)
    KillTexture(tab.middleSelectedTexture)
    KillTexture(tab.rightSelectedTexture)
    KillTexture(tab.leftHighlightTexture)
    KillTexture(tab.middleHighlightTexture)
    KillTexture(tab.rightHighlightTexture)
    KillTexture(tab.leftDisabledTexture)
    KillTexture(tab.middleDisabledTexture)
    KillTexture(tab.rightDisabledTexture)
    
    -- Glow textures
    KillTexture(tab.glow)
    KillTexture(tab.leftGlow)
    KillTexture(tab.middleGlow)
    KillTexture(tab.rightGlow)
    
    -- Background textures (WoW 12.0 specific)
    if tab.Background then
      KillTexture(tab.Background)
    end
    
    -- Try to find and kill ANY texture regions on the tab
    for i = 1, tab:GetNumRegions() do
      local region = select(i, tab:GetRegions())
      if region and region:GetObjectType() == "Texture" then
        local textureName = region:GetName()
        -- Don't kill the conversation icon or text
        if not textureName or not textureName:match("Conversation") then
          KillTexture(region)
        end
      end
    end
    
    -- Style the tab text - white color, clean font
    local tabText = tab:GetFontString() or _G[name.."TabText"] or tab.Text
    if tabText then
      tabText:SetFont(STANDARD_TEXT_FONT, 12, "OUTLINE")
      tabText:SetTextColor(1, 1, 1, 1)  -- White
      tabText:SetShadowOffset(1, -1)
      tabText:SetShadowColor(0, 0, 0, 0.8)
    end
    
    -- Hook to maintain white color
    tab:HookScript("OnShow", function(self)
      local text = self:GetFontString() or self.Text
      if text then
        text:SetTextColor(1, 1, 1, 1)
      end
    end)
    
    -- Highlight on mouse over
    tab:HookScript("OnEnter", function(self)
      local text = self:GetFontString() or self.Text
      if text then
        text:SetTextColor(0.9, 0.9, 0.5, 1)  -- Slight highlight on hover
      end
    end)
    
    tab:HookScript("OnLeave", function(self)
      local text = self:GetFontString() or self.Text
      if text then
        text:SetTextColor(1, 1, 1, 1)  -- Back to white
      end
    end)
  end
  
  --editbox
  local eb = _G[name.."EditBox"]
  if eb then
    eb:SetAltArrowKeyMode(false)
    --textures
    local left = _G[name.."EditBoxLeft"]
    local mid = _G[name.."EditBoxMid"]
    local right = _G[name.."EditBoxRight"]
    if left then left:Hide() end
    if mid then mid:Hide() end
    if right then right:Hide() end
    --reposition
    eb:ClearAllPoints()
    
    -- Get editbox position from settings
    local editboxPos = "top" -- default
    if rChatDB and rChatDB.chat and rChatDB.chat.editboxPosition then
      editboxPos = rChatDB.chat.editboxPosition
    end
    
    if editboxPos == "bottom" then
      -- Position at bottom
      eb:SetPoint("TOP", self, "BOTTOM", 0, -3)
      eb:SetPoint("LEFT", self, -5, 0)
      eb:SetPoint("RIGHT", self, 10, 0)
    else
      -- Position at top (default)
      if name == "ChatFrame2" then
        eb:SetPoint("BOTTOM", self, "TOP", 0, 22+24)
      else
        eb:SetPoint("BOTTOM", self, "TOP", 0, 22)
      end
      eb:SetPoint("LEFT", self, -5, 0)
      eb:SetPoint("RIGHT", self, 10, 0)
    end
  end
end

local function UpdateBackgroundAnchors(self)
  --fix weird combat log
  self:SetClampRectInsets(0, 0, 0, 0)
end

-- Function to update editbox positions for all chat frames
function L:UpdateEditBoxPositions()
  local editboxPos = "top" -- default
  if rChatDB and rChatDB.chat and rChatDB.chat.editboxPosition then
    editboxPos = rChatDB.chat.editboxPosition
  end
  
  for i = 1, NUM_CHAT_WINDOWS do
    local chatframe = _G["ChatFrame"..i]
    if chatframe then
      local name = chatframe:GetName()
      local eb = _G[name.."EditBox"]
      if eb then
        eb:ClearAllPoints()
        
        if editboxPos == "bottom" then
          -- Position at bottom
          eb:SetPoint("TOP", chatframe, "BOTTOM", 0, -3)
          eb:SetPoint("LEFT", chatframe, -5, 0)
          eb:SetPoint("RIGHT", chatframe, 10, 0)
        else
          -- Position at top (default)
          if name == "ChatFrame2" then
            eb:SetPoint("BOTTOM", chatframe, "TOP", 0, 22+24)
          else
            eb:SetPoint("BOTTOM", chatframe, "TOP", 0, 22)
          end
          eb:SetPoint("LEFT", chatframe, -5, 0)
          eb:SetPoint("RIGHT", chatframe, 10, 0)
        end
      end
    end
  end
end

-- Function to update chat fade time for all chat frames
function L:UpdateChatFadeTime()
  local fadeTime = 20 -- default
  if rChatDB and rChatDB.chat and rChatDB.chat.chatFadeTime then
    fadeTime = rChatDB.chat.chatFadeTime
  end
  
  for i = 1, NUM_CHAT_WINDOWS do
    local chatframe = _G["ChatFrame"..i]
    if chatframe then
      chatframe:SetTimeVisible(fadeTime)
      chatframe:SetFadeDuration(1)
    end
  end
end

-- Function to get all chat text for the copy window.
-- Uses issecretvalue() (same approach as Prat) to skip tainted entries safely.
local function GetChatText(chatFrame)
  local lines = {}
  local numMessages = chatFrame:GetNumMessages()

  for i = 1, numMessages do
    local ok, msg = pcall(chatFrame.GetMessageInfo, chatFrame, i)
    if ok and msg ~= nil then
      if issecretvalue and issecretvalue(msg) then
        -- tainted: skip silently
      else
        lines[#lines + 1] = msg
      end
    end
  end

  if #lines == 0 then return "" end
  return table.concat(lines, "\n")
end

-- Create chat copy button for a frame
local function CreateChatCopyButton(chatFrame)
  if not chatFrame or chatFrame.rChatCopyButton then return end
  
  local button = CreateFrame("Button", nil, chatFrame)
  button:SetSize(18, 18)
  button:SetPoint("BOTTOMRIGHT", chatFrame, "BOTTOMRIGHT", -2, 2)
  
  -- Create icon texture (using custom copy icon)
  local icon = button:CreateTexture(nil, "ARTWORK")
  icon:SetAllPoints()
  icon:SetTexture("Interface\\AddOns\\rChat\\core\\copy-icon")
  icon:SetAlpha(0.15) -- Very subtle, almost invisible
  
  -- Highlight texture on hover
  local highlight = button:CreateTexture(nil, "HIGHLIGHT")
  highlight:SetAllPoints()
  highlight:SetTexture("Interface\\AddOns\\rChat\\core\\copy-icon")
  highlight:SetAlpha(0)
  
  -- Hover effects
  button:SetScript("OnEnter", function(self)
    icon:SetAlpha(1.0) -- Fully visible on hover
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:SetText(L.Strings["CHAT_COPY_BUTTON_TOOLTIP"])
    GameTooltip:Show()
  end)
  
  button:SetScript("OnLeave", function(self)
    icon:SetAlpha(0.15) -- Back to almost invisible
    GameTooltip:Hide()
  end)
  
  -- Click handler
  button:SetScript("OnClick", function(self)
    local chatText = GetChatText(chatFrame)
    if chatText and #chatText > 0 then
      ShowCopyWindow(chatText)
    else
      print("|cff33ff99rChat:|r " .. L.Strings["CHAT_COPY_NO_MESSAGES"])
    end
  end)
  
  chatFrame.rChatCopyButton = button
  
  -- Hide if setting is disabled
  if not (rChatDB and rChatDB.chat and rChatDB.chat.enableChatCopyButton) then
    button:Hide()
  end
end

-- Update all chat copy buttons visibility
function L:UpdateChatCopyButtons()
  local enabled = rChatDB and rChatDB.chat and rChatDB.chat.enableChatCopyButton
  
  for i = 1, NUM_CHAT_WINDOWS do
    local chatframe = _G["ChatFrame"..i]
    if chatframe then
      if enabled then
        CreateChatCopyButton(chatframe)
        if chatframe.rChatCopyButton then
          chatframe.rChatCopyButton:Show()
        end
      else
        if chatframe.rChatCopyButton then
          chatframe.rChatCopyButton:Hide()
        end
      end
    end
  end
end

--OpenTemporaryWindow (AddMessage / mouse scroll applied in the FCF hook below)
local function OpenTemporaryWindow()
  for _, name in next, CHAT_FRAMES do
    local frame = _G[name]
    if (frame and frame.isTemporary) then
      SkinChat(frame)
    end
  end
end

--OnMouseScroll
local function OnMouseScroll(self,dir)
  if(dir > 0) then
    if(IsShiftKeyDown()) then self:ScrollToTop() else self:ScrollUp() end
  else
    if(IsShiftKeyDown()) then self:ScrollToBottom() else self:ScrollDown() end
  end
end

--we hook SetItemRef instead of replacing it, to avoid ADDON_ACTION_BLOCKED
--errors when Blizzard's handler triggers protected functions (e.g. SetPassThroughButtons
--on map pins when opening quest/worldmap links).
--
--hooksecurefunc is a POST-hook: Blizzard's original always runs first in its
--own secure context (no taint). rChat then handles its two custom cases:
--  1. "url:" links  - Blizzard ignores unknown link types, so we show our UI
--  2. Alt+player    - Blizzard shows the inspect/trade menu; we ALSO invite.
--                     Both actions happen, which is acceptable behaviour.
hooksecurefunc("SetItemRef", function(link, ...)
  if type(link) ~= "string" or (issecretvalue and issecretvalue(link)) then
    return
  end
  local linkType, linkData = link:match("(%a+):(.+)")
  if not linkType then return end

  -- Handle URL links: Blizzard does nothing with "url:" type, we show copy UI
  if linkType == "url" then
    if rChatDB and rChatDB.chat and rChatDB.chat.enableURLCopy then
      ShowCopyWindow(linkData)
    else
      local eb = LAST_ACTIVE_CHAT_EDIT_BOX or ChatFrame1EditBox
      if eb then
        eb:SetText(linkData)
        eb:SetFocus()
        eb:HighlightText()
        if not eb:IsShown() then eb:Show() end
      end
    end
    return
  end

  -- Handle Alt+Click player invite (in addition to Blizzard's default action)
  if IsAltKeyDown() and linkType == "player" then
    local playerName = linkData:match("([^:]+)")
    if playerName and not (issecretvalue and issecretvalue(playerName)) then
      pcall(C_PartyInfo.InviteUnit, playerName)
    end
    return
  end
end)

-- True when value is a Midnight "secret" that tainted code must not transform.
local function IsSecret(value)
  return issecretvalue and issecretvalue(value)
end

-- Short channel labels applied in AddMessage (after HistoryKeeper), never via
-- replacing CHAT_*_GET globals — those taint MessageEventHandler and break
-- ChatHistory_GetToken on secret NPC/player GUIDs (e.g. MONSTER_YELL).
local CHANNEL_SHORT = {
  {"|Hchannel:Guild|h.-|h",            "|Hchannel:Guild|hG.|h"},
  {"|Hchannel:Officer|h.-|h",          "|Hchannel:Officer|hGO.|h"},
  {"|Hchannel:Party|h.-|h",            "|Hchannel:Party|hP.|h"},
  {"|Hchannel:Party%-?Leader|h.-|h",   "|Hchannel:Party|hPL.|h"},
  {"|Hchannel:Raid|h.-|h",             "|Hchannel:Raid|hR.|h"},
  {"|Hchannel:Raid%-?Leader|h.-|h",    "|Hchannel:Raid|hRL.|h"},
  {"|Hchannel:RaidWarning|h.-|h",      "|Hchannel:RaidWarning|hRW.|h"},
  {"|Hchannel:Battleground|h.-|h",     "|Hchannel:Battleground|hBG.|h"},
  {"|Hchannel:Instance|h.-|h",         "|Hchannel:Battleground|hI.|h"},
}

local function ShortenChannelLabels(t)
  t = t:gsub("|h%[(%d+)%. .-%]|h", "|h%1.|h")
  for i = 1, #CHANNEL_SHORT do
    local pair = CHANNEL_SHORT[i]
    t = t:gsub(pair[1], pair[2])
  end
  return t
end

local function TryPrefixPlayerLevel(t)
  if not (rChatDB and rChatDB.chat and rChatDB.chat.showPlayerLevel) then
    return t
  end
  local playerName = t:match("|Hplayer:([^:|]+)")
  if not playerName or IsSecret(playerName) then
    return t
  end
  local baseName = playerName:match("^([^%-]+)") or playerName
  local level
  if UnitExists(baseName) then
    level = UnitLevel(baseName)
  elseif UnitExists(playerName) then
    level = UnitLevel(playerName)
  end
  if level and not IsSecret(level) and level > 0 then
    return "|cff888888[" .. level .. "]|r " .. t
  end
  return t
end

--AddMessage — runs after Blizzard ChatHistory_GetAccessID, so string work here
--does not taint HistoryKeeper. Never touch secret strings.
local function AddMessage(self, text, ...)
  if type(text) ~= "string" or IsSecret(text) then
    return self.DefaultAddMessage(self, text, ...)
  end

  local ok, result = pcall(function()
    local t = text

    if rChatDB and rChatDB.chat and rChatDB.chat.showTimestamp then
      t = "|cff888888[" .. date("%H:%M") .. "]|r " .. t
    end

    t = TryPrefixPlayerLevel(t)
    t = ShortenChannelLabels(t)
    t = t:gsub("([wWhH][wWtT][wWtT][%.pP]%S+[^%p%s])", "|cffffffff|Hurl:%1|h[%1]|h|r")
    return t
  end)

  if ok and type(result) == "string" and not IsSecret(result) then
    text = result
  end

  return self.DefaultAddMessage(self, text, ...)
end

-----------------------------
-- Stuff
-----------------------------

--editbox font
ChatFontNormal:SetFont(unpack(cfg.editbox.font))
ChatFontNormal:SetShadowOffset(unpack(cfg.dropshadow.offset))
ChatFontNormal:SetShadowColor(unpack(cfg.dropshadow.color))

--font size
CHAT_FONT_HEIGHTS = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}

--tabs (read by FCFTab_UpdateAlpha; not in HistoryKeeper path)
CHAT_TAB_HIDE_DELAY = 1
CHAT_FRAME_TAB_NORMAL_MOUSEOVER_ALPHA = 1
CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA = 0
CHAT_FRAME_TAB_SELECTED_MOUSEOVER_ALPHA = 1
CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA = 0
CHAT_FRAME_TAB_ALERTING_MOUSEOVER_ALPHA = 1
CHAT_FRAME_TAB_ALERTING_NOMOUSE_ALPHA = 1

-- Function to update tab visibility settings
local function UpdateTabVisibility()
  if rChatDB and rChatDB.chat and rChatDB.chat.alwaysShowTabs then
    -- Keep tabs always visible
    CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA = 1
    CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA = 1
  else
    -- Default: tabs fade out
    CHAT_FRAME_TAB_NORMAL_NOMOUSE_ALPHA = 0
    CHAT_FRAME_TAB_SELECTED_NOMOUSE_ALPHA = 0
  end
  
  -- Apply to all chat frames immediately
  for i = 1, NUM_CHAT_WINDOWS do
    local chatFrame = _G["ChatFrame"..i]
    if chatFrame then
      FCFTab_UpdateAlpha(chatFrame)
    end
  end
end

-- Do NOT replace CHAT_*_GET / CHAT_FLAG_* globals. Reading those from
-- MessageEventHandler taints execution; ChatHistory_GetToken then errors on
-- secret sender GUIDs (MONSTER_*, whispers in instances, etc.). Channel
-- shortening and flags are handled in AddMessage / left to Blizzard defaults.

--don't cut the toastframe
if BNToastFrame then
  BNToastFrame:SetClampedToScreen(true)
  BNToastFrame:SetClampRectInsets(-15,15,15,-15)
end

--ChatFrameMenuButton
if ChatFrameMenuButton then
  ChatFrameMenuButton:HookScript("OnShow", ChatFrameMenuButton.Hide)
  ChatFrameMenuButton:Hide()
end

--ChatFrameChannelButton
if ChatFrameChannelButton then
  ChatFrameChannelButton:HookScript("OnShow", ChatFrameChannelButton.Hide)
  ChatFrameChannelButton:Hide()
end

--ChatFrameToggleVoiceDeafenButton
if ChatFrameToggleVoiceDeafenButton then
  ChatFrameToggleVoiceDeafenButton:HookScript("OnShow", ChatFrameToggleVoiceDeafenButton.Hide)
  ChatFrameToggleVoiceDeafenButton:Hide()
end

--ChatFrameToggleVoiceMuteButton
if ChatFrameToggleVoiceMuteButton then
  ChatFrameToggleVoiceMuteButton:HookScript("OnShow", ChatFrameToggleVoiceMuteButton.Hide)
  ChatFrameToggleVoiceMuteButton:Hide()
end

--hide the friend micro button
local button = QuickJoinToastButton or FriendsMicroButton
if button then
  button:HookScript("OnShow", button.Hide)
  button:Hide()
end

--skin chat
for i = 1, NUM_CHAT_WINDOWS do
  local chatframe = _G["ChatFrame"..i]
  if chatframe then
    SkinChat(chatframe)
    --adjust channel display
    if (i ~= 2) then
      chatframe.DefaultAddMessage = chatframe.AddMessage
      chatframe.AddMessage = AddMessage
    end
  end
end

-- Shift+scroll: set OnMouseWheel on frames (do not replace FloatingChatFrame_OnMouseScroll
-- global — keeps Blizzard's secure references intact).
local function ApplyMouseScroll(frame)
  if frame and not frame.rChatMouseScroll then
    frame:SetScript("OnMouseWheel", OnMouseScroll)
    frame.rChatMouseScroll = true
  end
end

for i = 1, NUM_CHAT_WINDOWS do
  ApplyMouseScroll(_G["ChatFrame"..i])
end

--temporary chat windows
hooksecurefunc("FCF_OpenTemporaryWindow", function()
  OpenTemporaryWindow()
  for _, name in next, CHAT_FRAMES do
    local frame = _G[name]
    if frame and frame.isTemporary then
      ApplyMouseScroll(frame)
      if not frame.DefaultAddMessage then
        frame.DefaultAddMessage = frame.AddMessage
        frame.AddMessage = AddMessage
      end
    end
  end
end)

--background thingy
hooksecurefunc("FloatingChatFrame_UpdateBackgroundAnchors", UpdateBackgroundAnchors)

-----------------------------
-- Initialize rChat modules
-----------------------------

-- LibDBIcon and Minimap integration
local LDB = LibStub:GetLibrary("LibDataBroker-1.1", true)
local LDBIcon = LibStub:GetLibrary("LibDBIcon-1.0", true)

-- Create LDB data object
if LDB then
    local rChatLDB = LDB:NewDataObject("rChat", {
        type = "launcher",
        text = "rChat",
        icon = "Interface\\AddOns\\rChat\\core\\logo",
        OnClick = function(clickedFrame, button)
            if button == "LeftButton" then
                -- Open custom settings window directly
                L:ShowConfig()
            elseif button == "RightButton" then
                -- Toggle Chicchai
                local enabled = L:GetConfig("chicchai", "enabled")
                L:SetConfig("chicchai", "enabled", not enabled)
                print("|cff33ff99rChat:|r Chicchai", not enabled and "|cff00ff00" .. L.Strings["CHICCHAI_ENABLED"] .. "|r" or "|cffff0000" .. L.Strings["CHICCHAI_DISABLED"] .. "|r")
                ReloadUI()
            end
        end,
        OnTooltipShow = function(tooltip)
            local tc = {}
            if rChatGuiTheme and rChatGuiTheme.ApplyPalette then
                rChatGuiTheme.ApplyPalette(tc)
            end
            local hex = "af1917"
            if tc.primary and rChatGuiTheme and rChatGuiTheme.RGBToWoWColorHex then
                hex = rChatGuiTheme.RGBToWoWColorHex(tc.primary[1], tc.primary[2], tc.primary[3])
            end
            tooltip:AddLine("|cff" .. hex .. "rChat|r")
            tooltip:AddLine(L.Strings["MINIMAP_TOOLTIP"])
        end,
    })
end

-- Function to update minimap icon visibility
function L:UpdateMinimapIcon()
    if LDBIcon and rChatDB and rChatDB.minimap then
        if rChatDB.minimap.hide then
            LDBIcon:Hide("rChat")
        else
            LDBIcon:Show("rChat")
        end
    end
end

-- Initialize minimap icon
local function InitMinimapIcon()
    if LDBIcon then
        -- Register with LibDBIcon
        LDBIcon:Register("rChat", rChatLDB or LDB:GetDataObjectByName("rChat"), rChatDB.minimap)
        
        -- Apply initial visibility
        L:UpdateMinimapIcon()
    end
end

-- Create event frame for initialization
local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("PLAYER_LOGIN")
eventFrame:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_LOGIN" then
        -- Initialize config
        L:InitConfig()
        
        -- Initialize minimap icon
        InitMinimapIcon()
        
        -- Register Interface Options panel
        L:RegisterInterfaceOptions()
        
        -- Apply tab visibility settings
        UpdateTabVisibility()
        
        -- Update editbox positions
        C_Timer.After(0.5, function()
            L:UpdateEditBoxPositions()
        end)
        
        -- Create chat copy buttons
        C_Timer.After(0.7, function()
            L:UpdateChatCopyButtons()
        end)
        
        -- Initialize Chicchai if enabled
        L:InitChicchai()
        
        -- Unregister event
        self:UnregisterEvent("PLAYER_LOGIN")
    end
end)

-- Blizzard Addon Compartment (TOC)
function rChat_OnAddonCompartmentClick(_, btn)
    if btn == "LeftButton" then
        L:ShowConfig()
    elseif btn == "RightButton" then
        local enabled = L:GetConfig("chicchai", "enabled")
        L:SetConfig("chicchai", "enabled", not enabled)
        print("|cff33ff99rChat:|r Chicchai", not enabled and "|cff00ff00" .. L.Strings["CHICCHAI_ENABLED"] .. "|r" or "|cffff0000" .. L.Strings["CHICCHAI_DISABLED"] .. "|r")
        ReloadUI()
    end
end

function rChat_OnAddonCompartmentEnter(_, tooltip)
    if not tooltip or not tooltip.AddLine then return end
    local tc = {}
    if rChatGuiTheme and rChatGuiTheme.ApplyPalette then
        rChatGuiTheme.ApplyPalette(tc)
    end
    local hex = "af1917"
    if tc.primary and rChatGuiTheme and rChatGuiTheme.RGBToWoWColorHex then
        hex = rChatGuiTheme.RGBToWoWColorHex(tc.primary[1], tc.primary[2], tc.primary[3])
    end
    tooltip:AddLine("|cff" .. hex .. "rChat|r")
    tooltip:AddLine(L.Strings["MINIMAP_TOOLTIP"])
end

function rChat_OnAddonCompartmentLeave()
    GameTooltip:Hide()
end
