local PT = AhUI
if not PT then return end

local G  = PT.ProfessionGuides
local UI = {}
local Shopping = PT.ProfessionGuides_Shopping
local Waypoints = PT.ProfessionGuides_Waypoints
local State = PT.ProfessionGuides_State
local FONT          = AhUI.FONT
local DEFAULT_SCALE = AhUI.SCALE_DEFAULT
local SCALE_STEP    = AhUI.SCALE_STEP
local SCALE_MIN     = AhUI.SCALE_MIN
local SCALE_MAX     = AhUI.SCALE_MAX
local INFO_ATLAS = "help-i"
local FRAME_PADDING_TOP    = 16
local FRAME_PADDING_BOTTOM = 16
local FRAME_MIN_HEIGHT     = 200
local TEXT_SAFE_WIDTH = 270
local LINE_SPACING = 3
local Tooltip = PT.Tooltip
local Epochs = PT.ProfessionGuides_Epochs or {}
local lastOpenedSkillLine = nil

local _itemLinkCache = {}
local function GetCachedItemLink(itemID)
    if _itemLinkCache[itemID] then return _itemLinkCache[itemID] end
    local link = select(2, GetItemInfo(itemID))
    if link then _itemLinkCache[itemID] = link end
    return link or ("item:" .. itemID)
end

local _itemNameCache = {}
local function GetCachedItemName(itemID)
    if _itemNameCache[itemID] then return _itemNameCache[itemID] end
    local name = GetItemInfo(itemID)
    if name then _itemNameCache[itemID] = name end
    return name or ("item:" .. itemID)
end

local enchantTargetLocation = nil

local function GetRecipeIDBySpellID(spellID)
    local recipeIDs = C_TradeSkillUI.GetAllRecipeIDs()
    if not recipeIDs then return nil end

    for _, recipeID in ipairs(recipeIDs) do
        local info = C_TradeSkillUI.GetRecipeInfo(recipeID)
        if info and info.spellID == spellID then
            return recipeID
        end
    end
end

local function DoCraft(spellID, count)
    local recipeInfo = C_TradeSkillUI.GetRecipeInfo(spellID)
    if not recipeInfo then
        print("AhUI: GetRecipeInfo вернул nil для spellID:", spellID)
        return
    end

    if recipeInfo.isEnchantingRecipe then
        local target = nil
        for bag = 0, NUM_BAG_SLOTS do
            local slots = C_Container.GetContainerNumSlots(bag)
            if slots then
                for slot = 1, slots do
                    if C_Container.GetContainerItemID(bag, slot) == 38682 then
                        target = ItemLocation:CreateFromBagAndSlot(bag, slot)
                        break
                    end
                end
            end
            if target then break end
        end
        target = target or enchantTargetLocation
        if not target then
            print("AhUI: нет свитка наложения чар в сумках")
            return
        end
        C_TradeSkillUI.CraftEnchant(spellID, count or 1, nil, target)
    else
        C_TradeSkillUI.CraftRecipe(spellID, count or 1)
    end
end

local _craftPendingSpellID = nil
local _craftPendingCount   = nil
local _craftWaitFrame = nil

local function CraftRecipe(spellID, count)
    if not C_TradeSkillUI then return end
    if not spellID then return end

    local recipeIDs = C_TradeSkillUI.GetAllRecipeIDs()
    if recipeIDs and #recipeIDs > 0 then
        DoCraft(spellID, count)
        return
    end

    _craftPendingSpellID = spellID
    _craftPendingCount   = count or 1

    local skillLineID = G:GetSelectedSkillLine()
    if skillLineID then
        C_TradeSkillUI.OpenTradeSkill(skillLineID)
    end

    if not _craftWaitFrame then
        _craftWaitFrame = CreateFrame("Frame")
        _craftWaitFrame:RegisterEvent("TRADE_SKILL_LIST_UPDATE")
        _craftWaitFrame:SetScript("OnEvent", function()
            if _craftPendingSpellID then
                local sid = _craftPendingSpellID
                local cnt = _craftPendingCount
                _craftPendingSpellID = nil
                _craftPendingCount   = nil
                DoCraft(sid, cnt)
            end
        end)
    end
end


local function UpdateBuyButtonVisibility()
    if not UI.frame or not UI.frame.buyBtn then return end

    local skillLineID = G:GetSelectedSkillLine()
    local step = skillLineID and G:GetCurrentStepBySkillLine(skillLineID)

    UI.frame.buyBtn:SetShown(
        Shopping:CanBuy(skillLineID)
        and not (step and step.disablesShopping)
    )
end

local function CreateIconBorder(iconHolder)
    local holder = CreateFrame("Frame", nil, iconHolder, "BackdropTemplate")
    holder:SetFrameLevel(iconHolder:GetFrameLevel() + 10)

    holder:SetPoint("TOPLEFT", iconHolder, "TOPLEFT", -2, 2)
    holder:SetPoint("BOTTOMRIGHT", iconHolder, "BOTTOMRIGHT", 2, -2)

    holder:SetBackdrop({
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 2,
    })

    holder:SetBackdropBorderColor(0, 0, 0, 1)
    iconHolder.border = holder
end

local function CreateButtonBorder(button)
    local border = CreateFrame("Frame", nil, button, "BackdropTemplate")
    border:SetPoint("TOPLEFT", -1, 1)
    border:SetPoint("BOTTOMRIGHT", 1, -1)
    border:SetFrameLevel(button:GetFrameLevel() - 1)

    border:SetBackdrop({
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })

    border:SetBackdropBorderColor(1, 1, 1, 0.15)
    button.border = border
end

local _pgConfigCache = nil
local function GetPGConfig()
    if _pgConfigCache then return _pgConfigCache end
    AhUI_CharConfig = AhUI_CharConfig or {}
    AhUI_CharConfig.professionGuide = AhUI_CharConfig.professionGuide or {}
    _pgConfigCache = AhUI_CharConfig.professionGuide
    return _pgConfigCache
end

function UI:SetScale(delta)
    local cfg = GetPGConfig()
    local cur = cfg.scale or DEFAULT_SCALE

    local nextScale = cur + delta
    nextScale = math.max(SCALE_MIN, math.min(SCALE_MAX, nextScale))

    cfg.scale = nextScale

    if self.frame then
        self.frame:SetScale(nextScale)
    end
end

function UI:ResetScale()
    local cfg = GetPGConfig()
    cfg.scale = DEFAULT_SCALE

    if self.frame then
        self.frame:SetScale(DEFAULT_SCALE)
    end
