GemAtelier = {}
GemAtelier.__index = GemAtelier

local lockedOnly = false
local sortQuality = 1
local sortAffinity = 1
local currentPage = 1

local destroyGemWindow = nil
local lastSelectedGem = nil
local lastSelectedVessel = nil
local currentGemList = {}
local totalGemList = {}
local currentSearchText = ""

local cachedBasicMods = {}
local cachedSupremeMods = {}

GemRevealPrice = {
  [0] = 125000,  -- Lesser
  [1] = 1000000,  -- Regular
  [2] = 6000000  -- Greater
}

GemSwitchPrice = {
  [0] = 125000,   -- Lesser
  [1] = 250000,   -- Regular
  [2] = 1000000   -- Greater
}

function GemAtelier.resetFields()
	lockedOnly = false
	sortQuality = 1
	sortAffinity = 1
	destroyGemWindow = nil
	lastSelectedGem = nil
	currentGemList = {}
	currentSearchText = ""
	totalGemList = {}
	currentPage = 1
	gemAtelierWindow:recursiveGetChildById("filterPanel").searchText:clearText()
	gemAtelierWindow:recursiveGetChildById("affinitiesBox"):setCurrentIndex(1, true)
	gemAtelierWindow:recursiveGetChildById("qualitiesBox"):setCurrentIndex(1, true)
	gemAtelierWindow:recursiveGetChildById("lockedOnly"):setChecked(false, true)
	if lastSelectedVessel then
		lastSelectedVessel:setVisible(false)
		lastSelectedVessel = nil
	end

	cachedBasicMods = {}
	cachedSupremeMods = {}
	for _, data in pairs(Workshop.getFragmentList()) do
		if data.supreme then
			cachedSupremeMods[data.modID] = data
		else
			cachedBasicMods[data.modID] = data
		end
	end
end

