local _, ns = ...
local SlickUI = ns.SlickUI
local Theme = SlickUI.Theme

local ChatTools = {
	Buttons = {},
	EditBoxes = {},
	MaximumLines = 300,
}
SlickUI.ChatTools = ChatTools

local function SetEditBoxChrome(editBox, shown)
	local name = editBox and editBox.GetName and editBox:GetName()
	if type(name) ~= "string" then return end
	for _, suffix in ipairs({ "Left", "Mid", "Right", "FocusLeft", "FocusMid", "FocusRight" }) do
		local region = _G[name .. suffix]
		if region and region.SetAlpha then region:SetAlpha(shown and 1 or 0) end
	end
end

function ChatTools:UpdateEditBox(editBox)
	if not editBox then return end
	local focused = editBox.HasFocus and editBox:HasFocus()
	SetEditBoxChrome(editBox, focused == true)
end

function ChatTools:AttachEditBoxes()
	local count = tonumber(NUM_CHAT_WINDOWS) or 10
	for index = 1, count do
		local editBox = _G["ChatFrame" .. index .. "EditBox"]
		if editBox and not self.EditBoxes[editBox] then
			self.EditBoxes[editBox] = true
			editBox:HookScript("OnEditFocusGained", function(box) ChatTools:UpdateEditBox(box) end)
			editBox:HookScript("OnEditFocusLost", function(box) ChatTools:UpdateEditBox(box) end)
			editBox:HookScript("OnShow", function(box)
				C_Timer.After(0, function() ChatTools:UpdateEditBox(box) end)
			end)
		end
		self:UpdateEditBox(editBox)
	end
end

local function CleanChatText(message)
	if type(message) ~= "string" then
		return nil
	end

	-- Keep the readable portion of links while removing WoW display markup that
	-- is not useful after the text is pasted into another application.
	message = message:gsub("|c%x%x%x%x%x%x%x%x", "")
	message = message:gsub("|r", "")
	message = message:gsub("|H.-|h(.-)|h", "%1")
	message = message:gsub("|T.-|t", "")
	message = message:gsub("|A.-|a", "")
	return message
end

function ChatTools:GetChatFrameTitle(chatFrame)
	local index = chatFrame and chatFrame:GetID()
	if type(index) == "number" and type(FCF_GetChatWindowInfo) == "function" then
		local ok, name = pcall(FCF_GetChatWindowInfo, index)
		if ok and type(name) == "string" and name ~= "" then
			return name
		end
	end
	return "Chat"
end

function ChatTools:Collect(chatFrame)
	chatFrame = chatFrame or SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME
	if not chatFrame or type(chatFrame.GetNumMessages) ~= "function" or type(chatFrame.GetMessageInfo) ~= "function" then
		return "No chat history is available."
	end

	local ok, count = pcall(chatFrame.GetNumMessages, chatFrame)
	if not ok or type(count) ~= "number" then
		return "No chat history is available."
	end

	local first = math.max(1, count - self.MaximumLines + 1)
	local lines = {}
	for index = first, count do
		local messageOK, message = pcall(chatFrame.GetMessageInfo, chatFrame, index)
		if messageOK then
			local cleanOK, clean = pcall(CleanChatText, message)
			if cleanOK and clean and clean ~= "" then
				lines[#lines + 1] = clean
			end
		end
	end

	return table.concat(lines, "\n")
end

function ChatTools:RefreshWindow(selectAll)
	local window = self.Window
	if not window then return end

	local chatFrame = window.ChatFrame or SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME
	window.Title:SetText("SlickUI Chat Copy — " .. self:GetChatFrameTitle(chatFrame))
	local text = self:Collect(chatFrame)
	window.EditBox:SetText(text)
	local _, lineBreaks = text:gsub("\n", "\n")
	window.EditBox:SetHeight(math.max(350, (lineBreaks + 2) * 15))
	if selectAll then
		window.EditBox:SetFocus()
		window.EditBox:HighlightText()
	end
end

function ChatTools:CreateWindow()
	if self.Window then return self.Window end

	local window = CreateFrame("Frame", "SlickUIChatCopyFrame", UIParent, "BackdropTemplate")
	window:SetSize(760, 500)
	window:SetPoint("CENTER")
	window:SetFrameStrata("DIALOG")
	window:SetClampedToScreen(true)
	window:SetMovable(true)
	window:EnableMouse(true)
	window:RegisterForDrag("LeftButton")
	window:SetScript("OnDragStart", window.StartMoving)
	window:SetScript("OnDragStop", window.StopMovingOrSizing)
	Theme:CreatePanel(window)
	window:Hide()
	table.insert(UISpecialFrames, window:GetName())

	Theme:CreateHeaderIcon(window, 34, 14, -9)
	local title = Theme:CreateText(window, 17)
	title:SetPoint("TOPLEFT", 54, -16)
	title:SetText("SlickUI Chat Copy")
	title:SetTextColor(unpack(Theme.Colors.goldText))
	window.Title = title

	local help = Theme:CreateText(window, 10)
	help:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -6)
	help:SetText("Click Select All, press Ctrl+C, then paste the chat text wherever you need it.")
	help:SetTextColor(unpack(Theme.Colors.mutedText))

	local close = CreateFrame("Button", nil, window, "UIPanelCloseButton")
	close:SetPoint("TOPRIGHT", -4, -4)

	local scroll = CreateFrame("ScrollFrame", nil, window, "UIPanelScrollFrameTemplate")
	scroll:SetPoint("TOPLEFT", 18, -68)
	scroll:SetPoint("BOTTOMRIGHT", -38, 52)

	local editBox = CreateFrame("EditBox", nil, scroll)
	editBox:SetMultiLine(true)
	editBox:SetAutoFocus(false)
	editBox:SetWidth(688)
	editBox:SetHeight(350)
	editBox:SetTextInsets(6, 6, 6, 6)
	Theme:SetFont(editBox, 11, "")
	editBox:SetTextColor(unpack(Theme.Colors.text))
	editBox:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
	scroll:SetScrollChild(editBox)
	window.EditBox = editBox

	local selectAll = CreateFrame("Button", nil, window, "UIPanelButtonTemplate")
	selectAll:SetSize(180, 24)
	selectAll:SetPoint("BOTTOMLEFT", 18, 16)
	selectAll:SetText("Select All for Copy")
	selectAll:SetScript("OnClick", function() ChatTools:RefreshWindow(true) end)

	local refresh = CreateFrame("Button", nil, window, "UIPanelButtonTemplate")
	refresh:SetSize(130, 24)
	refresh:SetPoint("LEFT", selectAll, "RIGHT", 10, 0)
	refresh:SetText("Refresh Chat")
	refresh:SetScript("OnClick", function() ChatTools:RefreshWindow(false) end)

	self.Window = window
	return window