end

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

    local f = CreateFrame("Frame", "AhUIProfessionGuide", UIParent, "BackdropTemplate")
    f:SetSize(500, 210)
	f:ClearAllPoints()
	f:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 400, -200)
	f:SetWidth(500)
    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    f:SetBackdropColor(0, 0, 0, 0.92)
    f:SetBackdropBorderColor(1, 1, 1, 1)

    table.insert(UISpecialFrames, f:GetName())

    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", function(self)
		self:StopMovingOrSizing()

		local left, top = self:GetLeft(), self:GetTop()

		self:ClearAllPoints()
		self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
		self:SetWidth(self:GetWidth())
	end)
	
	f:EnableMouseWheel(true)

	f:SetScript("OnMouseWheel", function(self, delta)
		if not IsAltKeyDown() then return end

		if delta > 0 then
			UI:SetScale(SCALE_STEP)
		else
			UI:SetScale(-SCALE_STEP)
		end
	end)

	f:SetScript("OnMouseUp", function(self, button)
		if button == "MiddleButton" and IsAltKeyDown() then
			UI:ResetScale()
		end
	end)

    f.title = f:CreateFontString(nil, "OVERLAY")
    f.title:SetFont(FONT, 14, "OUTLINE")
    f.title:SetPoint("TOPLEFT", 14, -12)
    f.title:SetText("Руководства ремесленника.")
	f.profIconHolder = CreateFrame("Frame", nil, f, "BackdropTemplate")
	f.profIconHolder:SetSize(26, 26)
	f.profIconHolder:SetPoint("LEFT", f.title, "RIGHT", 8, 0)
	f.profIconHolder:SetFrameLevel(f:GetFrameLevel() + 5)

	f.profIcon = f.profIconHolder:CreateTexture(nil, "ARTWORK", nil, 5)
	f.profIcon:SetAllPoints()

	CreateIconBorder(f.profIconHolder)

	f.profIconHolder:Hide()

	f.profIconHolder:SetScript("OnEnter", function()
		GameTooltip:SetOwner(f.profIconHolder, "ANCHOR_RIGHT")
		GameTooltip:AddLine("Открыть профессию", 1, 1, 1)
		GameTooltip:Show()
	end)
	f.profHint = f:CreateFontString(nil, "OVERLAY")
	f.profHint:SetFont(FONT, 11)
	f.profHint:SetPoint("LEFT", f.profIconHolder, "RIGHT", 6, 0)
	f.profHint:SetText("|cffaaaaaa<< открыть\nпрофессию|r")
	f.profHint:Hide()

	f.profIconHolder:SetScript("OnLeave", function()
		GameTooltip:Hide()
	end)

	f.profIconHolder:SetScript("OnMouseDown", function(_, button)
		if button ~= "LeftButton" then return end

		f.profIcon:SetPoint("CENTER", 1, -1)

		local skillLineID = G:GetSelectedSkillLine()
		if not skillLineID then return end

		if not C_TradeSkillUI
		   or not C_TradeSkillUI.OpenTradeSkill
		then
			return
		end

		local parentID = G:GetParentProfessionID(skillLineID)
		if not parentID then return end

		if ProfessionsFrame and ProfessionsFrame:IsShown()
		   and lastOpenedSkillLine == parentID
		then
			HideUIPanel(ProfessionsFrame)
			lastOpenedSkillLine = nil
			return
		end

		ShowUIPanel(ProfessionsFrame)
		C_TradeSkillUI.OpenTradeSkill(parentID)

		lastOpenedSkillLine = parentID
	end)

	f.profIconHolder:SetScript("OnMouseUp", function(_, button)
		if button == "LeftButton" then
			f.profIcon:SetPoint("CENTER", 0, 0)
		end
	end)

    f.close = CreateFrame("Button", nil, f)
    f.close:SetSize(16, 16)
    f.close:SetPoint("TOPRIGHT", -10, -10)

    local c = f.close:CreateFontString(nil, "OVERLAY")
    c:SetFont(FONT, 25)
    c:SetPoint("CENTER")
    c:SetText("×")
    c:SetTextColor(0.7, 0.7, 0.7)

    f.close:SetScript("OnEnter", function() c:SetTextColor(1, 0.4, 0.4) end)
    f.close:SetScript("OnLeave", function() c:SetTextColor(0.7, 0.7, 0.7) end)
    f.close:SetScript("OnClick", function() f:Hide() end)
	f.infoBtn = CreateFrame("Button", nil, f)
	f.infoBtn:SetSize(14, 14)
	f.infoBtn:SetPoint("TOPRIGHT", f.close, "BOTTOMRIGHT", 0, -4)
	f.infoBtn:SetFrameLevel(f.close:GetFrameLevel() + 5)
	f.infoBtn.icon = f.infoBtn:CreateTexture(nil, "OVERLAY")
	f.infoBtn.icon:SetSize(25, 25)
	f.infoBtn.icon:SetPoint("CENTER")
	f.infoBtn.icon:SetDrawLayer("OVERLAY", 7)
	f.infoBtn.icon:SetTexture("Interface\\COMMON\\help-i")
	f.infoBtn.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
	f.infoBtn.icon:SetAlpha(0.85)
	f.infoBtn:SetScript("OnEnter", function(self)
	    if self.icon then
			self.icon:SetAlpha(1)
		end
		if not Tooltip then return end
		Tooltip:Clear()
		Tooltip:AddLine("Руководства ремесленника.", 14, 1, 1, 1)
		Tooltip:AddLine(" ")
		Tooltip:AddLine("• Выберите профессию из доступных персонажу")
		Tooltip:AddLine("• Выберите эпоху профессии")
		Tooltip:AddLine("• Откройте профессию - появятся кнопки создания предметов")
		Tooltip:AddLine("• Используйте кнопки «Предыдущий / Следующий»")
		Tooltip:AddLine("     для просмотра шагов руководства", 12, 0.85, 0.85, 0.85)
		Tooltip:AddLine("• «Создать» - крафт 1 предмета")
		Tooltip:AddLine("• «Создать всё» - крафт максимума")
		Tooltip:AddLine("• ЛКМ по имени учителя - метка на карте ")
		Tooltip:AddLine("     не забывайте изучать новые рецепты", 12, 0.85, 0.85, 0.85)
		Tooltip:AddLine(" ")
		Tooltip:AddLine("Alt + колесо - масштаб", 12, 0.85, 0.85, 0.85)
		Tooltip:AddLine("Alt + клик колёсиком - сброс", 12, 0.85, 0.85, 0.85)
		Tooltip:ShowAt(self, "BOTTOMRIGHT", "TOPRIGHT", 0, 6)
	end)
	f.infoBtn:SetScript("OnLeave", function(self)
	    if self.icon then
			self.icon:SetAlpha(0.85)
		end
		if Tooltip then
			Tooltip:Clear()
		end
	end)

	f.profBtn = CreateFrame("Button", nil, f)
	f.profBtn:SetFrameLevel(f:GetFrameLevel() + 20)
	f.profBtn:SetSize(160, 22)
	f.profBtn:SetPoint("TOPRIGHT", -40, -12)

	local bgp = f.profBtn:CreateTexture(nil, "BACKGROUND")
	bgp:SetAllPoints()
	bgp:SetColorTexture(1, 1, 1, 0.08)

	f.profBtn.text = f.profBtn:CreateFontString(nil, "OVERLAY")
	f.profBtn.text:SetFont(FONT, 12)
	f.profBtn.text:SetPoint("CENTER")
	f.profBtn.text:SetText("Выбрать профессию")

    f.epochBtn = CreateFrame("Button", nil, f)
	f.epochBtn:SetFrameLevel(f:GetFrameLevel() + 20)
    f.epochBtn:SetSize(160, 22)
    f.epochBtn:SetPoint("TOPRIGHT", -40, -40)

    local bg = f.epochBtn:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    bg:SetColorTexture(1, 1, 1, 0.08)

    f.epochBtn.text = f.epochBtn:CreateFontString(nil, "OVERLAY")
    f.epochBtn.text:SetFont(FONT, 12)
    f.epochBtn.text:SetPoint("CENTER")
    f.epochBtn.text:SetText("Выбрать эпоху")
	f.buyBtn = CreateFrame("Button", nil, f)
	f.buyBtn:SetFrameLevel(f:GetFrameLevel() + 20)
	f.buyBtn:SetSize(160, 22)
	f.buyBtn:SetPoint("TOPRIGHT", -40, -68)
	f.buyBtn:Hide()

	local bg = f.buyBtn:CreateTexture(nil, "BACKGROUND")
	bg:SetAllPoints()
	bg:SetColorTexture(0.1, 0.4, 0.9, 0.25)

	local glow = CreateFrame("Frame", nil, f.buyBtn, "BackdropTemplate")
	glow:SetPoint("TOPLEFT", f.buyBtn, "TOPLEFT", -3, 3)
	glow:SetPoint("BOTTOMRIGHT", f.buyBtn, "BOTTOMRIGHT", 3, -3)
	glow:SetFrameLevel(f.buyBtn:GetFrameLevel() - 1)
	glow:SetBackdrop({
		edgeFile = "Interface\\Buttons\\WHITE8x8",
		edgeSize = 2,
	})
	glow:SetBackdropBorderColor(0.3, 0.7, 1, 0.9)

	local ag = glow:CreateAnimationGroup()
	ag:SetLooping("BOUNCE")
	local anim = ag:CreateAnimation("Alpha")
	anim:SetFromAlpha(0.4)
	anim:SetToAlpha(1.0)
	anim:SetDuration(1.2)
	ag:Play()

	f.buyBtn.text = f.buyBtn:CreateFontString(nil, "OVERLAY")
	f.buyBtn.text:SetFont(FONT, 12)
	f.buyBtn.text:SetPoint("CENTER")
	f.buyBtn.text:SetText("Создать список покупок")
	f.buyBtn:SetScript("OnClick", function()
		local skillLineID = G:GetSelectedSkillLine()
		if not skillLineID then return end

		local mats = G:BuildShoppingList(skillLineID)
		if not mats or not next(mats) then
			print("AhUI: нечего покупать")
			return
		end

		local S = PT.ProfessionGuides_Shopping
		if not S or not S:IsAvailable() then
			print("AhUI: Auctionator недоступен")
			return
		end

		local ok, reason = S:CreateShoppingSession(mats)
		if not ok then
			print("AhUI: ошибка аукциона:", reason)
		end

	end)
	f.buyBtn:SetScript("OnEnter", function(self)
		if not Tooltip then return end

		Tooltip:Clear()
		Tooltip:AddLine("Создать список покупок", 14, 1, 1, 1)
		Tooltip:AddLine(" ")
		Tooltip:AddLine("Создаёт список покупок в Auctionator.", 12, 0.9, 0.9, 0.9)
		Tooltip:AddLine("Откройте вкладку «Покупка» для работы со списком.", 12, 0.9, 0.9, 0.9)
		Tooltip:ShowAt(self, "TOP", "BOTTOM", 0, -6)
	end)

	f.buyBtn:SetScript("OnLeave", function()
		if Tooltip then
			Tooltip:Clear()
		end
	end)

	f.nextStepBtn = CreateFrame("Button", nil, f)
	f.nextStepBtn:SetFrameLevel(f:GetFrameLevel() + 20)
	f.nextStepBtn:SetSize(100, 20)
	f.nextStepBtn:SetPoint("TOPRIGHT", -40, -96)
	f.nextStepBtn:Hide()

	local bgNext = f.nextStepBtn:CreateTexture(nil, "BACKGROUND")
	bgNext:SetAllPoints()
	bgNext:SetColorTexture(1, 1, 1, 0.06)

	f.nextStepBtn.text = f.nextStepBtn:CreateFontString(nil, "OVERLAY")
	f.nextStepBtn.text:SetFont(FONT, 11)
	f.nextStepBtn.text:SetPoint("CENTER")
	f.nextStepBtn.text:SetText("Следующий >")

	f.prevStepBtn = CreateFrame("Button", nil, f)
	f.prevStepBtn:SetFrameLevel(f:GetFrameLevel() + 20)
	f.prevStepBtn:SetSize(100, 20)
	f.prevStepBtn:SetPoint("TOPLEFT", f.nextStepBtn, "BOTTOMLEFT", -60, -6)
	f.prevStepBtn:Hide()

	local bgPrev = f.prevStepBtn:CreateTexture(nil, "BACKGROUND")
	bgPrev:SetAllPoints()
	bgPrev:SetColorTexture(1, 1, 1, 0.06)

	f.prevStepBtn.text = f.prevStepBtn:CreateFontString(nil, "OVERLAY")
	f.prevStepBtn.text:SetFont(FONT, 11)
	f.prevStepBtn.text:SetPoint("CENTER")
	f.prevStepBtn.text:SetText("< Предыдущий")

	f.prevStepBtn:SetScript("OnClick", function()
		G:StepPrev()
		State:Save()
		UI:Refresh()
	end)

	f.nextStepBtn:SetScript("OnClick", function()
		G:StepNext()
		State:Save()
		UI:Refresh()
	end)

	f.stepIndicator = f:CreateFontString(nil, "OVERLAY")
	f.stepIndicator:SetFont(FONT, 11)
	f.stepIndicator:SetPoint("LEFT", f.prevStepBtn, "RIGHT", 8, 0)
	f.stepIndicator:SetText("")
	f.stepIndicator:Hide()
	f.resetStepBtn = CreateFrame("Button", nil, f)
	f.resetStepBtn:SetFrameLevel(f:GetFrameLevel() + 20)
	f.resetStepBtn:SetSize(160, 20)
	f.resetStepBtn:SetPoint("TOPRIGHT", -40, -148)
	f.resetStepBtn:Hide()

	local bgReset = f.resetStepBtn:CreateTexture(nil, "BACKGROUND")
	bgReset:SetAllPoints()
	bgReset:SetColorTexture(1, 1, 1, 0.06)

	f.resetStepBtn.text = f.resetStepBtn:CreateFontString(nil, "OVERLAY")
	f.resetStepBtn.text:SetFont(FONT, 11)
	f.resetStepBtn.text:SetPoint("CENTER")
	f.resetStepBtn.text:SetText("К текущему шагу")
	f.resetStepBtn:SetScript("OnClick", function()
		G:ResetManualStep()
		State:Save()
		UI:Refresh()
	end)
	f.content = CreateFrame("Frame", nil, f)
	f.content:SetPoint("TOPLEFT", f.title, "BOTTOMLEFT", 0, -16)
	f.content:SetPoint("TOPRIGHT", f, "TOPRIGHT", -16, -48)
	f.content:SetFrameLevel(f:GetFrameLevel() + 1)
	f.content:SetHeight(1)
	f.content:SetHeight(1)
	f.textLines = {}
	f.trainersHeader = f.content:CreateFontString(nil, "OVERLAY")
	f.trainersHeader:SetFont(FONT, 12)
	f.trainersHeader:SetText("Учителя:")
	f.trainersHeader:Hide()

	f.trainerButtons = f.trainerButtons or {}

	f.footer = CreateFrame("Frame", nil, f)
	f.footer:SetPoint("TOPLEFT", f.content, "BOTTOMLEFT", 0, 6)
	f.footer:SetPoint("TOPRIGHT", f.content, "BOTTOMRIGHT", 0, 6)
	f.footer:SetHeight(48)

	f.craftAllBtn = CreateFrame("Button", nil, f)
	f.craftAllBtn:SetSize(90, 20)
	f.craftAllBtn:Hide()
	f.craftAllBtn:SetPoint("TOPRIGHT", f.footer, "TOPRIGHT", -24, 0)

	local bg2 = f.craftAllBtn:CreateTexture(nil, "BACKGROUND")
	bg2:SetAllPoints()
	bg2:SetColorTexture(1, 1, 1, 0.06)

	f.craftAllBtn.text = f.craftAllBtn:CreateFontString(nil, "OVERLAY")
	f.craftAllBtn.text:SetFont(FONT, 12)
	f.craftAllBtn.text:SetPoint("CENTER")
	f.craftAllBtn.text:SetText("Создать всё")
	f.craftBtn = CreateFrame("Button", nil, f)
	f.craftBtn:SetSize(90, 20)
	f.craftBtn:Hide()
	f.craftBtn:SetPoint("BOTTOMRIGHT", f.craftAllBtn, "TOPRIGHT", 0, 6)

	local bg1 = f.craftBtn:CreateTexture(nil, "BACKGROUND")
	bg1:SetAllPoints()
	bg1:SetColorTexture(1, 1, 1, 0.08)

	f.craftBtn.text = f.craftBtn:CreateFontString(nil, "OVERLAY")
	f.craftBtn.text:SetFont(FONT, 12)
	f.craftBtn.text:SetPoint("CENTER")
	f.craftBtn.text:SetText("Создать")
	f.craftBtn:SetScript("OnClick", function()
		local skillLineID = G:GetSelectedSkillLine()
		if not skillLineID then return end

		local step = G:GetCurrentStepBySkillLine(skillLineID)
		if not step or not step.recipes or not step.recipes[1] then
			print("AhUI: нет рецепта для крафта")
			return
		end

		local recipe = step.recipes[1]
		CraftRecipe(recipe.spellID, 1)
	end)

	f.enchantTargetSlot = CreateFrame("Button", nil, f)
	f.enchantTargetSlot:SetSize(36, 36)
	f.enchantTargetSlot:SetPoint("RIGHT", f.craftBtn, "LEFT", -8, -13)
	f.enchantTargetSlot:Hide()

	f.enchantTargetSlot.bg = f.enchantTargetSlot:CreateTexture(nil, "BACKGROUND")
	f.enchantTargetSlot.bg:SetAllPoints()
	f.enchantTargetSlot.bg:SetColorTexture(1, 1, 1, 0.08)

	f.enchantTargetSlot.icon = f.enchantTargetSlot:CreateTexture(nil, "ARTWORK")
	f.enchantTargetSlot.icon:SetAllPoints()
	f.enchantTargetSlot.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)

	f.enchantTargetSlot.label = f.enchantTargetSlot:CreateFontString(nil, "OVERLAY")
	f.enchantTargetSlot.label:SetFont(FONT, 9)
	f.enchantTargetSlot.label:SetPoint("TOP", f.enchantTargetSlot, "BOTTOM", 0, -2)
	f.enchantTargetSlot.label:SetText("Цель чар")
	f.enchantTargetSlot.label:SetTextColor(0.7, 0.7, 0.7)

	f.enchantTargetSlot:SetScript("OnClick", function(self)
		local cursorType, _, itemLink = GetCursorInfo()
		if cursorType == "item" then
			local targetLoc = nil
			for bag = 0, NUM_BAG_SLOTS do
				local slots = C_Container.GetContainerNumSlots(bag)
				if slots then
					for slot = 1, slots do
						local link = C_Container.GetContainerItemLink(bag, slot)
						if link == itemLink then
							targetLoc = ItemLocation:CreateFromBagAndSlot(bag, slot)
							break
						end
					end
				end
				if targetLoc then break end
			end
			if targetLoc then
				enchantTargetLocation = targetLoc
				local icon = C_Item.GetItemIconByID(C_Container.GetContainerItemID(
					targetLoc:GetBagAndSlot()))
				if icon then
					self.icon:SetTexture(icon)
				end
				ClearCursor()
			end
		elseif enchantTargetLocation then

			enchantTargetLocation = nil
			self.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
		end
	end)

	f.enchantTargetSlot:SetScript("OnReceiveDrag", function(self)
		local cursorType, _, itemLink = GetCursorInfo()
		if cursorType == "item" then
			local targetLoc = nil
			for bag = 0, NUM_BAG_SLOTS do
				local slots = C_Container.GetContainerNumSlots(bag)
				if slots then
					for slot = 1, slots do
						local link = C_Container.GetContainerItemLink(bag, slot)
						if link == itemLink then
							targetLoc = ItemLocation:CreateFromBagAndSlot(bag, slot)
							break
						end
					end
				end
				if targetLoc then break end
			end
			if targetLoc then
				enchantTargetLocation = targetLoc
				local icon = C_Item.GetItemIconByID(C_Container.GetContainerItemID(
					targetLoc:GetBagAndSlot()))
				if icon then
					self.icon:SetTexture(icon)
				end
				ClearCursor()
			end
		end
	end)

	f.enchantTargetSlot:SetScript("OnEnter", function(self)
		if enchantTargetLocation then
			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
			local bag, slot = enchantTargetLocation:GetBagAndSlot()
			GameTooltip:SetBagItem(bag, slot)
			GameTooltip:Show()
		else
			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
			GameTooltip:SetText("Перетащите предмет сюда\nдля наложения чар", nil, nil, nil, nil, true)
			GameTooltip:Show()
		end
	end)

	f.enchantTargetSlot:SetScript("OnLeave", function()
		GameTooltip:Hide()
	end)

	f.enchantTargetSlot.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
	f.enchantTargetSlot.icon:SetDesaturated(true)

	f.craftAllBtn:SetScript("OnClick", function()
		local skillLineID = G:GetSelectedSkillLine()
		if not skillLineID then return end

		local step = G:GetCurrentStepBySkillLine(skillLineID)
		if not step or not step.recipes or not step.recipes[1] then
			print("AhUI: нет рецепта для крафта")
			return
		end

		local recipe = step.recipes[1]

		local need = recipe.crafts or 0
		if need <= 0 then
			print("AhUI: crafts не указан")
			return
		end

		local byMaterials = G:GetMaxCraftsByMaterials(recipe)
		if byMaterials <= 0 then
			print("AhUI: недостаточно материалов")
			return
		end

		local craftCount = math.min(need, byMaterials)

		CraftRecipe(recipe.spellID, craftCount)

	end)
	
    f:Hide()
    self.frame = f
	State:Restore()

	if State.materialsExpanded == nil then
		State.materialsExpanded = false
	end

    UI:Refresh()

    return f
