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

local CharacterEquipment = {}
SlickUI:RegisterModule("CharacterEquipment", CharacterEquipment)

local SLOT_DEFINITIONS = {
	{ id = 1, button = "CharacterHeadSlot", side = "LEFT", enchant = true },
	{ id = 2, button = "CharacterNeckSlot", side = "LEFT" },
	{ id = 3, button = "CharacterShoulderSlot", side = "LEFT", enchant = true },
	{ id = 15, button = "CharacterBackSlot", side = "LEFT" },
	{ id = 5, button = "CharacterChestSlot", side = "LEFT", enchant = true },
	{ id = 4, button = "CharacterShirtSlot", side = "LEFT" },
	{ id = 19, button = "CharacterTabardSlot", side = "LEFT" },
	{ id = 9, button = "CharacterWristSlot", side = "LEFT" },
	{ id = 10, button = "CharacterHandsSlot", side = "RIGHT" },
	{ id = 6, button = "CharacterWaistSlot", side = "RIGHT" },
	{ id = 7, button = "CharacterLegsSlot", side = "RIGHT", enchant = true },
	{ id = 8, button = "CharacterFeetSlot", side = "RIGHT", enchant = true },
	{ id = 11, button = "CharacterFinger0Slot", side = "RIGHT", enchant = true },
	{ id = 12, button = "CharacterFinger1Slot", side = "RIGHT", enchant = true },
	{ id = 13, button = "CharacterTrinket0Slot", side = "RIGHT" },
	{ id = 14, button = "CharacterTrinket1Slot", side = "RIGHT" },
	{ id = 16, button = "CharacterMainHandSlot", side = "BOTTOM", enchant = true },
	{ id = 17, button = "CharacterSecondaryHandSlot", side = "BOTTOM", offHand = true },
}

local OFF_HAND_WEAPON_LOCATIONS = {
	INVTYPE_WEAPON = true,
	INVTYPE_WEAPONOFFHAND = true,
	INVTYPE_2HWEAPON = true,
}

local scanTooltip
local slotsCreated = false
local paperDollHooked = false
local refreshQueued = false
local controlsCreated = false

local function CreateDisplayCheckBox(name, label)
	local checkBox = CreateFrame("CheckButton", name, PaperDollFrame, "UICheckButtonTemplate")
	checkBox:SetSize(122, 22)
	checkBox.Label = Theme:CreateText(checkBox, 10, "OVERLAY")
	checkBox.Label:SetPoint("LEFT", checkBox, "LEFT", 25, 0)
	checkBox.Label:SetText(label)
	checkBox.Label:SetTextColor(1, 0.82, 0.16, 1)
	return checkBox
end

local function GetScanTooltip()
	if scanTooltip then
		return scanTooltip
	end

	scanTooltip = CreateFrame("GameTooltip", "SlickUICharacterEquipmentScanTooltip", UIParent, "GameTooltipTemplate")
	scanTooltip:SetOwner(UIParent, "ANCHOR_NONE")
	return scanTooltip
end

local function GetEnchantID(itemLink)
	local itemString = itemLink and itemLink:match("|H(item:[^|]+)|h")
	if not itemString then
		return 0
	end

	local _, _, enchantID = strsplit(":", itemString)
	return tonumber(enchantID) or 0
end

local function GetEnchantName(itemLink)
	if GetEnchantID(itemLink) == 0 then
		return nil
	end

	local tooltip = GetScanTooltip()
	tooltip:ClearLines()
	tooltip:SetHyperlink(itemLink)

	local pattern = type(ENCHANTED_TOOLTIP_LINE) == "string"
		and ENCHANTED_TOOLTIP_LINE:gsub("%%s", "(.+)")
		or nil
	for lineIndex = 2, tooltip:NumLines() do
		local line = _G[tooltip:GetName() .. "TextLeft" .. lineIndex]
		local text = line and line:GetText()
		if type(text) == "string" and pattern then
			local enchantName = text:match(pattern)
			if enchantName and enchantName ~= "" then
				return enchantName
			end
		end
	end

	return "Enchanted"
end

local function GetItemLevel(itemLink)
	if not itemLink then
		return nil
	end

	if C_Item and type(C_Item.GetDetailedItemLevelInfo) == "function" then
		return C_Item.GetDetailedItemLevelInfo(itemLink)
	end
	if type(GetDetailedItemLevelInfo) == "function" then
		return GetDetailedItemLevelInfo(itemLink)
	end

	local _, _, _, itemLevel = GetItemInfo(itemLink)
	return itemLevel
end

local function OffHandCanBeEnchanted(itemLink)
	if not itemLink then
		return false
	end

	local _, _, _, equipLocation, _, classID, subClassID = C_Item.GetItemInfoInstant(itemLink)
	if classID ~= Enum.ItemClass.Weapon or not OFF_HAND_WEAPON_LOCATIONS[equipLocation] then
		return false
	end
	if Enum.ItemWeaponSubclass and subClassID == Enum.ItemWeaponSubclass.Fishingpole then
		return false
	end
	return true
end

