local addonName, ApiVault = ...

ApiVault.ModuleBrowser = ApiVault.ModuleBrowser or {}

local ROW_HEIGHT = 22
local ROW_COUNT = 22
local FILTERS = { "All", "LoadOnDemand", "HasDeps", "SavedVars", "Secure", "Lua", "XML" }

local function createFont(parent, template, justify)
	local font = parent:CreateFontString(nil, "OVERLAY", template or "GameFontNormal")
	font:SetJustifyH(justify or "LEFT")
	return font
end

local function setBackdrop(frame, r, g, b, a)
	if not frame.SetBackdrop then
		return
	end
	frame:SetBackdrop({
		bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
		edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
		tile = true,
		tileSize = 32,
		edgeSize = 32,
		insets = { left = 8, right = 8, top = 8, bottom = 8 },
	})
	frame:SetBackdropColor(r or 0.04, g or 0.04, b or 0.06, a or 0.96)
end

local function createBackdropFrame(name, parent)
	return CreateFrame("Frame", name, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
end

function ApiVault.ModuleBrowser:Create()
	if self.frame then
		return
	end

	local frame = createBackdropFrame("ApiVaultModuleBrowserFrame", UIParent)
	self.frame = frame
	frame:SetSize(1040, 650)
	frame:SetPoint("CENTER", 90, -90)
	frame:SetFrameStrata("FULLSCREEN_DIALOG")
	frame:SetMovable(true)
	frame:EnableMouse(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", frame.StartMoving)
	frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
	if frame.SetResizable then
		frame:SetResizable(true)
		if frame.SetResizeBounds then
			frame:SetResizeBounds(860, 500)
		elseif frame.SetMinResize then
			frame:SetMinResize(860, 500)
		end
	end
	setBackdrop(frame)
	if ApiVault.PrepareWindow then
		ApiVault:PrepareWindow(frame, "ModuleBrowser")
	end
	frame:Hide()

	local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
	close:SetPoint("TOPRIGHT", -6, -6)

	local title = createFont(frame, "GameFontHighlightLarge")
	title:SetPoint("TOPLEFT", 18, -16)
	title:SetText("ApiVault - TOC Module Browser")

	local tocEntries, moduleEntries, fileRefs = 0, 0, 0
	if ApiVault.ModuleCatalog and ApiVault.ModuleCatalog.GetStats then
		tocEntries, moduleEntries, fileRefs = ApiVault.ModuleCatalog:GetStats()
	end
	local meta = createFont(frame, "GameFontDisableSmall")
	meta:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -3)
	meta:SetText("TOCs " .. tostring(tocEntries) .. " - modules " .. tostring(moduleEntries) .. " - TOC file refs " .. tostring(fileRefs) .. " - source root Interface/AddOns/ApiVault/Examples/Interface")

	local searchBox = CreateFrame("EditBox", nil, frame, "InputBoxTemplate")
	self.searchBox = searchBox
	searchBox:SetSize(360, 26)
	searchBox:SetPoint("TOPLEFT", frame, "TOPLEFT", 22, -62)
	searchBox:SetAutoFocus(false)
	searchBox:SetText("")
	searchBox:SetScript("OnTextChanged", function()
		ApiVault.ModuleBrowser:RefreshSearch()
	end)
	searchBox:SetScript("OnEnterPressed", function(selfBox)
		selfBox:ClearFocus()
	end)

	local placeholder = createFont(frame, "GameFontDisableSmall")
	placeholder:SetPoint("LEFT", searchBox, "RIGHT", 10, 0)
	placeholder:SetText("Search module, TOC, dependency, title, or notes")

	self.filter = "All"
	self.filterButtons = {}
	local prev
	for _, filterName in ipairs(FILTERS) do
		local button = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
		button:SetSize(filterName == "LoadOnDemand" and 112 or filterName == "SavedVars" and 86 or 76, 22)
		if prev then
			button:SetPoint("LEFT", prev, "RIGHT", 4, 0)
		else
			button:SetPoint("TOPLEFT", searchBox, "BOTTOMLEFT", 0, -8)
		end
		button:SetText(filterName)
		button:SetScript("OnClick", function()
			ApiVault.ModuleBrowser.filter = filterName
			ApiVault.ModuleBrowser:RefreshFilterButtons()
			ApiVault.ModuleBrowser:RefreshSearch()
		end)
		self.filterButtons[filterName] = button
		prev = button
	end

	local listTitle = createFont(frame, "GameFontNormalSmall")
	listTitle:SetPoint("TOPLEFT", frame, "TOPLEFT", 22, -120)
	listTitle:SetText("TOC modules")

	local listBg = createBackdropFrame(nil, frame)
	self.listBg = listBg
	listBg:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -138)
	listBg:SetSize(450, 475)
	setBackdrop(listBg, 0.02, 0.02, 0.03, 0.82)

	local scroll = CreateFrame("ScrollFrame", "ApiVaultModuleBrowserScrollFrame", frame, "FauxScrollFrameTemplate")
	self.scroll = scroll
	scroll:SetPoint("TOPLEFT", listBg, "TOPLEFT", 10, -10)
	scroll:SetPoint("BOTTOMRIGHT", listBg, "BOTTOMRIGHT", -28, 10)
	scroll:SetScript("OnVerticalScroll", function(selfScroll, offset)
		FauxScrollFrame_OnVerticalScroll(selfScroll, offset, ROW_HEIGHT, function()
			ApiVault.ModuleBrowser:RefreshRows()
		end)
	end)

	self.rows = {}
	for index = 1, ROW_COUNT do
		local row = CreateFrame("Button", nil, frame)
		row:SetHeight(ROW_HEIGHT)
		row:SetPoint("TOPLEFT", scroll, "TOPLEFT", 0, -((index - 1) * ROW_HEIGHT))
		row:SetPoint("RIGHT", scroll, "RIGHT", -4, 0)
		row.text = createFont(row, "GameFontHighlightSmall")
		row.text:SetPoint("LEFT", row, "LEFT", 2, 0)
		row.text:SetPoint("RIGHT", row, "RIGHT", -4, 0)
		row.text:SetWordWrap(false)
		row:SetScript("OnClick", function(selfRow)
			ApiVault.ModuleBrowser:SelectEntry(selfRow.entry)
		end)
		self.rows[index] = row
	end

	local detailBg = createBackdropFrame(nil, frame)
	self.detailBg = detailBg
	detailBg:SetPoint("TOPLEFT", listBg, "TOPRIGHT", 14, 0)
	detailBg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, 38)
	setBackdrop(detailBg, 0.025, 0.025, 0.035, 0.86)

	self.detailTitle = createFont(detailBg, "GameFontHighlightLarge")
	self.detailTitle:SetPoint("TOPLEFT", detailBg, "TOPLEFT", 14, -14)
	self.detailTitle:SetPoint("RIGHT", detailBg, "RIGHT", -14, 0)
	self.detailTitle:SetWordWrap(false)
	self.detailTitle:SetText("Select a module")

	self.detailScroll = CreateFrame("ScrollFrame", nil, detailBg, "UIPanelScrollFrameTemplate")
	self.detailScroll:SetPoint("TOPLEFT", self.detailTitle, "BOTTOMLEFT", 0, -12)
	self.detailScroll:SetPoint("BOTTOMRIGHT", detailBg, "BOTTOMRIGHT", -30, 14)
	self.detailBox = CreateFrame("EditBox", nil, self.detailScroll)
	self.detailBox:SetMultiLine(true)
	self.detailBox:SetAutoFocus(false)
	self.detailBox:SetFontObject(ChatFontNormal)
	self.detailBox:SetWidth(540)
	self.detailBox:SetTextInsets(4, 4, 4, 4)
	self.detailBox:SetScript("OnEscapePressed", function(selfBox) selfBox:ClearFocus() end)
	self.detailScroll:SetScrollChild(self.detailBox)

	self:RefreshFilterButtons()
	self:RefreshSearch()