end

local _menuOverlay = nil
local _activeMenu  = nil  

local function GetMenuOverlay()
    if _menuOverlay then return _menuOverlay end

    _menuOverlay = CreateFrame("Frame", nil, UIParent)
    _menuOverlay:SetAllPoints(UIParent)
    _menuOverlay:SetFrameStrata("FULLSCREEN_DIALOG")
    _menuOverlay:EnableMouse(true)
    _menuOverlay:Hide()

    _menuOverlay:SetScript("OnMouseDown", function()
        if _activeMenu then
            _activeMenu:Hide()
            _activeMenu = nil
        end
        _menuOverlay:Hide()
    end)

    return _menuOverlay
end

local function OpenMenu(menu, btn)
    local overlay = GetMenuOverlay()

    if _activeMenu and _activeMenu ~= menu then
        _activeMenu:Hide()
    end

    menu:SetParent(overlay)
    menu:ClearAllPoints()
    menu:SetPoint("TOPRIGHT", btn, "BOTTOMRIGHT", 0, -2)

    if UI.frame then
        menu:SetScale(UI.frame:GetScale())
    end

    _activeMenu = menu
    overlay:Show()
    menu:Show()
end

local function CloseMenu(menu)
    if menu then menu:Hide() end
    if _menuOverlay then _menuOverlay:Hide() end
    _activeMenu = nil