end

function ChatTools:Show(chatFrame)
	local window = self:CreateWindow()
	window.ChatFrame = chatFrame or SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME
	self:RefreshWindow(false)
	window:Show()
	window:Raise()
end

function ChatTools:CreateButton(chatFrame, index)
	if not chatFrame or self.Buttons[chatFrame] then return end

	local button = CreateFrame("Button", "SlickUIChatCopyButton" .. index, chatFrame, "UIPanelButtonTemplate")
	button:SetSize(44, 18)
	button:SetPoint("TOPRIGHT", chatFrame, "TOPRIGHT", -2, -2)
	button:SetText("Copy")
	button:SetFrameLevel(chatFrame:GetFrameLevel() + 20)
	button:SetScript("OnClick", function() ChatTools:Show(chatFrame) end)
	button:SetScript("OnEnter", function(self)
		GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT")
		GameTooltip:SetText("Copy chat")
		GameTooltip:AddLine("Opens selectable history for this chat window.", 1, 1, 1, true)
		GameTooltip:Show()
	end)
	button:SetScript("OnLeave", GameTooltip_Hide)
	self.Buttons[chatFrame] = button
	button:SetShown(not SlickUI.DB or not SlickUI.DB.chatTools or SlickUI.DB.chatTools.showCopyButton ~= false)
end

function ChatTools:RefreshButtonVisibility()
	local shown = not SlickUI.DB or not SlickUI.DB.chatTools or SlickUI.DB.chatTools.showCopyButton ~= false
	for _, button in pairs(self.Buttons) do
		button:SetShown(shown)
	end
end

function ChatTools:AttachButtons()
	local count = tonumber(NUM_CHAT_WINDOWS) or 10
	for index = 1, count do
		local chatFrame = _G["ChatFrame" .. index]
		if chatFrame then self:CreateButton(chatFrame, index) end
	end
	self:RefreshButtonVisibility()
end

function ChatTools:Initialize()
	self:AttachButtons()
	self:AttachEditBoxes()

	local events = CreateFrame("Frame")
	events:RegisterEvent("UPDATE_CHAT_WINDOWS")
	events:RegisterEvent("UPDATE_FLOATING_CHAT_WINDOWS")
	events:SetScript("OnEvent", function()
		ChatTools:AttachButtons()
		ChatTools:AttachEditBoxes()
	end)
	self.EventFrame = events

	SLASH_SLICKUICHATCOPY1 = "/slickchatcopy"
	SlashCmdList.SLICKUICHATCOPY = function()
		ChatTools:Show(SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME)
	end
end

SlickUI:RegisterModule("ChatTools", ChatTools)