end

function ApiVault.ModuleBrowser:Toggle()
	self:Create()
	if self.frame:IsShown() then
		self.frame:Hide()
	else
		self.frame:Show()
		self.searchBox:SetFocus()
	end
end

function ApiVault.ModuleBrowser:RefreshFilterButtons()
	for name, button in pairs(self.filterButtons or {}) do
		button:SetEnabled(name ~= self.filter)
	end
end

function ApiVault.ModuleBrowser:RefreshSearch()
	if not self.frame then
		return
	end
	if not ApiVault.ModuleCatalog or not ApiVault.ModuleCatalog.Search then
		self.results = {}
	else
		local query = self.searchBox and self.searchBox:GetText() or ""
		self.results = ApiVault.ModuleCatalog:Search(query, self.filter or "All", 300)
	end
	self:RefreshRows()
end

function ApiVault.ModuleBrowser:RefreshRows()
	local results = self.results or {}
	local offset = FauxScrollFrame_GetOffset(self.scroll)
	FauxScrollFrame_Update(self.scroll, #results, ROW_COUNT, ROW_HEIGHT)
	for index = 1, ROW_COUNT do
		local resultIndex = offset + index
		local row = self.rows[index]
		local entry = results[resultIndex]
		row.entry = entry
		if entry then
			row:Show()
			local data = entry.data or {}
			local flags = ""
			if tostring(data.loadOnDemand or "") == "1" then
				flags = flags .. " |cff88ccffLoD|r"
			end
			if tostring(data.secure or "") == "1" or tostring(data.useSecureEnvironment or "") == "1" then
				flags = flags .. " |cffff8080secure|r"
			end
			row.text:SetText(tostring(entry.display or "?") .. flags)
		else
			row:Hide()
		end
	end
end

function ApiVault.ModuleBrowser:SelectEntry(entry)
	self.selectedEntry = entry
	if ApiVault.ChecklistExport and ApiVault.ChecklistExport.CaptureContext and entry then
		ApiVault.ChecklistExport:CaptureContext("Module", entry.data or entry, entry.display, entry.kind, entry.module, entry.riskClass)
	end
	if not entry then
		return
	end
	self.detailTitle:SetText(tostring(entry.display or "Module"))
	local text = ApiVault.ModuleCatalog and ApiVault.ModuleCatalog.DescribeModule and ApiVault.ModuleCatalog:DescribeModule(entry) or "Module metadata unavailable."
	if ApiVault.ResourceLinks and ApiVault.ResourceLinks.AppendModuleDetail then
		text = ApiVault.ResourceLinks:AppendModuleDetail(text, entry, 5)
	end
	self.detailBox:SetText(text)
	self.detailBox:SetCursorPosition(0)
end