end

local _profMenu = nil
local _profMenuItems = {}  
local function BuildProfMenu()
    if not Epochs.professionsOrder then return end

    local overlay = GetMenuOverlay()
    local itemHeight = 18

    local m = CreateFrame("Frame", nil, overlay, "BackdropTemplate")
    m:SetSize(160, #Epochs.professionsOrder * itemHeight + 12)
    m:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    m:SetBackdropColor(0, 0, 0, 0.95)
    m:SetBackdropBorderColor(1, 1, 1, 0.15)
    m:EnableMouse(true)
    m:SetScript("OnHide", function() CloseMenu(nil) end)
    m:Hide()

    local y = -6
    for _, prof in ipairs(Epochs.professionsOrder) do
        local b = CreateFrame("Button", nil, m)
        b:SetPoint("TOPLEFT", 8, y)
        b:SetSize(144, itemHeight)

        local fs = b:CreateFontString(nil, "OVERLAY")
        fs:SetFont(FONT, 12)
        fs:SetPoint("LEFT")

        local displayName = (PT.L and PT.L[prof]) or prof
        fs:SetText(displayName)

        b._prof        = prof
        b._displayName = displayName

        b:SetScript("OnClick", function(self)
            G:SetProfession(self._prof)
            State:Save()

            if UI.frame and UI.frame.profBtn then
                UI.frame.profBtn.text:SetText(self._displayName)
            end
            if UI.frame and UI.frame.epochBtn then
                UI.frame.epochBtn.text:SetText("Выбрать эпоху")
            end

            CloseMenu(_profMenu)
            UI:Refresh()
        end)

        table.insert(_profMenuItems, { btn = b, label = fs })
        y = y - itemHeight
    end

    return m
end

local function ShowProfessionMenu(btn)
    if not Epochs.professionsOrder then return end

    if not _profMenu then
        _profMenu = BuildProfMenu()
    end
    if not _profMenu then return end

    OpenMenu(_profMenu, btn)
end

local _epochMenu      = nil
local _epochMenuBtns  = {}  

local function GetOrCreateEpochMenu()
    if _epochMenu then return _epochMenu end

    local overlay = GetMenuOverlay()
    local m = CreateFrame("Frame", nil, overlay, "BackdropTemplate")
    m:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    m:SetBackdropColor(0, 0, 0, 0.95)
    m:SetBackdropBorderColor(1, 1, 1, 0.15)
    m:EnableMouse(true)
    m:SetScript("OnHide", function() CloseMenu(nil) end)
    m:Hide()

    _epochMenu = m
    return m
end

local function ShowEpochMenu(btn)
    local prof = G:GetSelectedProfession()
    if not prof then
        print("AhUI: выберите профессию")
        return
    end

    local profEpochs = Epochs[prof]
    if not profEpochs then
        print("AhUI: no epochs for profession", prof)
        return
    end

    local order      = profEpochs.order
    local data       = profEpochs.data
    local itemHeight = 18
    local m          = GetOrCreateEpochMenu()

    for _, b in ipairs(_epochMenuBtns) do
        b:Hide()
    end

    local count = 0
    for _, epochSkillLineID in ipairs(order) do
        local info = data[epochSkillLineID]
        if info then
            count = count + 1
            local b = _epochMenuBtns[count]

            if not b then
                b = CreateFrame("Button", nil, m)
                b:SetSize(144, itemHeight)

                local fs = b:CreateFontString(nil, "OVERLAY")
                fs:SetFont(FONT, 12)
                fs:SetPoint("LEFT")
                b._label = fs

                b:SetScript("OnClick", function(self)
                    G:SetSkillLine(self._epochID)
                    State:Save()

                    if UI.frame and UI.frame.epochBtn then
                        UI.frame.epochBtn.text:SetText(self._epochName)
                    end

                    CloseMenu(_epochMenu)
                    UpdateBuyButtonVisibility()
                    UI:Refresh()
                end)

                _epochMenuBtns[count] = b
            end

            b._epochID   = epochSkillLineID
            b._epochName = info.name
            b._label:SetText(info.name)
            b:ClearAllPoints()
            b:SetPoint("TOPLEFT", 8, -6 - (count - 1) * itemHeight)
            b:Show()
        end
    end

    m:SetSize(160, count * itemHeight + 12)
    OpenMenu(m, btn)
end

local function GetTextLine(f, index)
    local line = f.textLines[index]

    if not line then
        line = CreateFrame("Button", nil, f.content)
        line:SetHeight(16)
        line:SetWidth(380)
        line:EnableMouse(true)

        line.text = line:CreateFontString(nil, "OVERLAY")
        line.text:SetFont(FONT, 12)
        line.text:SetPoint("LEFT", 0, 0)
        line.text:SetJustifyH("LEFT")

        line:SetScript("OnEnter", function(self)
            if self.itemID then
                GameTooltip:SetOwner(self, "ANCHOR_CURSOR")
                GameTooltip:SetItemByID(self.itemID)
                GameTooltip:Show()
            end
        end)

        line:SetScript("OnLeave", function()
            GameTooltip:Hide()
        end)

        line:SetScript("OnClick", function(self)
            if self.itemLink then
                ChatEdit_InsertLink(self.itemLink)
            end
        end)

        f.textLines[index] = line
    end

    return line
end

local function BuildNoteRefs(step)
    if not step or not step.noteRefs then return nil end

    local out = {}

    for _, ref in ipairs(step.noteRefs) do
        if ref.type == "spell" and ref.spellID then
            local name

            if C_Spell and C_Spell.GetSpellInfo then
                local info = C_Spell.GetSpellInfo(ref.spellID)
                name = info and info.name
            elseif GetSpellInfo then
                name = GetSpellInfo(ref.spellID)
            end

            table.insert(out, "• " .. (name or ("spell:" .. ref.spellID)))

        elseif ref.type == "item" and ref.itemID then
            local name = GetCachedItemName(ref.itemID)
            table.insert(out, "• " .. (name or ("item:" .. ref.itemID)))
        end
    end

    if #out == 0 then return nil end
    return out
end

local function GetFirstLearnedRecipe(step)
	if not step or not step.recipes then return nil end

	for _, r in ipairs(step.recipes) do
		if r.spellID and G:IsRecipeLearned(r.spellID) then
			return r
		end
	end

	return nil
end

function UI:Refresh()
    local f = self.frame
    local skillLineID = G:GetSelectedSkillLine()

for _, l in ipairs(f.textLines) do
    l:Hide()
    if l.icon then l.icon:Hide() end
end

if f.trainersHeader then
    f.trainersHeader:Hide()
end

if f.trainerButtons then
    for _, b in ipairs(f.trainerButtons) do
        b:Hide()
    end
end

local ctx = { y = 0, i = 1 }
local skipDefaultTrainers = false

local function AddLine(text, color)
    local line = GetTextLine(f, ctx.i)

    if line.icon then
        line.icon:Hide()
    end

    line:SetWidth(TEXT_SAFE_WIDTH)

    line.text:ClearAllPoints()
    line.text:SetPoint("LEFT", 0, 0)
    line.text:SetWidth(TEXT_SAFE_WIDTH)
    line.text:SetWordWrap(true)

    line:ClearAllPoints()
    line:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)

    if color then
        text = color .. text .. "|r"
    end

    line.text:SetText(text)

    line.itemID = nil
    line.itemLink = nil
    line:Show()

    local h = line.text:GetStringHeight()
    line:SetHeight(h)

    ctx.y = ctx.y + h + LINE_SPACING
    ctx.i = ctx.i + 1