function GemAtelier.redirectToGem(gemData)
	if not WheelOfDestiny or not gemAtelierWindow then
		return true
	end

	GemAtelier.resetFields()
	GemAtelier.setupVesselPanel()
	local gemList = gemAtelierWindow:recursiveGetChildById("gemContent")
	if not gemList then
		return true
	end

	if gemData then
		local affinityWidget = gemAtelierWindow:recursiveGetChildById("affinitiesBox")
		local qualityWidget = gemAtelierWindow:recursiveGetChildById("qualitiesBox")
		affinityWidget:setCurrentIndex(gemData.gemDomain + 2, true)
		qualityWidget:setCurrentIndex(1, true)
		sortQuality = 1
		sortAffinity = gemData.gemDomain + 2

		local highLight = gemAtelierWindow:recursiveGetChildById("selectVessel" .. gemData.gemDomain)
		highLight:setVisible(true)

		if lastSelectedVessel then
			lastSelectedVessel:setVisible(false)
		end

		lastSelectedVessel = highLight
	end

	gemList:destroyChildren()
	
	totalGemList = {}
	currentGemList = {}

	local index = 1
	local foundIndex = 1

	for i, data in pairs(WheelOfDestiny.atelierGems) do
		repeat
		if (sortQuality > 1 and data.gemType ~= sortQuality - 2) or (sortAffinity > 1 and data.gemDomain ~= sortAffinity - 2) then
		 	break
	 	end

		if gemData.gemID == data.gemID then
			currentPage = math.ceil(index / 15)
			foundIndex = math.max(1, index - 15)
		end

		index = index + 1
		table.insert(totalGemList, data)
		until true
	end

	local gemCount = 0
	local beginList = (currentPage - 1) * 15 + 1
	local focusedGem = nil

	for i, data in pairs(totalGemList) do
		local __brk = false
		repeat
		if gemCount == 15 then
			do __brk = true break end
		end

		if i < beginList then
			break
		end

		local widget = g_ui.createWidget('GemPanel', gemList)
		local success = GemAtelier.setupGemWidget(widget, data)
			
		if success then
			currentGemList[#currentGemList + 1] = data
			if widget then
				widget.gemIndex = #currentGemList
			end
			gemCount = gemCount + 1

			if data.gemID == gemData.gemID then
				focusedGem = widget
			end
		else
			widget:destroy()
		end
		
		until true
		if __brk then break end
	end

	GemAtelier.showGemRevelation()
	GemAtelier.configurePages()

	gemList.onChildFocusChange = function(self, selected) GemAtelier.onSelectGem(selected, true) end
	
	if not focusedGem then
		focusedGem = gemList:getFirstChild()
	end
	
	gemList:focusChild(focusedGem, ActiveFocusReason, true)
	GemAtelier.onSelectGem(focusedGem, true)
end

function GemAtelier.showGems(selectFirst, lastIndex)
	if not WheelOfDestiny or not gemAtelierWindow then
		return true
	end

	GemAtelier.setupVesselPanel()
	local gemList = gemAtelierWindow:recursiveGetChildById("gemContent")
	if not gemList then
		return true
	end

	totalGemList = {}
	currentGemList = {}
	
	
	for i, data in pairs(WheelOfDestiny.atelierGems) do
		repeat
		if not data.gemID or data.gemID < 0 then
			g_logger.debug(string.format("[GemAtelier] Skipping gem with invalid ID: %s", tostring(data.gemID)))
			break
		end

		local isLocked = data.locked == 1 or data.locked == true
		if lockedOnly and not isLocked then
			break
		elseif sortQuality > 1 and data.gemType ~= sortQuality - 2 then
			break
		elseif sortAffinity > 1 and data.gemDomain ~= sortAffinity - 2 then
			break
		elseif #currentSearchText > 0 and not GemAtelier.matchGemText(data) then
			break
		end

		table.insert(totalGemList, data)
		until true
	end

	gemList:destroyChildren()
	gemList.onChildFocusChange = function(self, selected) GemAtelier.onSelectGem(selected, true) end

	local gemCount = 0
	local beginList = (currentPage - 1) * 15 + 1

	for i, data in pairs(totalGemList) do
		local __brk = false
		repeat
		if gemCount == 15 then
			do __brk = true break end
		end

		if i < beginList then
			break
		end

		local widget = g_ui.createWidget('GemPanel', gemList)
		local success = GemAtelier.setupGemWidget(widget, data)

		if success then
			currentGemList[#currentGemList + 1] = data
			if widget then
				widget.gemIndex = #currentGemList
			end
			gemCount = gemCount + 1
		else
			widget:destroy()
		end

		until true
		if __brk then break end
	end

	GemAtelier.showGemRevelation()
	GemAtelier.configurePages()

	local panel = gemAtelierWindow:recursiveGetChildById("clickedPanel")
	local children = gemList:getChildren()

	if #children == 0 then
		panel.clickedContent:setVisible(false)
		panel.cleanContent:setVisible(true)
	else
		gemList:focusChild(nil)
		panel.cleanContent:setVisible(false)
		if selectFirst then
			gemList:focusChild(gemList:getFirstChild())
		elseif lastIndex then
			gemList:focusChild(children[lastIndex])
		-- No llamar métodos sobre lastSelectedGem: puede ser un widget ya destruido por el
		-- propio redraw (destroyChildren) y el método lanza error; leer .gemID sí es seguro.
		elseif lastSelectedGem and lastSelectedGem.gemID then
			local targetIndex = 0
			for i, widget in ipairs(children) do
				if widget.gemID == lastSelectedGem.gemID then
					targetIndex = i
					break
				end
			end
		
			if targetIndex > 0 then
				gemList:focusChild(children[targetIndex])
			else
				g_logger.debug(string.format("[GemAtelier] gemID %d not found among displayed children.", lastSelectedGem.gemID or -1))
				if #children > 0 then
					gemList:focusChild(children[1])
				end
			end
		else
			if #children > 0 then
				gemList:focusChild(children[1])
			end
		end
	end
end

function GemAtelier.matchGemText(data)
	local descriptions = {RegularGemDescription[data.lesserBonus].text, cachedBasicMods[data.lesserBonus].tooltip}

	if data.gemType > 0 then
		table.insert(descriptions, RegularGemDescription[data.regularBonus].text)
		table.insert(descriptions, cachedBasicMods[data.regularBonus].tooltip)
	end
	if data.gemType > 1 then
		table.insert(descriptions, SupremeGemDescription[data.supremeBonus].text)
		table.insert(descriptions, cachedSupremeMods[data.supremeBonus].tooltip)
	end

	for _, text in pairs(descriptions) do
		if matchText(currentSearchText, text) then
			return true
		end
	end
	return false
end

function GemAtelier.setupGemWidget(widget, data)
	if not widget then
	  g_logger.debug("[GemAtelier] widget nil — could not configure gem")
	  return false
	end
  
	if data and data.gemID and data.gemID >= 0 then
		widget.gemID = data.gemID
		g_logger.debug(string.format("[GemAtelier] setupGemWidget: set gemID=%d for widget", data.gemID))
	else
		widget.gemID = -1
		g_logger.debug(string.format("[GemAtelier] gem without valid gemID — gemID=%s (type=%s)", tostring(data and data.gemID), type(data and data.gemID)))
		return false
	end
  
	if not data then
	  g_logger.debug("[GemAtelier] setupGemWidget called with data=nil")
	  return false
	end
	if not data.gemType or not data.gemDomain then
	  g_logger.debug(string.format("[GemAtelier] incomplete gem data: id=%s type=%s domain=%s",
		tostring(data.gemID), tostring(data.gemType), tostring(data.gemDomain)))
	  return false
	end
  
	local typeOffset = data.gemType * 32
	local domainOffset = data.gemDomain * 96
	local vocationOffset = (WheelOfDestiny.vocationId - 1) * 384
	local gemOffset = vocationOffset + domainOffset + typeOffset
  
	local tmpData = GemVocations[WheelOfDestiny.vocationId][data.gemType]
	if not tmpData then
	  g_logger.debug(string.format("[GemAtelier] gem id %d not found in GemVocations[%d][%d]",
		data.gemID or -1, WheelOfDestiny.vocationId or -1, data.gemType or -1))
	  return false
	end
  
	local lockedState = data.locked == 1
	widget.locker:setChecked(lockedState)
	widget.locker.onClick = GemAtelier.onLockGem
	widget.locker.gemID = data.gemID

	g_logger.debug(string.format(
	"[GemAtelier] Locker configured for gemID=%d | locked=%d | checked=%s | visible=%s",
	data.gemID or -1,
	data.locked or -1,
	tostring(widget.locker:isChecked()),
	tostring(widget.locker:isVisible())
	))
	widget.gemRevelationItem:setImageClip(gemOffset .. " 0 32 32")
	widget.gemRevelationItem:setTooltip(tmpData.name:gsub(" %(x 0%)", ""))
  
	if GemAtelier.isGemEquipped(data.gemID) then
	  widget.gemDomainImage:setVisible(true)
	  widget.gemDomainImage:setImageClip(data.gemDomain * 26 .. " 0 26 26")
	end
  
	local gemTypeWidget = widget:recursiveGetChildById("modType" .. data.gemType)
	if not gemTypeWidget then
	  g_logger.debug(string.format("[GemAtelier] gemTypeWidget modType%d not found for gemID=%d", data.gemType, data.gemID))
	  return false
	end
  
	gemTypeWidget:setVisible(true)
  
	GemAtelier.setupGemSlot(gemTypeWidget.fragmentType0.gemMod0, data.lesserBonus, WheelOfDestiny.basicModsUpgrade, false, data, 0)
	GemAtelier.setGemUpgradeImage(gemTypeWidget.fragmentType0, data.lesserBonus, WheelOfDestiny.basicModsUpgrade, nil)
  
	if data.gemType > 0 then
	  GemAtelier.setupGemSlot(gemTypeWidget.fragmentType1.gemMod1, data.regularBonus, WheelOfDestiny.basicModsUpgrade, false, data, 1)
	  GemAtelier.setGemUpgradeImage(gemTypeWidget.fragmentType1, data.regularBonus, WheelOfDestiny.basicModsUpgrade, WheelOfDestiny.basicModsUpgrade[data.lesserBonus] or 0, true)
	end
  
	if data.gemType > 1 then
	  GemAtelier.setupGemSlot(gemTypeWidget.fragmentType2.gemMod2, data.supremeBonus, WheelOfDestiny.supremeModsUpgrade, true, data, 2)
	  local effectiveBonus = math.min(WheelOfDestiny.basicModsUpgrade[data.lesserBonus] or 0, WheelOfDestiny.basicModsUpgrade[data.regularBonus] or 0)
	  GemAtelier.setGemUpgradeImage(gemTypeWidget.fragmentType2, data.supremeBonus, WheelOfDestiny.supremeModsUpgrade, effectiveBonus)
	end
  
g_logger.debug(string.format("[GemAtelier] GemWidget configured: id=%d type=%d domain=%d locked=%d",
	  data.gemID or -1, data.gemType or -1, data.gemDomain or -1, data.locked or -1))
	
	return true
  end
  

function GemAtelier.setupGemSlot(gemSlot, bonus, upgradeData, isSupreme, gemData, gemPosition)
	gemSlot:setImageClip(bonus * (isSupreme and 35 or 30) .. " 0 " .. (isSupreme and 35 or 30) .. " " .. (isSupreme and 35 or 30))
	GemAtelier.createGemInformation(gemSlot, bonus, isSupreme, true, gemData, gemPosition)
end

function GemAtelier.setGemUpgradeImage(gemFragment, bonus, upgradeData, prevBonus, debug)
	local upgradeLevel = upgradeData[bonus] or 0
	if prevBonus then
		if upgradeLevel > prevBonus then
			gemFragment.potential:setVisible(true)
			gemFragment.potential:setImageClip(upgradeLevel * 50 .. " 0 50 50")
			gemFragment:setImageClip(prevBonus * 50 .. " 0 50 50")
		else
			gemFragment:setImageClip(upgradeLevel * 50 .. " 0 50 50")
		end
	else
		gemFragment:setImageClip(upgradeLevel * 50 .. " 0 50 50")
	end
end

function GemAtelier.showGemRevelation()
	local data = GemVocations[WheelOfDestiny.vocationId]
	if not data then
		return true
	end

	local player = g_game:getLocalPlayer()
	local revelation = gemAtelierWindow.gemRevelation
	local totalBalance = player:getResourceBalance(ResourceTypes.BANK_BALANCE) + player:getResourceBalance(ResourceTypes.GOLD_EQUIPPED)
	local resources = {
		[0] = player:getResourceBalance(ResourceTypes.LESSER_GEMS),
		[1] = player:getResourceBalance(ResourceTypes.REGULAR_GEMS),
		[2] = player:getResourceBalance(ResourceTypes.GREATER_GEMS)
	}

	for i = 0, 2 do
		local itemWidget = revelation:recursiveGetChildById("gemRevelationItem" .. i)
		local gemInfo = revelation:recursiveGetChildById("gemInfo" .. i)
		local revealCost = revelation:recursiveGetChildById("gemRevealCost" .. i)
		local button = revelation:recursiveGetChildById("revealButton" .. i)

		itemWidget:setItemId(data[i].id)
		gemInfo:setText(data[i].name:gsub("%d", resources[i]))
		gemInfo:setMarginTop(60)
		if not gemInfo:isTextWrap() then
			gemInfo:setMarginTop(67)
		end

		revealCost.gold:setText(comma_value(GemRevealPrice[i] / 1000) .. "k")

		local toolTip = ""
		button:setOn(true)
		button:setTooltip("")
		if totalBalance < GemRevealPrice[i] then
			toolTip = tr(GemStaticTooltips[0], comma_value(GemRevealPrice[i]))
		end

		if resources[i] < 1 then
			toolTip = tr("%s" .. GemStaticTooltips[1], (#toolTip > 0 and toolTip .. "\n" or ""), comma_value(GemRevealPrice[i]))
		end

		if WheelOfDestiny.changeState ~= 1 then
			toolTip = tr("%s%s", (#toolTip > 0 and toolTip .. "\n" or ""), GemStaticTooltips[2])
		end

		if #WheelOfDestiny.atelierGems >= 225 then
			toolTip = tr("%s%s", (#toolTip > 0 and toolTip .. "\n" or ""), GemStaticTooltips[3])
		end

		if #toolTip > 0 then
			button:setTooltip(toolTip)
			button:setOn(false)
		end
	end
end

function GemAtelier.configurePages()
	if not gemAtelierWindow then
		return true
	end

	local panel = gemAtelierWindow:recursiveGetChildById("filterPanel")
	local totalCount = #totalGemList
	local maxPage = math.max(1, math.ceil(totalCount / 15))
	panel.pagesPanel.pagesLabel:setText(tr('Page %s / %s (%s Gems)', currentPage, maxPage, totalCount))

	if currentPage == 1 and maxPage == 1  then
		panel.pagesPanel.leftArrow:setOn(false)
		panel.pagesPanel.rightArrow:setOn(false)
	end

	if currentPage == 1 and maxPage > 1 then
		panel.pagesPanel.leftArrow:setOn(false)
		panel.pagesPanel.rightArrow:setOn(true)
	end

	if currentPage > 1 and currentPage < maxPage then
		panel.pagesPanel.leftArrow:setOn(true)
		panel.pagesPanel.rightArrow:setOn(true)
	end

	if currentPage > 1 and currentPage == maxPage then
		panel.pagesPanel.leftArrow:setOn(true)
		panel.pagesPanel.rightArrow:setOn(false)
	end
end

function GemAtelier.managePage(button, foward)
	if not button:isOn() then
		return true
	end

	local totalCount = #WheelOfDestiny.atelierGems
	local maxPage = math.max(1, math.ceil(totalCount / 15))

	if foward then
		currentPage = math.min(currentPage + 1, math.ceil(maxPage))
	else
		currentPage = math.max(1, currentPage - 1)
	end
	GemAtelier.showGems(true)
end

function shortenAfterCooldown(text)
    local cooldownIndex = text:find("Cooldown")
    if cooldownIndex then
        local afterCooldownIndex = cooldownIndex + #"Cooldown" - 1
        if text:sub(afterCooldownIndex + 1):match("%S") then
            return text:sub(1, afterCooldownIndex) .. "…"
        else
            return text
        end
    else
        return text
    end
end

function GemAtelier.getEffectiveLevel(gemData, currentBonusID, supreme, gemSlot)
	local basicUpgrade = WheelOfDestiny.basicModsUpgrade
	local supremeUpgrade = WheelOfDestiny.supremeModsUpgrade
	local upgradeTier = supreme and (supremeUpgrade[currentBonusID] or 0) or (basicUpgrade[currentBonusID] or 0)

	if gemSlot == 0 then
		return upgradeTier
	elseif gemSlot == 1 then
		local lesserUpgradeTier = basicUpgrade[gemData.lesserBonus] or 0
		return math.min(upgradeTier, lesserUpgradeTier)
	elseif gemSlot == 2 then
		local lesserUpgradeTier = basicUpgrade[gemData.lesserBonus] or 0
		local regularUpgradeTier = basicUpgrade[gemData.regularBonus] or 0
		local effectiveTier = math.min(lesserUpgradeTier, regularUpgradeTier)
		return math.min(upgradeTier, effectiveTier)
	end
end

function GemAtelier.createGemInformation(widget, gemTypeID, supremeMod, tooltip, gemData, gemSlot)
	local search = nil
	if supremeMod then
		search = cachedSupremeMods[gemTypeID]
	else
		search = cachedBasicMods[gemTypeID]
	end

	if not search then
		return true
	end

	local function shortenAfterCooldown(text)
		local cooldownIndex = text:find("Cooldown")
		if cooldownIndex then
			local afterCooldownIndex = cooldownIndex + #"Cooldown" - 1
			if text:sub(afterCooldownIndex + 1):match("%S") then
				return text:sub(1, afterCooldownIndex) .. "...", true
			else
				return text, false
			end
		else
			return text, false
		end
	end

	local shorted = false
	local currentTier = GemAtelier.getEffectiveLevel(gemData, gemTypeID, supremeMod, gemSlot)
	local text = Workshop.getBonusDescription(search, currentTier)
	if tooltip then
		widget:setTooltip(text)
	else
		local originalText = text
		text, shorted = shortenAfterCooldown(text)
		widget:setTooltip(shorted and originalText or "")
		widget:setText(text)
	end
end

function GemAtelier.onSelectGem(selected, clicked)
	if not selected or not selected.gemID then
		return true
	end
	
	g_logger.debug(string.format("[GemAtelier] onSelectGem: gemID=%d currentGemList size=%d", selected.gemID, #currentGemList))

	if #currentGemList == 0 then
		return true
	end

	local gemData = GemAtelier.getGemDataById(selected.gemID)
	if not gemData then
		g_logger.debug(string.format(
			"[GemAtelier] gemData is nil for gemID=%s (currentGemList size=%d)",
			tostring(selected.gemID), #currentGemList
		))
		return true
	end

	if lastSelectedGem then
		lastSelectedGem:setBorderWidth(0)
		lastSelectedGem:setBorderColor('alpha')
	end
	lastSelectedGem = selected
	lastSelectedGem:setBorderWidth(2)
	lastSelectedGem:setBorderColor('white')

	local panel = gemAtelierWindow:recursiveGetChildById("clickedPanel")
	if panel.cleanContent:isVisible() then
		panel.cleanContent:setVisible(false)
	end
	panel.clickedContent:setVisible(true)

	panel.clickedContent.placeVessel.onClick  = function() GemAtelier.manageVessel(false) end
	panel.clickedContent.removeVessel.onClick = function() GemAtelier.manageVessel(true)  end
	panel.clickedContent.switch.onClick       = GemAtelier.onSwitchDomain
	panel.clickedContent.destroy.onClick      = GemAtelier.onDestroyGem

	local typeOffset    = gemData.gemType * 64
	local domainOffset  = gemData.gemDomain * 192
	local vocationOffset = (WheelOfDestiny.vocationId - 1) * 64
	local gemOffset     = domainOffset + typeOffset

	local gemText = GemVocations[WheelOfDestiny.vocationId][gemData.gemType].name
	panel.clickedContent.gemDetails.gemName:setText(string.gsub(gemText, " %(x 0%)", ""))
	panel.clickedContent.gemDetails.gemDetailItem:setImageClip(gemOffset .. " " .. vocationOffset .. " 64 64")
	panel.clickedContent.gemDetails.domain:setImageClip(gemData.gemDomain * 26 .. " 0 26 26")

	local widgetMods = panel.clickedContent.gemMods
	for i = 0, 2 do
		widgetMods:recursiveGetChildById("fragmentType" .. i):setVisible(false)
		widgetMods:recursiveGetChildById("gemModItem" .. i):setVisible(false)
		widgetMods:recursiveGetChildById("modLabel" .. i):setVisible(false)
	end
	widgetMods.fragmentType0.gemModItem0:setImageClip(gemData.lesserBonus * 30 .. " 0 30 30")
	GemAtelier.setupModAvailable(widgetMods, 0, 1, gemData)
	GemAtelier.createGemInformation(widgetMods.modLabel0, gemData.lesserBonus, false, false, gemData, 0)
	if gemData.gemType > 0 then
		widgetMods.fragmentType1.gemModItem1:setImageClip(gemData.regularBonus * 30 .. " 0 30 30")
		GemAtelier.setupModAvailable(widgetMods, 1, 2, gemData)
		GemAtelier.createGemInformation(widgetMods.modLabel1, gemData.regularBonus, false, false, gemData, 1)
	end
	if gemData.gemType > 1 then
		widgetMods.fragmentType2.gemModItem2:setImageClip(gemData.supremeBonus * 35 .. " 0 35 35")
		GemAtelier.setupModAvailable(widgetMods, 2, 3, gemData)
		GemAtelier.createGemInformation(widgetMods.modLabel2, gemData.supremeBonus, true, false, gemData, 2)
	end

	local player = g_game:getLocalPlayer()
	local totalBalance = player:getResourceBalance(ResourceTypes.BANK_BALANCE)
	+ player:getResourceBalance(ResourceTypes.GOLD_EQUIPPED)

	local price = GemSwitchPrice[gemData.gemType] or 0
	local enough = totalBalance >= price
	local goldWidget = panel.clickedContent.switchCost.gold

	goldWidget:setText((price / 1000) .. "k")
	goldWidget:setColor(enough and "#c0c0c0" or "#d33c3c")
	panel.clickedContent.switchCost:setTooltip(comma_value(price))

	local alreadyEquipped = false
	local isTheSameGem = false
	for _, id in pairs(WheelOfDestiny.equipedGems or {}) do
		if type(id) == "number" and id > 0 then
			local dom = GemAtelier.getGemDomainById(id)
			if dom == gemData.gemDomain then
				alreadyEquipped = true
				if id == gemData.gemID then
					isTheSameGem = true
				end
				break
			end
		end
	end

	-- decide button visibility
	-- If selected gem is the same as equipped, show "Remove from Vessel"
	-- If it's a different gem but same color, show "Place in Vessel" (will replace)
	-- If no gem is equipped, show "Place in Vessel"
	if alreadyEquipped and isTheSameGem then
		panel.clickedContent.placeVessel:setVisible(false)
		panel.clickedContent.removeVessel:setVisible(true)
	else
		panel.clickedContent.placeVessel:setVisible(true)
		panel.clickedContent.removeVessel:setVisible(false)
	end


	local switchTip, destroyTip = "", ""
	local canInteract = (WheelOfDestiny.changeState == 1) and (gemData.locked == 0)
	panel.clickedContent.switch:setOn(canInteract)
	panel.clickedContent.destroy:setOn(canInteract)
	g_logger.debug(string.format(
		"[GemAtelier] Buttons updated -> changeState=%d locked=%d canInteract=%s",
		WheelOfDestiny.changeState or -1, gemData.locked or -1, tostring(canInteract)
	))

	local gemCount = GemAtelier.getGemCountByDomain(gemData.gemDomain)
	if gemCount < 2 then
		switchTip  = tr("%s%sYou cannot switch the last gem of the domain.", switchTip,  (#switchTip  > 0 and "\n" or ""))
		destroyTip = tr("%s%sYou cannot destroy the last gem of the domain.", destroyTip,(#destroyTip > 0 and "\n" or ""))
	end
	if totalBalance < price then
		switchTip = tr("%s%sYou need at least %s gold to change the domain of this gem.", switchTip, (#switchTip > 0 and "\n" or ""), comma_value(price))
	end
	if gemData.locked == 1 then
		switchTip  = tr("%s%sBefore you can change the domain of this gem, you must unlock it.", switchTip,  (#switchTip  > 0 and "\n" or ""))
		destroyTip = tr("%s%sBefore you can destroy the gem, you must unlock it.", destroyTip,(#destroyTip > 0 and "\n" or ""))
	end
	if GemAtelier.isGemEquipped(gemData.gemID) then
		switchTip  = tr("%s%sYou must remove the gem from its vessel before you can switch its domain.", switchTip,  (#switchTip  > 0 and "\n" or ""))
		destroyTip = tr("%s%sThe gem must be removed from its vessel before it can be destroyed.", destroyTip,(#destroyTip > 0 and "\n" or ""))
	end

	panel.clickedContent.switch:setTooltip(switchTip)
	panel.clickedContent.destroy:setTooltip(destroyTip)
	if #switchTip  > 0 then panel.clickedContent.switch:setOn(false)  end
	if #destroyTip > 0 then panel.clickedContent.destroy:setOn(false) end
	if totalBalance >= price then
		panel.clickedContent.switch:setTooltip(tr("%s%sSwitch the gem\'s domain one step clockwise by paying the free of %s gold.", switchTip, (#switchTip > 0 and "\n" or ""), comma_value(price)))
	end
end


function GemAtelier.isVesselAvailable(domain, count)
	local vesselFilled = 0
	local domainIndex = VesselIndex[domain]
	if not domainIndex then
		return false
	end

	for _, index in pairs(domainIndex) do
		local bonus = WheelBonus[index]
		local currentPoints = WheelOfDestiny.pointInvested[index + 1]
		if currentPoints >= bonus.maxPoints then
			vesselFilled = vesselFilled + 1
		end
	end
	return vesselFilled >= count
end

function GemAtelier.getFilledVesselCount(domain)
	local vesselFilled = 0
	local domainIndex = VesselIndex[domain]
	for _, index in pairs(domainIndex) do
		local bonus = WheelBonus[index]
		local currentPoints = WheelOfDestiny.pointInvested[index + 1]
		if bonus and currentPoints and currentPoints >= bonus.maxPoints then
			vesselFilled = vesselFilled + 1
		end
	end
	return vesselFilled
end

function GemAtelier.setupModAvailable(widget, gemType, vesselCount, gemData)
	if not widget then
		return true
	end

	local gemDomain = gemData.gemDomain
	local fragmentType = widget:recursiveGetChildById("fragmentType" .. gemType)
	local modItem = widget:recursiveGetChildById("gemModItem" .. gemType)
	local modLabel = widget:recursiveGetChildById("modLabel" .. gemType)
	local potentialLevel = fragmentType:recursiveGetChildById("potential")

	fragmentType:setVisible(true)
	modItem:setVisible(true)
	modLabel:setVisible(true)

	if GemAtelier.isVesselAvailable(gemDomain, vesselCount) then
		fragmentType:setShader("")
		modItem:setShader("")
		modLabel:setColor("#c0c0c0")
	else
		fragmentType:setShader("image_black_white")
		modItem:setShader("image_black_white")
		modLabel:setColor("#707070")
	end

	potentialLevel:setVisible(false)
	local gemBonusID = (gemType == 0 and gemData.lesserBonus or gemType == 1 and gemData.regularBonus or gemType == 2 and gemData.supremeBonus)
	local upgradeTier = WheelOfDestiny.basicModsUpgrade[gemBonusID] or 0
	if vesselCount == 3 then
		upgradeTier= WheelOfDestiny.supremeModsUpgrade[gemBonusID] or 0
	end

	if gemType == 0 then
		fragmentType:setImageClip(upgradeTier * 50 .. " 0 50 50")
		fragmentType.currentTier = upgradeTier
	elseif gemType == 1 then
		local lesserUpgradeTier = WheelOfDestiny.basicModsUpgrade[gemData.lesserBonus] or 0
		if upgradeTier > lesserUpgradeTier then
			fragmentType:setImageClip(lesserUpgradeTier * 50 .. " 0 50 50")
			potentialLevel:setVisible(true)
			potentialLevel:setImageClip(upgradeTier * 50 .. " 0 50 50")
			fragmentType.currentTier = lesserUpgradeTier
		else
			fragmentType:setImageClip(upgradeTier * 50 .. " 0 50 50")
			fragmentType.currentTier = upgradeTier
		end
	elseif gemType == 2 then
		local lesserUpgradeTier = WheelOfDestiny.basicModsUpgrade[gemData.lesserBonus] or 0
		local regularUpgradeTier = WheelOfDestiny.basicModsUpgrade[gemData.regularBonus] or 0
		local effectiveTier = math.min(lesserUpgradeTier, regularUpgradeTier)

		if upgradeTier > effectiveTier then
			fragmentType:setImageClip(effectiveTier * 50 .. " 0 50 50")
			potentialLevel:setVisible(true)
			potentialLevel:setImageClip(upgradeTier * 50 .. " 0 50 50")
			fragmentType.currentTier = effectiveTier
		else
			fragmentType:setImageClip(upgradeTier * 50 .. " 0 50 50")
			fragmentType.currentTier = upgradeTier
		end
	end

	fragmentType.modID = gemBonusID
	fragmentType.isSupreme = vesselCount == 3
end

function GemAtelier.manageVessel(remove)
	if not lastSelectedGem then
		return true
	end

	local gemData
	for _, data in ipairs(currentGemList) do
		if data.gemID == lastSelectedGem.gemID then
			gemData = data
			break
		end
	end
	if not gemData then
		g_logger.debug(string.format("[GemAtelier] gemData not found for gemID=%s", tostring(lastSelectedGem.gemID)))
		return true
	end

	local equipedList = {-1, -1, -1, -1}
	
	for _, id in pairs(WheelOfDestiny.equipedGems or {}) do
		if type(id) == "number" and id >= 0 then
			local domain = GemAtelier.getGemDomainById(id)
			if domain ~= -1 and domain ~= gemData.gemDomain then
				equipedList[domain + 1] = id
			end
		end
	end
	
	if not remove then
		equipedList[gemData.gemDomain + 1] = gemData.gemID
	else
		equipedList[gemData.gemDomain + 1] = -1
	end
	
	WheelOfDestiny.equipedGems = equipedList

	if WheelOfDestiny.currentPreset then
		WheelOfDestiny.currentPreset.equipedGems = equipedList
	end

	g_logger.debug(string.format(
		"[GemAtelier] manageVessel -> %s gemID=%d domain=%d | equipedGems={%s}",
		remove and "remove" or "equip",
		gemData.gemID, gemData.gemDomain,
		table.concat(WheelOfDestiny.equipedGems, ", ")
	))

	if lastSelectedGem then
		g_logger.debug("[GemAtelier] Updating side panel after equip/remove.")
		GemAtelier.setupVesselPanel()
		GemAtelier.onSelectGem(lastSelectedGem, true)
	else
		g_logger.debug("[GemAtelier] No gem selected after manageVessel, could not update panel.")
	end

	GemAtelier.showGems(false, lastSelectedGem.gemIndex or 1)
end


function GemAtelier.isGemEquipped(gemID)
	if not gemID or gemID < 0 then
		return false
	end
	for _, id in pairs(WheelOfDestiny.equipedGems or {}) do
		if type(id) == "number" and id == gemID then
			return true
		end
	end
	return false
end

function GemAtelier.getGemDomainById(id)
	if type(id) ~= "number" or id < 0 then
		return -1
	end
	for _, data in pairs(WheelOfDestiny.atelierGems) do
	  if data.gemID == id then
		return data.gemDomain
	  end
	end
	return -1
  end

function GemAtelier.getGemCountByDomain(domain)
	local count = 0
	for _, data in pairs(WheelOfDestiny.atelierGems) do
		if data.gemDomain == domain then
			count = count + 1
		end
	end
	return count
end

function GemAtelier.getGemDataById(id)
	if type(id) ~= "number" or id < 0 then
		g_logger.debug(string.format("[GemAtelier] getGemDataById called with invalid id: %s (type=%s)", tostring(id), type(id)))
		return nil
	end
	for _, data in pairs(WheelOfDestiny.atelierGems) do
		if data.gemID == id then
			return data
		end
	end
	g_logger.debug(string.format("[GemAtelier] getGemDataById: gemID=%d not found in atelierGems (total gems=%d)", id, #WheelOfDestiny.atelierGems))
	return nil
end

function GemAtelier.getEquipedGem(domain)
  for _, data in pairs(WheelOfDestiny.atelierGems) do
		if data.gemDomain == domain and GemAtelier.isGemEquipped(data.gemID) then
			return data
		end
	end
	return nil
end

function sendgemAction(actionType, param, pos)
	param = param or 0
	pos = pos or 0

	g_logger.debug(string.format("[GemAtelier] Sending action -> type=%d param=%d pos=%d", actionType, param, pos))
	g_game.gemAction(actionType, param, pos)
	-- Sin toggle optimista para ToggleLock: el server responde SIEMPRE con el refresh completo
	-- de la ventana (sendOpenWheelWindow) y ese estado es el real; el flip local a +300ms
	-- llegaba DESPUÉS del refresh y lo invertía — la UI quedaba al revés del server en cada click.
end



function GemAtelier.onRevealGem(button, gemType)
	if not button:isOn() then
		return true
	end
	sendgemAction(1, gemType)
end

function GemAtelier.onSwitchDomain(button)
	if not button:isOn() or not lastSelectedGem then
	  return true
	end
  
	local gemData = GemAtelier.getGemDataById(lastSelectedGem.gemID)
	if gemData then
	  g_logger.debug(string.format("[GemAtelier] Requesting domain switch for gem id=%d", gemData.gemID))
	  sendgemAction(2, gemData.gemID)
	else
	  g_logger.debug(string.format("[GemAtelier] onSwitchDomain: gemData not found for gemID=%s", tostring(lastSelectedGem.gemID)))
	end
end

function GemAtelier.onDestroyGem(button)
	if not button or not button:isOn() or not lastSelectedGem or destroyGemWindow ~= nil then
	  return true
	end

	local gemData = GemAtelier.getGemDataById(lastSelectedGem.gemID)
	if not gemData then
	  g_logger.debug("[GemAtelier] Failed to destroy: gemData not found.")
	  return true
	end
  
	wheelWindow:hide()
	wheelWindow:ungrabMouse()
	wheelWindow:ungrabKeyboard()
  
	local yesFunction = function()
	  sendgemAction(0, gemData.gemID)
	  g_logger.debug(string.format("[GemAtelier] Requesting gem destruction id=%d", gemData.gemID))
	  wheelWindow:show(true)
	  destroyGemWindow:destroy()
	  destroyGemWindow = nil
	  wheelWindow:grabMouse()
	  wheelWindow:grabKeyboard()
	end
  
	local noFunction = function()
	  wheelWindow:show(true)
	  destroyGemWindow:destroy()
	  destroyGemWindow = nil
	  wheelWindow:grabMouse()
	  wheelWindow:grabKeyboard()
	end
  
	destroyGemWindow = displayGeneralBox(
	  tr('Destroy Gem'),
	  tr("Are you sure you want to destroy this gem?"),
	  { { text = tr('Yes'), callback = yesFunction }, { text = tr('No'), callback = noFunction } },
	  yesFunction, noFunction
	)
  end
  

function GemAtelier.onLockGem(button)
	local gemID = (button and button.gemID) or (lastSelectedGem and lastSelectedGem.gemID)
	if not gemID then
	  g_logger.debug("[GemAtelier] onLockGem called without gemID.")
	  return true
	end
	g_logger.debug(string.format("[GemAtelier] Toggle lock gemID=%d", gemID))
	sendgemAction(3, gemID)
end
  
function GemAtelier.showLockedOnly(button)
	if not gemAtelierWindow then
		return true
	end

	lockedOnly = button:isChecked()
	currentPage = 1
	GemAtelier.showGems()
	GemAtelier.configurePages()
	local gemList = gemAtelierWindow:recursiveGetChildById("gemContent")
	if not gemList then
		return true
	end

	if #gemList:getChildren() > 0 then
		GemAtelier.onSelectGem(gemList:getChildren()[1])
	end
end

function GemAtelier.onSortQuality(widget, selectedIndex)
	if not gemAtelierWindow then
		return true
	end

	if type(selectedIndex) ~= "number" then
		if type(widget) == "number" then
			selectedIndex = widget
		elseif widget and widget.currentIndex then
			selectedIndex = widget.currentIndex
		else
			selectedIndex = 1
		end
	end


	sortQuality = selectedIndex
	currentPage = 1
	GemAtelier.showGems()
	GemAtelier.configurePages()
	local gemList = gemAtelierWindow:recursiveGetChildById("gemContent")
	if not gemList then
		return true
	end

	if #gemList:getChildren() > 0 then
		GemAtelier.onSelectGem(gemList:getChildren()[1])
	end
end

function GemAtelier.onSortAffinity(widget, selectedIndex)
	if not gemAtelierWindow then
		return true
	end

	if type(selectedIndex) ~= "number" then
		if type(widget) == "number" then
			selectedIndex = widget
		elseif widget and widget.currentIndex then
			selectedIndex = widget.currentIndex
		else
			selectedIndex = 1
		end
	end


	sortAffinity = selectedIndex
	currentPage = 1
	GemAtelier.showGems()
	GemAtelier.configurePages()
	local gemList = gemAtelierWindow:recursiveGetChildById("gemContent")
	if not gemList then
		return true
	end

	if #gemList:getChildren() > 0 then
		GemAtelier.onSelectGem(gemList:getChildren()[1])
	end
end

function GemAtelier.onSearchChange(self)
	local text = self:getText()
	if #text == 0 then
		currentSearchText = ""
		GemAtelier.showGems(true)
		return true
	end
	currentSearchText = text
	GemAtelier.showGems(true)
end

function GemAtelier.setupVesselPanel()
    if not gemAtelierWindow then
        return true
    end

    local selectWidget = gemAtelierWindow:recursiveGetChildById("vesselsContent")
    if not selectWidget then
        g_logger.debug("[GemAtelier] vesselsContent not found in window.")
        return
    end

    for i = 0, 3 do
        local background = selectWidget:recursiveGetChildById("vesselBg" .. i)
        local gemContainer = selectWidget:recursiveGetChildById("vessel" .. i)
        local gemItem = selectWidget:recursiveGetChildById("gemItem" .. i)

        if not background or not gemContainer or not gemItem then
            g_logger.debug(string.format(
                "[GemAtelier] Estrutura do slot %d incompleta (bg=%s, container=%s, gem=%s)",
                i, tostring(background ~= nil), tostring(gemContainer ~= nil), tostring(gemItem ~= nil)
            ))
        else
            background:setImageSource("/images/game/wheel/backdrop_skillwheel_socket_inactive")
            gemContainer:setVisible(false)
            gemItem:setImageClip("0 0 32 32")

            gemItem.gemID = -1
            gemItem:setVisible(false)

            local filledCount = GemAtelier.getFilledVesselCount(i)
            if filledCount ~= 0 then
                local startPos = 442
                local containerOffset = startPos + (102 * i)
                local modOffset = 34 * math.max(0, filledCount - 1)
                gemContainer:setImageClip(containerOffset + modOffset .. " 0 34 34")
                gemContainer:setVisible(true)
                background:setImageSource("/images/game/wheel/backdrop_skillwheel_socket_active")
            end
        end
    end

    for _, id in pairs(WheelOfDestiny.equipedGems) do
        repeat
        if type(id) ~= "number" or id < 0 then
            break
        end
        
        local data = GemAtelier.getGemDataById(id)
        if data then
            local background = selectWidget:recursiveGetChildById("vesselBg" .. data.gemDomain)
            local gemContainer = selectWidget:recursiveGetChildById("vessel" .. data.gemDomain)
            local gemItem = selectWidget:recursiveGetChildById("gemItem" .. data.gemDomain)

            if background and gemContainer and gemItem then
                if GemAtelier.isVesselAvailable(data.gemDomain, 1) then
                    background:setImageSource("/images/game/wheel/backdrop_skillwheel_socket_active")
                    gemContainer:setVisible(true)
                end

                local typeOffset = data.gemType * 32
                local domainOffset = data.gemDomain * 96
                local vocationOffset = (WheelOfDestiny.vocationId - 1) * 384
                local gemOffset = vocationOffset + domainOffset + typeOffset

                gemItem:setImageClip(gemOffset .. " 0 32 32")
                gemItem.gemID = data.gemID
                gemItem:setVisible(true)

                local startPos = 442
                local filledCount = GemAtelier.getFilledVesselCount(data.gemDomain)
                if filledCount == (data.gemType + 1) then
                    startPos = 34
                end

                local containerOffset = startPos + (102 * data.gemDomain)
                local modOffset = 34 * math.max(0, filledCount - 1)
                gemContainer:setImageClip(containerOffset + modOffset .. " 0 34 34")
            else
                g_logger.debug(string.format(
                    "[GemAtelier] Slot %d incomplete when applying gem %d.",
                    data.gemDomain or -1, data.gemID or -1
                ))
            end
        end
        until true
    end

    g_logger.debug("[GemAtelier] setupVesselPanel completed with 4 vessels.")
end


function GemAtelier.onClickVessel(widget, domain)
	g_logger.debug(string.format("[DebugClick] Click on vessel domain=%d gemID=%s", domain, tostring(widget.gemID)))
  
	if lastSelectedVessel then
	  lastSelectedVessel:setVisible(false)
	end
  
	local currentHighlight = gemAtelierWindow:recursiveGetChildById("selectVessel" .. domain)
	if currentHighlight then
	  currentHighlight:setVisible(true)
	  lastSelectedVessel = currentHighlight
	end
  
	local gemItem = gemAtelierWindow:recursiveGetChildById("gemItem" .. domain)
	local gemID = gemItem and gemItem.gemID
  
	local gemData = nil
	if gemID ~= nil then
	  gemData = GemAtelier.getGemDataById(gemID)
	end
  
	if gemData and gemData.gemDomain == domain then
	  g_logger.debug(string.format("[DebugClick] Gem found id=%d domain=%d (valid for vessel)", gemData.gemID, gemData.gemDomain))
	  GemAtelier.redirectToGem(gemData)
	  return
	end
  
	local fallbackGem = nil
	for _, data in pairs(WheelOfDestiny.atelierGems or {}) do
	  if data.gemDomain == domain then
		if not fallbackGem or data.gemID < fallbackGem.gemID then
		  fallbackGem = data
		end
	  end
	end
  
	if fallbackGem then
	  g_logger.debug(string.format("[DebugClick] No vessel equipped -> focusing smallest gem of domain %d (gemID=%d)", domain, fallbackGem.gemID))
	  GemAtelier.redirectToGem(fallbackGem)
	else
	  g_logger.debug(string.format("[DebugClick] No gem found in domain %d -> just filtering", domain))
	  GemAtelier.currentDomain = domain
	  if GemAtelier.showGems then
		GemAtelier.showGems(false, domain)
	  end
	end
end
  
function GemAtelier.onUnlockGem(gemID)
end

function GemAtelier.onModRedirect(self)
	local modID = self.modID
	local isSupreme = self.isSupreme
	local itemsPerPage = 30
	local pageIndex = nil
	local focusIndex = 0

	for i, k in pairs(Workshop.getFragmentList()) do
		if (isSupreme and k.supreme and k.modID == modID) or (not isSupreme and not k.supreme and k.modID == modID) then
			pageIndex = math.ceil(i / itemsPerPage)
			focusIndex = ((i - 1) % itemsPerPage) + 1
			break
		end
	end

	if not pageIndex then
		return true
	end

	Workshop.setCurrentPage(pageIndex)
	Workshop.showFragmentList(true, false, false, "", focusIndex)
	gemAtelierWindow:hide()
	fragmentWindow:show(true)
    gemMenuButton:setChecked(false)
    fragmentMenuButton:setChecked(true)
end

function GemAtelier.onHoverGem(self, hovered)
	local hoverWidget = self:recursiveGetChildById("hover")
	if not hoverWidget then
		return true
	end
	hoverWidget:setVisible(hovered)
	hoverWidget:setImageClip(self.currentTier and (200 + self.currentTier * 50 .. " 0 50 50") or "0 0 50 50")
	if hovered then
		g_mouse.pushCursor('pointer')
	else
		g_mouse.popCursor('pointer')
	end
end

function GemAtelier.getDamageAndHealing(self)
  local damage = 0
	for i = 0, 3 do
		local data = self.getEquipedGem(i)
		if data then
			local filledCount = self.getFilledVesselCount(i)
			if filledCount >= (data.gemType + 1) then
				damage = damage + (data.gemType == 2 and 2 or 1)
			end
		end
	end
  return damage
end