local function CreateSlotOverlay(definition)
	local button = _G[definition.button]
	if not button or button.SlickUIEquipmentOverlay then
		return button ~= nil
	end

	local overlay = CreateFrame("Frame", nil, button)
	overlay:SetAllPoints(button)
	overlay:SetFrameLevel(button:GetFrameLevel() + 8)
	overlay:EnableMouse(false)

	local itemLevel = Theme:CreateText(overlay, 13, "OVERLAY")
	itemLevel:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 2)
	itemLevel:SetJustifyH("RIGHT")
	itemLevel:SetTextColor(1, 0.84, 0.18, 1)
	itemLevel:SetDrawLayer("OVERLAY", 7)

	local enchant = Theme:CreateText(overlay, 10, "OVERLAY")
	enchant:SetWidth(150)
	enchant:SetWordWrap(false)
	if definition.side == "LEFT" then
		enchant:SetPoint("LEFT", button, "RIGHT", 7, 0)
		enchant:SetJustifyH("LEFT")
	elseif definition.side == "RIGHT" then
		enchant:SetPoint("RIGHT", button, "LEFT", -7, 0)
		enchant:SetJustifyH("RIGHT")
	else
		enchant:SetPoint("BOTTOM", button, "TOP", 0, 5)
		enchant:SetJustifyH("CENTER")
	end

	button.SlickUIEquipmentOverlay = overlay
	button.SlickUIItemLevel = itemLevel
	button.SlickUIEnchantStatus = enchant
	return true
end

function CharacterEquipment:CreateControls()
	if controlsCreated or not PaperDollFrame or not CharacterHeadSlot then
		return controlsCreated
	end

	local itemLevels = CreateDisplayCheckBox("SlickUIShowCharacterItemLevels", "Show item levels")
	itemLevels:SetPoint("BOTTOMLEFT", CharacterHeadSlot, "TOPLEFT", -3, 6)
	itemLevels:SetScript("OnClick", function(self)
		SlickUI.DB.characterEquipment.showItemLevels = self:GetChecked() == true
		CharacterEquipment:Refresh()
	end)

	local enchants = CreateDisplayCheckBox("SlickUIShowCharacterEnchants", "Show enchants")
	enchants:SetPoint("LEFT", itemLevels, "RIGHT", 3, 0)
	enchants:SetScript("OnClick", function(self)
		SlickUI.DB.characterEquipment.showEnchants = self:GetChecked() == true
		CharacterEquipment:Refresh()
	end)

	self.ItemLevelsCheckBox = itemLevels
	self.EnchantsCheckBox = enchants
	controlsCreated = true
	return true
end

function CharacterEquipment:CreateOverlays()
	if slotsCreated then
		return true
	end

	local found = 0
	for _, definition in ipairs(SLOT_DEFINITIONS) do
		if CreateSlotOverlay(definition) then
			found = found + 1
		end
	end
	slotsCreated = found == #SLOT_DEFINITIONS
	self:CreateControls()

	if PaperDollFrame and not paperDollHooked then
		PaperDollFrame:HookScript("OnShow", function()
			CharacterEquipment:QueueRefresh()
		end)
		paperDollHooked = true
	end

	return slotsCreated
end

function CharacterEquipment:Refresh()
	refreshQueued = false
	if not self:CreateOverlays() then
		return
	end

	local settings = SlickUI.DB.characterEquipment
	if self.ItemLevelsCheckBox then
		self.ItemLevelsCheckBox:SetChecked(settings.showItemLevels == true)
	end
	if self.EnchantsCheckBox then
		self.EnchantsCheckBox:SetChecked(settings.showEnchants == true)
	end

	for _, definition in ipairs(SLOT_DEFINITIONS) do
		local button = _G[definition.button]
		local itemLevelText = button and button.SlickUIItemLevel
		local enchantText = button and button.SlickUIEnchantStatus
		local itemLink = GetInventoryItemLink("player", definition.id)

		if itemLevelText then
			itemLevelText:SetShown(settings.showItemLevels == true)
			local itemLevel = GetItemLevel(itemLink)
			itemLevelText:SetText(itemLevel and tostring(itemLevel) or "")
		end

		if enchantText then
			enchantText:SetShown(settings.showEnchants == true)
			enchantText:SetText("")
			local enchantable = definition.enchant == true
				or (definition.offHand and OffHandCanBeEnchanted(itemLink))
			if itemLink and enchantable then
				local enchantName = GetEnchantName(itemLink)
				if enchantName then
					enchantText:SetText(enchantName)
					enchantText:SetTextColor(0.20, 1.00, 0.34, 1)
				else
					enchantText:SetText("MISSING ENCHANT")
					enchantText:SetTextColor(1.00, 0.18, 0.12, 1)
				end
			end
		end
	end
end

function CharacterEquipment:QueueRefresh()
	if refreshQueued then
		return
	end
	refreshQueued = true
	C_Timer.After(0, function()
		CharacterEquipment:Refresh()
	end)
	C_Timer.After(0.35, function()
		CharacterEquipment:Refresh()
	end)
end

function CharacterEquipment:Initialize()
	local eventFrame = CreateFrame("Frame")
	eventFrame:RegisterEvent("ADDON_LOADED")
	eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
	eventFrame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
	eventFrame:RegisterEvent("UNIT_INVENTORY_CHANGED")
	eventFrame:RegisterEvent("GET_ITEM_INFO_RECEIVED")
	eventFrame:SetScript("OnEvent", function(_, event, arg1)
		if event == "UNIT_INVENTORY_CHANGED" and arg1 ~= "player" then
			return
		end
		CharacterEquipment:QueueRefresh()
	end)

	self.EventFrame = eventFrame
	self:QueueRefresh()
end