end

ctx.y = ctx.y + 6

f.profIconHolder:Hide()
if f.profHint then f.profHint:Hide() end

if skillLineID and G.GetProfessionIcon then
    local icon = G:GetProfessionIcon(skillLineID)
    if icon then
        f.profIcon:SetTexture(icon)
        f.profIconHolder:Show()
        if f.profHint then f.profHint:Show() end
    end
end

if not skillLineID then

	AddLine("|cff66ccff• Выберите профессию из доступных персонажу.|r")
	AddLine("|cff4da6ff• Выберите эпоху профессии.|r")
	AddLine("|cff66ccff• Откройте профессию — появятся кнопки создания предметов.|r")
	AddLine("|cff66ff66• Используйте кнопки |cffffcc00«Предыдущий / Следующий»|r")
	AddLine("|cff66ff66для просмотра шагов руководства.|r")
	AddLine("|cffff9900• «Создать» - крафт 1 предмета.|r")
	AddLine("|cffff9900• «Создать всё» - крафт максимума.|r")
	AddLine("|cff66ff66• ЛКМ по кнопке с именем учителя - метка на карте.|r")
	AddLine("|cffffdd55  Не забывайте изучать новые рецепты из руководств.|r")
	AddLine("")
	AddLine("|cff66ff66Рекомендация:|r\nпосле классической эпохи изучать сразу катаклизм - учитель один и тот же.\n")
	AddLine("|cffffcc00Созданную броню и оружие пересылайте на своего инчантера - сэкономите на реагентах.|r")
	AddLine("")
	AddLine("|cff888888Alt + колесо - масштаб окна|r")
	AddLine("|cff888888Alt + клик колёсиком - сброс масштаба окна|r")

    if f.craftBtn then f.craftBtn:Hide() end
    if f.craftAllBtn then f.craftAllBtn:Hide() end
    if f.enchantTargetSlot then f.enchantTargetSlot:Hide() end

    UpdateBuyButtonVisibility()

    local contentHeight = ctx.y
    local footerHeight = 0
    if f.footer and f.footer:IsShown() then
        footerHeight = f.footer:GetHeight() + 6
    end

    f.content:SetHeight(contentHeight)

    local totalHeight =
        FRAME_PADDING_TOP
        + contentHeight
        + footerHeight
        + FRAME_PADDING_BOTTOM
        + 32

    local left = f:GetLeft()
    local top  = f:GetTop()

    f:SetHeight(math.max(totalHeight, FRAME_MIN_HEIGHT))

    if left and top then
        f:ClearAllPoints()
        f:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
    end

    return
end

    local step, skill, guide =
        G:GetCurrentStepBySkillLine(skillLineID)

	local autoIdx = G:GetAutoStepIndex(skillLineID)

	if G.state.manualStepIndex
	   and autoIdx
	   and G.state.manualStepIndex == autoIdx
	then
		G.state.manualStepIndex = nil
		skill = G:GetEffectiveSkillLevel(skillLineID)
	end
	
	local recipeVendor =
		step and G:GetRecipeVendorForStep(skillLineID, step)

	if not guide then
		guide = G:GetGuideBySkillLine(skillLineID)
	end
	local recipe = GetFirstLearnedRecipe(step)
	local recipeLearned = recipe ~= nil

	if G:IsGuideCompleted(skillLineID) then
		AddLine("Гайд завершён!", "|cff00ff00")
		AddLine("Вы достигли максимального навыка для этой эпохи.", "|cffaaaaaa")
		AddLine("")
		AddLine("Можно перейти к следующей эпохе профессии.", "|cff66ccff")

		if f.craftBtn then f.craftBtn:Hide() end
		if f.craftAllBtn then f.craftAllBtn:Hide() end
		if f.enchantTargetSlot then f.enchantTargetSlot:Hide() end

		UpdateBuyButtonVisibility()
		return
	end

	if not step then
		AddLine("Профессия этой эпохи не изучена", "|cffffffaa")
		AddLine("")

		local g = guide or G:GetGuideBySkillLine(skillLineID)

		if g then
			if g.learnViaScroll then
				AddLine(
					"Купите свиток обучения у продавца рядом с учителем:",
					"|cffaaaaaa"
				)
			elseif g.learnViaQuest then
				AddLine(
					"Начните делать квестовую цепочку для изучения этой эпохи:",
					"|cffaaaaaa"
				)
			else
				AddLine(
					"Изучите профессию у учителя:",
					"|cffaaaaaa"
				)
			end
		else
			AddLine(
				"Изучите профессию у учителя:",
				"|cffaaaaaa"
			)
		end

		if f.craftBtn then f.craftBtn:Hide() end
		if f.craftAllBtn then f.craftAllBtn:Hide() end
		if f.enchantTargetSlot then f.enchantTargetSlot:Hide() end

		if g then
			self:RenderTrainers(f, g, ctx)
		end

		return
	end

	if f.prevStepBtn and f.nextStepBtn and f.stepIndicator then
		local steps = G:GetStepsBySkillLine(skillLineID)
		local idx   = G:GetCurrentStepIndex(skillLineID)

		if steps and idx and steps[idx] then

			local total = #steps

			f.prevStepBtn:Show()
			f.nextStepBtn:Show()
			f.stepIndicator:Show()

			f.stepIndicator:SetText(
				string.format("Шаг %d / %d", idx, total)
			)

			if idx <= 1 then
				f.prevStepBtn:Disable()
				f.prevStepBtn.text:SetTextColor(0.5, 0.5, 0.5)
			else
				f.prevStepBtn:Enable()
				f.prevStepBtn.text:SetTextColor(1, 1, 1)
			end

			if idx >= total then
				f.nextStepBtn:Disable()
				f.nextStepBtn.text:SetTextColor(0.5, 0.5, 0.5)
			else
				f.nextStepBtn:Enable()
				f.nextStepBtn.text:SetTextColor(1, 1, 1)
			end
		else
			f.prevStepBtn:Hide()
			f.nextStepBtn:Hide()
			f.stepIndicator:Hide()
		end
		if f.resetStepBtn then
			local autoIdx = G:GetAutoStepIndex(skillLineID)

			if G.state.manualStepIndex
			   and autoIdx
			   and autoIdx ~= G.state.manualStepIndex
			then
				f.resetStepBtn:Show()
			else
				f.resetStepBtn:Hide()
			end

		end

	end

self:RenderHeader(f, guide, skill, step, recipeLearned, AddLine, ctx)
self:RenderNote(f, step, AddLine, ctx)
self:RenderRecipes(f, step, AddLine, ctx)
self:RenderMaterials(f, step, skill, AddLine, ctx, GetTextLine)

local stepRefs = G:GetStepNoteRefs(step)

if stepRefs then
    for _, ref in ipairs(stepRefs) do
        if ref.type == "npc" then
			AddLine("") 

			self:RenderRecipeVendor(
				f,
				{
					npcID = ref.npcID,
					name  = ref.name,
					mapID = ref.mapID,
					x     = ref.x,
					y     = ref.y,
					isDropSource = true, 
				},
				ctx
			)

			ctx.y = ctx.y + 6
			skipDefaultTrainers = true
        end
    end
end

if not skipDefaultTrainers then
    if recipeVendor then
        self:RenderRecipeVendor(f, recipeVendor, ctx)
    else
        self:RenderTrainers(f, guide, ctx)
    end
end

local showCraftButtons = G:IsProfessionWindowOpen(skillLineID)

local isEnchanting = false
if guide and guide.profession == "Enchanting" then
    if step and step.recipes and step.recipes[1] and step.recipes[1].spellID then
        isEnchanting = true
    end
end

if f.craftBtn and f.craftAllBtn then
    if showCraftButtons and recipeLearned then

        if step and step.disablesCraft then
            f.craftBtn:Hide()
        else
            f.craftBtn:Show()
        end

        if step and step.disablesCraftAll then
            f.craftAllBtn:Hide()
        else
            f.craftAllBtn:Show()
        end

    else
        f.craftBtn:Hide()
        f.craftAllBtn:Hide()
        if f.enchantTargetSlot then f.enchantTargetSlot:Hide() end
    end
end

local contentHeight = ctx.y
local footerHeight = 0
if f.footer and f.footer:IsShown() then
    footerHeight = f.footer:GetHeight() + 6 
end

f.content:SetHeight(contentHeight)

local totalHeight =
    FRAME_PADDING_TOP
    + contentHeight
    + footerHeight
    + FRAME_PADDING_BOTTOM
    + 32

local left = f:GetLeft()
local top  = f:GetTop()

f:SetHeight(math.max(totalHeight, FRAME_MIN_HEIGHT))

if left and top then
    f:ClearAllPoints()
    f:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
end

UpdateBuyButtonVisibility()

end

function UI:RenderHeader(f, guide, skill, step, recipeLearned, AddLine, ctx)
    AddLine(guide.name, "|cffffff00")
	if step and step.experiment then
		AddLine("Тип шага: эксперимент", "|cff66ccff")
	end

	if step and step.recipes and step.recipes[1] then
		local skillLineID = G:GetSelectedSkillLine()
		local remaining = G:GetRemainingCraftsForStep(skillLineID, step)
		local total = step.recipes[1].crafts or 0

		local skill = G:GetSkillForStep(skillLineID, step)
		local target = step.max or "?"

		AddLine(
			"Осталось создать: "
			.. (remaining > 0 and "|cff66ccff" or "|cff00ff00")
			.. remaining .. " / " .. total .. "|r"
			.. "  |cffaaaaaa(до навыка " .. target .. ")|r"
		)
	end

	if recipeLearned == false then
		local questRecipe = false

		if step and step.recipes then
			for _, r in ipairs(step.recipes) do
				if r.recipeSource == "quest" then
					questRecipe = true
					break
				end
			end
		end

		if not questRecipe then
			AddLine("|cffff5555Рецепт не изучен!|r")
			AddLine("|cffaaaaaaИзучите рецепт|r")
		end
	end

    if skill == false then
        AddLine("Ручной просмотр шага", "|cffaaaaaa")
    elseif type(skill) == "number" then
        local eff, real, bonus = G:GetEffectiveSkillLevel(G:GetSelectedSkillLine())

		if bonus > 0 then
			AddLine("Уровень навыка: "..real.." |cff66ccff(+"..bonus.." раса)|r", "|cffaaaaaa")
		else
			AddLine("Уровень навыка: "..real, "|cffaaaaaa")
		end
    end
end

function UI:RenderNote(f, step, AddLine, ctx)
    if not step then return end

    local note = step.note
    if not note or note == "" then return end
	local refs = BuildNoteRefs(step)

    ctx.y = ctx.y + 8

	local line = GetTextLine(f, ctx.i)
	line:ClearAllPoints()
	line:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)
	line:SetWidth(220)
	line:SetHeight(16)   
	line.text:SetWidth(200) 
	line.text:SetWordWrap(true)

    if not line.icon then
        line.icon = line:CreateTexture(nil, "OVERLAY")
        line.icon:SetSize(20, 20)
        line.icon:SetPoint("LEFT", 0, 0)
        line.icon:SetTexture("Interface\\COMMON\\help-i")
		line.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)

    end
    line.icon:Show()

    line.text:SetPoint("LEFT", line.icon, "RIGHT", 2, 0)
    line.text:SetText("|cff66ccffЗаметка|r")

    line.itemID = nil
    line.itemLink = nil
    line:Show()

	local h = line.text:GetStringHeight()
	line:SetHeight(h)
	ctx.y = ctx.y + h + LINE_SPACING
	ctx.i = ctx.i + 1

	for textLine in string.gmatch(note, "[^\n]+") do
		local l = GetTextLine(f, ctx.i)
		l:ClearAllPoints()

		l:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)
		l:SetWidth(TEXT_SAFE_WIDTH)

		l.text:ClearAllPoints()
		l.text:SetPoint("LEFT", 0, 0)
		l.text:SetWidth(TEXT_SAFE_WIDTH)
		l.text:SetWordWrap(true)

		l.text:SetWordWrap(true)
		l.text:SetText("|cffaaaaaa"..textLine.."|r")

		l.itemID = nil
		l.itemLink = nil
		l:Show()

		local h = l.text:GetStringHeight()
		l:SetHeight(h)

		ctx.y = ctx.y + h
		ctx.i = ctx.i + 1
	end
    if refs then
        ctx.y = ctx.y + 4
        for _, text in ipairs(refs) do
            AddLine(text, "|cff66ccff")
        end
    end

    AddLine("")
end


function UI:RenderRecipes(f, step, AddLine, ctx)
	ctx.y = ctx.y + 10
    AddLine("Сейчас крафтить:", "|cffffffaa")

    for _, r in ipairs(step.recipes or {}) do
        local recipeName

        if r.spellID and C_Spell and C_Spell.GetSpellInfo then
            local info = C_Spell.GetSpellInfo(r.spellID)
            recipeName = info and info.name
        elseif r.name then
            recipeName = r.name
        end

        if recipeName then
            AddLine(" • "..recipeName, "|cff66ccff")
        end
    end

    AddLine("")
end

function UI:RenderMaterials(f, step, skill, AddLine, ctx, GetTextLine)
	ctx.y = ctx.y + 12
    AddLine("Список материалов:", "|cffffffaa")

    local recipe = step.recipes and step.recipes[1]
    if not recipe then
        AddLine("")
        return
    end
	if recipe.reagentsAny then
		for i, r in ipairs(recipe.reagentsAny) do
			local need = r.count or 0
			local have = G:GetItemCountTotal(r.itemID)

			local line = GetTextLine(f, ctx.i)
			line:ClearAllPoints()
			line:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)

			local link = GetCachedItemLink(r.itemID)
			local color = have >= need and "|cff00ff00" or "|cffff5555"
			local prefix = (i == 1) and " • " or "   ИЛИ "

			line.text:SetText(
				prefix .. link .. " " .. color .. "× " .. need .. "|r  |cffaaaaaa(есть: "..have..")|r"
			)

			line.itemID   = r.itemID
			line.itemLink = link
			line:Show()

			local h = line.text:GetStringHeight()
			line:SetHeight(h)
			ctx.y = ctx.y + h + LINE_SPACING
			ctx.i = ctx.i + 1

		end

		AddLine("")
		return
	end

    local skillLineID = G:GetSelectedSkillLine()
	
	local crafts = G:GetRemainingCraftsForStep(
		G:GetSelectedSkillLine(),
		step
	)

    for _, r in ipairs(recipe.reagents or {}) do
        local need = math.max(0, (r.count or 0) * crafts)

        if r.anyOf and r.isMilling then
            local totalHave = 0

            for _, opt in ipairs(r.anyOf) do
                totalHave = totalHave + G:GetItemCountTotal(opt.itemID)
            end

			local needColor  = totalHave >= need and "|cff00ff00" or "|cffff5555"
			local haveColor  = totalHave >= need and "|cff00ff00" or "|cffff5555"

            local line = GetTextLine(f, ctx.i)
            line:ClearAllPoints()
            line:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)

			line.text:SetText(
				" • Любая трава × "
				.. needColor .. need .. "|r  "
				.. haveColor .. "(суммарно есть: "..totalHave..")|r"
			)

            line.itemID   = nil
            line.itemLink = nil
            line:Show()

			line:SetWidth(TEXT_SAFE_WIDTH)
			line.text:SetWidth(TEXT_SAFE_WIDTH)
			line.text:SetWordWrap(true)

			local h = line.text:GetStringHeight()
			line:SetHeight(h)

			ctx.y = ctx.y + h + LINE_SPACING
			ctx.i = ctx.i + 1

            for _, opt in ipairs(r.anyOf) do
                local have = G:GetItemCountTotal(opt.itemID)
                local link = GetCachedItemLink(opt.itemID)

                local sub = GetTextLine(f, ctx.i)
                sub:ClearAllPoints()
                sub:SetPoint("TOPLEFT", f.content, "TOPLEFT", 12, -ctx.y)

                local haveColor = have >= need and "|cff00ff00" or "|cffff5555"

				sub.text:SetText(
					"   - " .. link .. " "
					.. haveColor .. "(есть: "..have..")|r"
				)

                sub.itemID   = opt.itemID
                sub.itemLink = link
                sub:Show()

				sub:SetWidth(TEXT_SAFE_WIDTH - 12)
				sub.text:SetWidth(TEXT_SAFE_WIDTH - 12)
				sub.text:SetWordWrap(true)

				local h = sub.text:GetStringHeight()
				sub:SetHeight(h)

				ctx.y = ctx.y + h + LINE_SPACING
				ctx.i = ctx.i + 1

            end

        elseif r.anyOf then
            local maxHave = 0
            local bestItemID

            for _, opt in ipairs(r.anyOf) do
                local have = G:GetItemCountTotal(opt.itemID)
                if have > maxHave then
                    maxHave = have
                    bestItemID = opt.itemID
                end
            end

            if not bestItemID and r.anyOf[1] then
                bestItemID = r.anyOf[1].itemID
            end

            local link = bestItemID
                and GetCachedItemLink(bestItemID)
                or "любой грейд"

            local color = maxHave >= need and "|cff00ff00" or "|cffff5555"

            local line = GetTextLine(f, ctx.i)
			line:SetWidth(TEXT_SAFE_WIDTH)
			line.text:SetWidth(TEXT_SAFE_WIDTH)
			line.text:SetWordWrap(true)
            line:ClearAllPoints()
            line:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)

            line.text:SetText(
                " • " .. link .. " " .. color .. "× " .. need ..
                "|r  |cff00ff00(самый дешёвый)|r"
            )

            line.itemID   = bestItemID
            line.itemLink = link
            line:Show()

			local h = line.text:GetStringHeight()
			line:SetHeight(h)

			ctx.y = ctx.y + h + LINE_SPACING
			ctx.i = ctx.i + 1

        else
            local have = G:GetItemCountTotal(r.itemID)
            local link = GetCachedItemLink(r.itemID)
            local color = have >= need and "|cff00ff00" or "|cffff5555"

            local line = GetTextLine(f, ctx.i)
            line:ClearAllPoints()
            line:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)

			local haveColor = have >= need and "|cff00ff00" or "|cffff5555"

			line.text:SetText(
				" • " .. link .. " "
				.. color .. "× " .. need .. "|r  "
				.. haveColor .. "(есть: "..have..")|r"
			)

            line.itemID   = r.itemID
            line.itemLink = link
            line:Show()

			local h = line.text:GetStringHeight()
			line:SetHeight(h)

			ctx.y = ctx.y + h + LINE_SPACING
			ctx.i = ctx.i + 1
        end
    end

    AddLine("")

end

local function CreateTrainerButton(parent)
    local b = CreateFrame("Button", nil, parent)
    b:SetSize(230, 20)
    b:EnableMouse(true)
    CreateButtonBorder(b)

    b.bg = b:CreateTexture(nil, "BACKGROUND")
    b.bg:SetAllPoints()
    b.bg:SetColorTexture(1, 1, 1, 0)

    b:SetScript("OnEnter", function(self)
        self.bg:SetColorTexture(1, 1, 1, 0.08)
        if self.border then
            self.border:SetBackdropBorderColor(1, 1, 1, 0.35)
        end
    end)

    b:SetScript("OnLeave", function(self)
        self.bg:SetColorTexture(1, 1, 1, 0)
        if self.border then
            self.border:SetBackdropBorderColor(1, 1, 1, 0.15)
        end
    end)

    b:SetScript("OnClick", function(self)
        Waypoints:SetTrainerWaypoint(self._waypoint)
    end)

    b.text = b:CreateFontString(nil, "OVERLAY")
    b.text:SetFont(FONT, 12)
    b.text:SetPoint("LEFT", 6, 0)

    return b
end

function UI:RenderRecipeVendor(f, vendor, ctx)
    if f.trainerButtons then
        for _, b in ipairs(f.trainerButtons) do
            b:Hide()
        end
    end

    if f.trainersHeader then
        f.trainersHeader:Hide()
    end

    ctx.y = ctx.y + 12
    f.trainersHeader:ClearAllPoints()
    f.trainersHeader:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)
    if vendor.isDropSource then
		f.trainersHeader:SetText("Источник рецепта:")
	else
		f.trainersHeader:SetText("Продавец рецепта:")
	end

    f.trainersHeader:Show()

    ctx.y = ctx.y + 18

    local b = f.trainerButtons[1]

    if not b then
        b = CreateTrainerButton(f.content)
        f.trainerButtons[1] = b
    end

    b:ClearAllPoints()
    b:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)
    b.text:SetText(" " .. (vendor.name or ("NPC " .. (vendor.npcID or "?"))))
    b._waypoint = vendor

    b:Show()
    ctx.y = ctx.y + 22
end

function UI:RenderTrainers(f, guide, ctx)
    if f.trainerButtons then
        for _, b in ipairs(f.trainerButtons) do
            b:Hide()
        end
    end
    if f.trainersHeader then
        f.trainersHeader:Hide()
    end

    if not guide or not guide.trainers then return end

    local faction = (G.GetPlayerFaction and G:GetPlayerFaction())
    if not faction then return end

    local trainers = guide.trainers[faction]
    if not trainers or #trainers == 0 then return end

    ctx.y = ctx.y + 12
    f.trainersHeader:ClearAllPoints()
    f.trainersHeader:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)
    f.trainersHeader:Show()

    ctx.y = ctx.y + 18

    for idx, t in ipairs(trainers) do
        local b = f.trainerButtons[idx]

        if not b then
            b = CreateTrainerButton(f.content)
            f.trainerButtons[idx] = b
        end

        b:ClearAllPoints()
        b:SetPoint("TOPLEFT", f.content, "TOPLEFT", 0, -ctx.y)
        b.text:SetText(" " .. (t.name or ("NPC " .. (t.npcID or "?"))))
        b._waypoint = t

        b:Show()
        ctx.y = ctx.y + 22
    end
end

SLASH_AHUIPG1 = "/pg"
SlashCmdList.AHUIPG = function()
    local f = UI.frame or UI:Create()
	if f:IsShown() then
		f:Hide()
	else
		f:Show()
		UpdateBuyButtonVisibility()
		UI:Refresh()
	end

end

UI.frame = UI:Create()
UI.frame.epochBtn:SetScript("OnClick", function(self)
    ShowEpochMenu(self)
end)
UI.frame.profBtn:SetScript("OnClick", function(self)
    ShowProfessionMenu(self)
end)
Shopping.OnAuctionStateChanged = function()
    UpdateBuyButtonVisibility()
end

local pgWatcher = CreateFrame("Frame")

	pgWatcher:RegisterEvent("SKILL_LINES_CHANGED")
	pgWatcher:RegisterEvent("TRADE_SKILL_DATA_SOURCE_CHANGED")
	pgWatcher:RegisterEvent("TRADE_SKILL_CLOSE")
	pgWatcher:RegisterEvent("BAG_UPDATE_DELAYED")
	pgWatcher:RegisterEvent("PLAYER_REGEN_ENABLED")
	pgWatcher:RegisterEvent("NEW_RECIPE_LEARNED")
	pgWatcher:RegisterEvent("TRADE_SKILL_LIST_UPDATE")

pgWatcher:SetScript("OnEvent", function(_, event)
    if not UI.frame or not UI.frame:IsShown() then return end

    local skillLineID = G:GetSelectedSkillLine()
    if not skillLineID then return end

    if event == "NEW_RECIPE_LEARNED"
       or event == "TRADE_SKILL_LIST_UPDATE"
       or event == "TRADE_SKILL_DATA_SOURCE_CHANGED"
    then
        C_Timer.After(0, function()
            if not UI.frame or not UI.frame:IsShown() then return end

            G:ValidateManualStep(skillLineID)
            State:Save()
            UI:Refresh()
        end)

        return
    end

    G:ValidateManualStep(skillLineID)
    State:Save()
    UI:Refresh()
end)

local scaleLoader = CreateFrame("Frame")
scaleLoader:RegisterEvent("PLAYER_LOGIN")

scaleLoader:SetScript("OnEvent", function()
    if not UI.frame then return end

    local cfg = GetPGConfig()
    local scale = cfg.scale or DEFAULT_SCALE

    UI.frame:SetScale(scale)
end)