local PT = AhUI
local L = PT.L
local Tracker = PT.Tracker
local Tooltip = PT.Tooltip

local function GetProfessionFilters()
    AhUI_CharConfig = AhUI_CharConfig or {}
    AhUI_CharConfig.professionFilters = AhUI_CharConfig.professionFilters or {
        uniqueTreasure = true,
        uniqueBook     = true,
        weekly         = true,
        catchUp        = true,
        treatise       = true,
        onlyMap        = false,
    }
    return AhUI_CharConfig.professionFilters
end

local CHECKBOX_ON  = "Interface\\Buttons\\UI-CheckBox-Check"
local CHECKBOX_OFF = "Interface\\Buttons\\UI-CheckBox-Up"

local function CheckboxItem(label, value)
    return {
        text = label,
        icon = value and CHECKBOX_ON or CHECKBOX_OFF,
        iconSize = 14,
    }
end

local function GetActivePreset()
    local F = GetProfessionFilters()

    if F.onlyMap then
        return "only_map"
    end

    if F.weekly
       and not F.uniqueTreasure
       and not F.uniqueBook
       and not F.catchUp
       and not F.treatise
    then
        return "only_weekly"
    end

    if F.uniqueTreasure
       and F.uniqueBook
       and F.weekly
       and F.catchUp
       and F.treatise
       and not F.onlyMap
    then
        return "all"
    end

    return nil
end

local function ApplyFilterPreset_All()
    local F = GetProfessionFilters()
    F.uniqueTreasure = true
    F.uniqueBook     = true
    F.weekly         = true
    F.catchUp        = true
    F.treatise       = true
    F.onlyMap        = false
end

local function ApplyFilterPreset_OnlyMap()
    local F = GetProfessionFilters()
    F.uniqueTreasure = true
    F.uniqueBook     = true
    F.weekly         = true
    F.catchUp        = true
    F.treatise       = true
    F.onlyMap        = true
end

local function ApplyFilterPreset_OnlyWeekly()
    local F = GetProfessionFilters()
    F.uniqueTreasure = false
    F.uniqueBook     = false
    F.weekly         = true
    F.catchUp        = false
    F.treatise       = false
    F.onlyMap        = false
end

PT.collapsedProfessions = PT.collapsedProfessions or {}

local ICON_DB = {
    DF  = "Interface\\AddOns\\AhUI\\media\\DF.tga",
    TWW = "Interface\\AddOns\\AhUI\\media\\TWW.tga",
}

if PT.IS_MID_AVAILABLE then
    ICON_DB.MID = "Interface\\AddOns\\AhUI\\media\\MID.tga"
end

local FONT = "Interface\\AddOns\\AhUI\\fonts\\GothamXNarrow-Medium.ttf"
local DEFAULT_SCALE = 1.0
local SCALE_STEP    = 0.05
local SCALE_MIN     = 0.7
local SCALE_MAX     = 1.4
local pendingRowsByItemId = {}

local function NormalizeItemId(v)
    if type(v) == "number" then
        return v
    end
    if type(v) == "table" then
        if type(v.id) == "number" then return v.id end
        if type(v[1]) == "number" then return v[1] end
    end
    return nil
end

local refreshQueued = false
local function QueueRefreshUI()
    if refreshQueued then return end
    refreshQueued = true
    C_Timer.After(0, function()
        refreshQueued = false
        if PT.RefreshUI then
            PT:RefreshUI()
        end
    end)
end

local itemEventFrame = CreateFrame("Frame")
itemEventFrame:RegisterEvent("ITEM_DATA_LOAD_RESULT")
itemEventFrame:SetScript("OnEvent", function(_, _, itemID, success)
    if not success then return end

    local rows = pendingRowsByItemId[itemID]
    if not rows then return end

    local name = C_Item.GetItemNameByID(itemID) or GetItemInfo(itemID)
    local icon = C_Item.GetItemIconByID(itemID)

    for i = #rows, 1, -1 do
        local row = rows[i]
        if not row or not row:IsShown() then
            table.remove(rows, i)
        else
            if name and row.text then
                row.text:SetText(name)
            end
            if icon and row.icon then
                row.icon:SetTexture(icon)
            end

            local item = row._ahuiItem
            if item and item.MeetRequirements and row.text and row.value then
                local ok = item:MeetRequirements()
                if ok then
                    row.text:SetTextColor(1, 1, 1)
                else
                    row.text:SetTextColor(1, 0.2, 0.2)
                end

                local val = item.GetRemainingKnowledgePoints and item:GetRemainingKnowledgePoints() or 0
                if val > 0 then
                    row.value:SetText("+" .. val)
                else
                    row.value:SetText("")
                end
            end
        end
    end

    if #rows == 0 then
        pendingRowsByItemId[itemID] = nil
    end
end)

local mapEventFrame = CreateFrame("Frame")
mapEventFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
mapEventFrame:RegisterEvent("ZONE_CHANGED")
mapEventFrame:RegisterEvent("ZONE_CHANGED_INDOORS")

mapEventFrame:SetScript("OnEvent", function()
        QueueRefreshUI()
end)

local function WarmupItemsForActiveDB()
    if not AhUI or not AhUI.professions then return end

    for _, prof in pairs(AhUI.professions) do
        if prof.entries then
            for _, entry in ipairs(prof.entries) do
                local id = NormalizeItemId(entry.itemId)
                if id then
                    C_Item.RequestLoadItemDataByID(id)
                end
            end
        end
    end
end

local function WarmupProfessionInfo()
    if not AhUI or not AhUI.professions then return end

    for _, p in pairs(AhUI.professions) do
        C_TradeSkillUI.GetProfessionInfoBySkillLineID(p.id)
        C_TradeSkillUI.GetChildProfessionInfo(p.id)
        C_TradeSkillUI.GetTradeSkillTexture(p.id)
    end
end

local function ApplyBackdrop(frame, r, g, b, a)
    frame.bg = frame:CreateTexture(nil, "BACKGROUND")
    frame.bg:SetAllPoints()
    frame.bg:SetColorTexture(r, g, b, a)
end

local function GetItemDistanceSq(item)
    if not item
       or not item.waypoint
       or not item.waypoint.map
       or not item.waypoint.x
       or not item.waypoint.y
    then
        return nil
    end

    local mapID = C_Map.GetBestMapForUnit("player")
    if not mapID or mapID ~= item.waypoint.map then
        return nil
    end

    local pos = C_Map.GetPlayerMapPosition(mapID, "player")
    if not pos then return nil end

    local px, py = pos:GetXY()
    local dx = px - item.waypoint.x
    local dy = py - item.waypoint.y

    return dx * dx + dy * dy
end

local function SortItems(a, b)
end

local function SortItems(a, b)
    local aDist = a._distSq
    local bDist = b._distSq

    local aOnMap = aDist ~= nil
    local bOnMap = bDist ~= nil

    if aOnMap ~= bOnMap then
        return aOnMap
    end

    if aOnMap and bOnMap and aDist ~= bDist then
        return aDist < bDist
    end

    local A = a.GetType and a:GetType() or 99
    local B = b.GetType and b:GetType() or 99
    if A ~= B then return A < B end

    return (a:GetName() or "") < (b:GetName() or "")
end


local function SavePosition(frame)
    AhUI_DB = AhUI_DB or {}
    AhUI_DB.positions = AhUI_DB.positions or {}

    local left = frame:GetLeft()
    local top  = frame:GetTop()
    if not left or not top then return end

    local uiTop = UIParent:GetTop()
    local x = left
    local y = top - uiTop

    AhUI_DB.positions.professions = { x = x, y = y }
end

local function LoadPosition(frame)
    AhUI_DB = AhUI_DB or {}
    AhUI_DB.positions = AhUI_DB.positions or {}

    frame:ClearAllPoints()

    local p = AhUI_DB.positions.professions
    if p and p.x and p.y then
        frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", p.x, p.y)
    else
        frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 20, -20)
    end
end

local function CreateDBSelector(frame)
    local btn = CreateFrame("Button", nil, frame)
    btn:SetPoint("TOPRIGHT", -2, -5)
    btn:SetSize(45, 28)
    btn:RegisterForClicks("LeftButtonUp", "RightButtonUp")

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

    local mask = btn:CreateMaskTexture()
    mask:SetTexture(
        "Interface\\CharacterFrame\\TempPortraitAlphaMask",
        "CLAMPTOBLACKADDITIVE",
        "CLAMPTOBLACKADDITIVE"
    )
    mask:SetAllPoints(btn.icon)
    btn.icon:AddMaskTexture(mask)

    function btn:Update()
        local db = AhUI_CharConfig.activeDB or "DF"
        btn.icon:SetTexture(ICON_DB[db])
    end

    function btn:UpdateTooltip()
        Tooltip:Clear()
        Tooltip:AddLine("Database", 14, 1, 1, 1)
        Tooltip:AddLine(" ")

        local db = AhUI_CharConfig.activeDB or "DF"

        if db == "MID" and PT.IS_MID_AVAILABLE then
            Tooltip:AddLine("Midnight", 12, 0.7, 0.6, 1)
        elseif db == "TWW" then
            Tooltip:AddLine("The War Within", 12, 1, 0.6, 0.2)
        else
            Tooltip:AddLine("Dragonflight", 12, 0.6, 0.8, 1)
        end

        Tooltip:ShowAtCursor()
    end

    btn:SetScript("OnClick", function(self, mouseButton)
        local list = { "DF", "TWW" }
        if PT.IS_MID_AVAILABLE then
            table.insert(list, "MID")
        end

        local cur = AhUI_CharConfig.activeDB or "DF"
        local pos = 1
        for i, v in ipairs(list) do
            if v == cur then
                pos = i
                break
            end
        end

        if mouseButton == "RightButton" then
            pos = pos - 1
            if pos < 1 then pos = #list end
        else
            pos = pos + 1
            if pos > #list then pos = 1 end
        end

        local newDB = list[pos]
        AhUI_CharConfig.activeDB = newDB
        PT.config.activeDB = newDB

        PT.LoadActiveDB()
        PT:RefreshData()

        C_Timer.After(0, function()
            WarmupProfessionInfo()
            WarmupItemsForActiveDB()
            C_Timer.After(0.1, function()
                QueueRefreshUI()
            end)
        end)

        self:Update()
        if PT.UpdateAllProfessionNames then
            PT.UpdateAllProfessionNames()
        end

        QueueRefreshUI()

        if self:IsMouseOver() then
            self:UpdateTooltip()
        end
    end)

    btn:SetScript("OnEnter", function()
        btn:UpdateTooltip()
    end)

    btn:SetScript("OnLeave", function()
        Tooltip:Clear()
    end)

    btn:Update()
    frame.dbSelector = btn
end

local UI = {}
PT.UI = UI

local function PreviewOrSaved(cfg, key)
    if cfg.__preview and cfg.__preview[key] then
        return cfg.__preview[key]
    end
    return cfg[key]
end

function UI:ApplyCustomization()

    if not self.frame then return end
    if not PT.config or not PT.config.customization then return end

    local cfg = PT.config.customization.professionsWindow
    if not cfg then return end

    local font     = cfg.font
    local fontSize = tonumber(cfg.fontSize) or 11
	if fontSize <= 0 then fontSize = 11 end
    local fontColor = (cfg.__preview and cfg.__preview.fontColor) or cfg.fontColor

    local bg = (cfg.__preview and cfg.__preview.backgroundColor) or cfg.backgroundColor
    if self.frame.bg and bg then
        self.frame.bg:SetColorTexture(bg.r, bg.g, bg.b, bg.a)
    end

    local border = (cfg.__preview and cfg.__preview.borderColor) or cfg.borderColor
    if border then
        self.frame:SetBackdropBorderColor(border.r, border.g, border.b, border.a)
    end

    if self.frame.title and font then
		local _, defaultSize, flags = self.frame.title:GetFont()

		if not defaultSize or defaultSize <= 0 then
			defaultSize = 14
		end

		self.frame.title:SetFont(font, defaultSize, flags)
        if fontColor then
            self.frame.title:SetTextColor(
                fontColor.r, fontColor.g, fontColor.b, fontColor.a
            )
        end
    end

		local headers = PT.UI and PT.UI.headers
		if headers then
			for _, header in ipairs(headers) do

            local h = (cfg.__preview and cfg.__preview.headerColor) or cfg.headerColor
            if h then
                header:SetBackdropColor(h.r, h.g, h.b, h.a)
            end

            if header.text and font then
                header.text:SetFont(font, fontSize)
                if fontColor then
                    header.text:SetTextColor(
                        fontColor.r, fontColor.g, fontColor.b, fontColor.a
                    )
                end
            end
        end
    end

	if self.itemRows then
		for _, row in ipairs(self.itemRows) do
			if row.text and font then
				row.text:SetFont(font, fontSize)
			end

			if row.value and font then
				row.value:SetFont(font, fontSize)
			end
		end
	end
end

function UI:SetScale(delta)
    AhUI_Config = AhUI_Config or {}
    AhUI_Config.professionsScale =
        AhUI_Config.professionsScale or DEFAULT_SCALE

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

    AhUI_Config.professionsScale = nextScale

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

function UI:ResetScale()
    AhUI_Config = AhUI_Config or {}
    AhUI_Config.professionsScale = DEFAULT_SCALE

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

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

    local f = CreateFrame("Frame", "AhUIFrame", UIParent, "BackdropTemplate")
    f:SetSize(330, 70)
	AhUI_Config = AhUI_Config or {}
	local scale = AhUI_Config.professionsScale or DEFAULT_SCALE
	f:SetScale(scale)

    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 2,
    })
    f:SetBackdropColor(0, 0, 0, 0)
    f:SetBackdropBorderColor(0, 0, 0, 1)

    ApplyBackdrop(f, 0, 0, 0, 0.55)

    LoadPosition(f)

    f:SetMovable(true)
    f:EnableMouse(true)
	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:RegisterForDrag("LeftButton")

    f:SetScript("OnDragStart", function(self)
        self:StartMoving()
    end)

    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()

        local left = self:GetLeft()
        local top  = self:GetTop()
        local uiTop = UIParent:GetTop()

        self:ClearAllPoints()
        self:SetPoint("TOPLEFT", UIParent, "TOPLEFT", left, top - uiTop)

        SavePosition(self)
    end)

    f:SetClampedToScreen(true)

    local title = f:CreateFontString(nil, "OVERLAY")
    title:SetFont(FONT, 14)
    title:SetPoint("TOPLEFT", 10, -10)
    title:SetJustifyH("LEFT")
    title:SetText(L["PROF_WINDOW_TITLE"])
    f.title = title

	local filterBtn = CreateFrame("Button", nil, f)
	filterBtn:SetSize(18, 18)
	filterBtn:SetPoint("TOPRIGHT", -54, -8)

	filterBtn.icon = filterBtn:CreateTexture(nil, "ARTWORK")
	filterBtn.icon:SetAllPoints()
	filterBtn.icon:SetAtlas("common-search-magnifyingglass")

	filterBtn:SetScript("OnEnter", function(self)
    local T = PT.Tooltip
    if not T then return end

    T:Clear()
    T:AddLine(L["FILTER_TOOLTIP_TITLE"] or "Filters", 14, 1, 1, 1)
    T:AddLine(
        L["FILTER_TOOLTIP_HINT"] or "Configure what is shown in the list",
        12, 0.8, 0.8, 0.8
    )

    T:ShowAt(self, "TOPLEFT", "TOPRIGHT", 8, 0)
end)

filterBtn:SetScript("OnLeave", function()
    if PT.Tooltip then
        PT.Tooltip:Clear()
    end
end)

filterBtn:SetScript("OnClick", function(self)
    local F = GetProfessionFilters()
    local activePreset = GetActivePreset()

    PT.Dropdown:Show({
        owner = self,
        items = {
            { header = true, text = L["FILTER_PRESETS"] or "Presets:" },

            {
                text = L["FILTER_PRESET_ALL"] or "All",
                icon = (activePreset == "all") and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    ApplyFilterPreset_All()
                    QueueRefreshUI()
					onClick = function()
						F.weekly = not F.weekly
						QueueRefreshUI()
					end
                end,
            },
            {
                text = L["FILTER_PRESET_ONLY_MAP"] or "Only this map",
                icon = (activePreset == "only_map") and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    ApplyFilterPreset_OnlyMap()
                    QueueRefreshUI()
					onClick = function()
						F.weekly = not F.weekly
						QueueRefreshUI()
					end
                end,
            },
            {
                text = L["FILTER_PRESET_ONLY_WEEKLY"] or "Only weekly",
                icon = (activePreset == "only_weekly") and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    ApplyFilterPreset_OnlyWeekly()
                    QueueRefreshUI()
					onClick = function()
						F.weekly = not F.weekly
						QueueRefreshUI()
					end
                end,
            },

            { header = true, text = L["FILTER_SHOW"] or "Show:" },

            {
                text     = L["FILTER_UNIQUE_TREASURE"] or "Unique Treasures",
                icon     = F.uniqueTreasure and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    F.uniqueTreasure = not F.uniqueTreasure
                    QueueRefreshUI()
					onClick = function()
						F.weekly = not F.weekly
						QueueRefreshUI()
					end
                end,
            },
            {
                text     = L["FILTER_UNIQUE_BOOK"] or "Unique Books",
                icon     = F.uniqueBook and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    F.uniqueBook = not F.uniqueBook
                    QueueRefreshUI()
					onClick = function()
						F.weekly = not F.weekly
						QueueRefreshUI()
					end
                end,
            },
            {
                text     = L["FILTER_WEEKLY"] or "Weekly",
                icon     = F.weekly and CHECKBOX_ON or CHECKBOX_OFF,
                iconSize = 14,
                onClick = function()
                    F.weekly = not F.weekly
                    QueueRefreshUI()
					onClick = function()
						F.weekly = not F.weekly
						QueueRefreshUI()
					end
                end,
            },
        }
    })
end)

    CreateDBSelector(f)

    local content = CreateFrame("Frame", nil, f)
    content:SetPoint("TOPLEFT", 10, -35)
    content:SetPoint("TOPRIGHT", -10, -35)
    content:SetHeight(1)

    self.content = content
    self.frame   = f
	PT:RegisterHideInCombat(f, "hideProfessionsInCombat")
    if PT.config.showProfessionsWindow == false then
        f:Hide()
    end

    return f
end

local function CreateProfessionHeader(parent, profession)
    local header = CreateFrame("Button", nil, parent, "BackdropTemplate")
    header:SetSize(310, 20)

    header:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",

    })
    header:SetBackdropColor(0.10, 0.10, 0.10, 0.85)

    header.icon = header:CreateTexture(nil, "ARTWORK")
    header.icon:SetSize(16, 16)
    header.icon:SetPoint("LEFT", 4, 0)
    header.icon:SetTexture(profession.icon)

    header.text = header:CreateFontString(nil, "OVERLAY")
    header.text:SetFont(FONT, 12)
    header.text:SetPoint("LEFT", header, "LEFT", 24, 0)
    header.text:SetJustifyH("LEFT")
    header.text:SetText(profession.name or "Profession")

    header.kpText = header:CreateFontString(nil, "OVERLAY")
    header.kpText:SetFont(FONT, 11)
    header.kpText:SetPoint("RIGHT", header, "RIGHT", -20, 0)
    header.kpText:SetJustifyH("RIGHT")
    header.kpText:SetTextColor(0, 1, 0)

    local totalKP = 0
    for _, entry in ipairs(profession.entries or {}) do
        if entry.GetRemainingKnowledgePoints
           and entry.MeetRequirements
           and entry:MeetRequirements()
        then
            local val = entry:GetRemainingKnowledgePoints()
            if val and val > 0 then
                totalKP = totalKP + val
            end
        end
    end

    if totalKP > 0 then
        header.kpText:SetText("+" .. totalKP)
    else
        header.kpText:SetText("")
    end

	header.toggleText = header:CreateFontString(nil, "OVERLAY")
	header.toggleText:SetFont(FONT, 14, "OUTLINE")
	header.toggleText:SetPoint("RIGHT", header, "RIGHT", -6, 0)
	header.toggleText:SetJustifyH("RIGHT")
	header.toggleText:SetTextColor(1, 1, 1)

	header:SetScript("OnClick", function()
		local willExpand = not profession.expanded

		local profs = PT.GetProfessions(true)
		for _, p in pairs(profs) do
			p.expanded = false
			PT.collapsedProfessions[p.id] = true
		end

		profession.expanded = willExpand
		PT.collapsedProfessions[profession.id] = not willExpand

		if willExpand then
			header.toggleText:SetText("−")

			for _, entry in ipairs(profession.entries or {}) do
				local id = NormalizeItemId(entry.itemId)
				if id then
					C_Item.RequestLoadItemDataByID(id)
				end
			end
		else
			header.toggleText:SetText("+")
		end

		QueueRefreshUI()
	end)

	if profession.expanded then
		header.toggleText:SetText("−")
	else
		header.toggleText:SetText("+")
	end

	PT.UI.headers = PT.UI.headers or {}
	table.insert(PT.UI.headers, header)

    return header
end

local function CreateItemRow(parent, item)
    if not item or not item.profession then
        return nil
    end

    local row = CreateFrame("Button", nil, parent)
    row:SetSize(310, 18)
    row._ahuiItem = item

    row.bg = row:CreateTexture(nil, "BACKGROUND")
    row.bg:SetAllPoints()
    row.bg:SetColorTexture(1, 0.8, 0, 0.25)
    row.bg:SetShown(item.isTracked)

local id = NormalizeItemId(item.itemId)
	if id then
		C_Item.RequestLoadItemDataByID(id)
	end

    row.iconBtn = CreateFrame("Button", nil, row)
    row.iconBtn:SetSize(16, 16)
    row.iconBtn:SetPoint("LEFT", 14, 0)
    row.iconBtn:EnableMouse(true)

    row.icon = row.iconBtn:CreateTexture(nil, "ARTWORK")
    row.icon:SetAllPoints()

    local icon = (item.GetIcon and item:GetIcon()) or (id and C_Item.GetItemIconByID(id)) or 134400
    row.icon:SetTexture(icon)

    row.iconBtn:SetScript("OnEnter", function()
        GameTooltip:SetOwner(row.iconBtn, "ANCHOR_RIGHT")
        if id then
            GameTooltip:SetItemByID(id)
        else
            GameTooltip:SetText(item:GetName() or "Unknown")
        end
        GameTooltip:Show()
    end)

    row.iconBtn:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    row.iconBtn:SetScript("OnClick", function()
        -- reserved
    end)

    local markup = item.GetCategoryIcon and item:GetCategoryIcon()
    if markup then
        row.typeFS = row:CreateFontString(nil, "OVERLAY")
        row.typeFS:SetFont(FONT, 12)
        row.typeFS:SetPoint("LEFT", row.icon, "RIGHT", 2, 0)
        row.typeFS:SetText(markup)
    end

    row.text = row:CreateFontString(nil, "OVERLAY")
    row.text:SetFont(FONT, 11)

    if row.typeFS then
        row.text:SetPoint("LEFT", row.typeFS, "RIGHT", 4, 0)
    else
        row.text:SetPoint("LEFT", row.icon, "RIGHT", 6, 0)
    end

    row.text:SetWidth(220)
    row.text:SetJustifyH("LEFT")

    local nm = item.GetName and item:GetName() or ""
    if (not nm or nm == "") and id then
        nm = "Loading…"
        if C_Item and C_Item.RequestLoadItemDataByID then
            C_Item.RequestLoadItemDataByID(id)
        end
        pendingRowsByItemId[id] = pendingRowsByItemId[id] or {}
        table.insert(pendingRowsByItemId[id], row)
    elseif nm == "" then
        nm = "Loading…"
    end

    row.text:SetText(nm)

    if item.MeetRequirements and not item:MeetRequirements() then
        row.text:SetTextColor(1, 0.2, 0.2)
    end

    row.value = row:CreateFontString(nil, "OVERLAY")
    row.value:SetFont(FONT, 11)
    row.value:SetPoint("RIGHT", -4, 0)
    row.value:SetWidth(40)
    row.value:SetJustifyH("RIGHT")

    local val = item.GetRemainingKnowledgePoints and item:GetRemainingKnowledgePoints() or 0
    if val > 0 then
        row.value:SetText("+" .. val)
        if item.MeetRequirements and not item:MeetRequirements() then
            row.value:SetTextColor(1, 0.2, 0.2)
        elseif item.IsCatchUp and item:IsCatchUp() then
            row.value:SetTextColor(0.2, 1, 0.2)
        else
            row.value:SetTextColor(1, 0.7, 0.2)
        end
    else
        row.value:SetText("")
    end

    row.check = row:CreateTexture(nil, "ARTWORK")
    row.check:SetSize(12, 12)
    row.check:SetPoint("RIGHT", row.value, "LEFT", -4, 0)
    row.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    row.check:SetVertexColor(0.4, 1, 0.4)
    if item.GetRemainingKnowledgePoints then
        row.check:SetShown(item:GetRemainingKnowledgePoints() == 0)
    else
        row.check:Hide()
    end

    row:SetScript("OnEnter", function()
        Tooltip:Clear()
        Tooltip:AddLine(item:GetName() or "Unknown", 14, 1, 1, 1)

			if item.IsCatchUp and item:IsCatchUp()
			   and item.unlockRequirements
			   and next(item.unlockRequirements)
			then
				Tooltip:AddLine(" ")
				Tooltip:AddLine(L["CATCHUP_REQUIREMENTS"], 12, 1, 0.82, 0)

				for _, req in ipairs(item.unlockRequirements) do
					local ok = req:GetRemainingKnowledgePoints() == 0
					local nm2 = req:GetName() or "Unknown"
					if ok then
						Tooltip:AddLine(" + " .. nm2, 12, 0.2, 1, 0.2)
					else
						Tooltip:AddLine(" × " .. nm2, 12, 1, 0.2, 0.2)
					end
				end
			elseif item.IsCatchUp and item:IsCatchUp() then
				Tooltip:AddLine(" ")
				Tooltip:AddLine(
					L["CATCHUP_SOURCE_NPC_ORDERS"]
						or "Obtained from NPC crafting orders",
					12, 0.9, 0.9, 0.9
				)				
			else
            local reqText = item.GetRequirementsText and item:GetRequirementsText()
            if reqText then
                Tooltip:AddLine(" ")
                Tooltip:AddLine(L["REQUIREMENTS"], 12, 1, 0.82, 0)
                Tooltip:AddLine(reqText, 12, 0.9, 0.9, 0.9)
            else
                local desc = item.GetDescription and item:GetDescription()
                if desc then
                    Tooltip:AddLine(" ")
                    Tooltip:AddLine(desc, 12, 0.9, 0.9, 0.9)
                end
            end
        end

        Tooltip:ShowAtCursor()
    end)

    row:SetScript("OnLeave", function()
        Tooltip:Clear()
    end)

    row:SetScript("OnClick", function()
        if not Tracker or not Tracker.ToggleTrackedItem then
            return
        end
        Tracker:ToggleTrackedItem(item)
    end)

	PT.UI.itemRows = PT.UI.itemRows or {}
	table.insert(PT.UI.itemRows, row)

    return row
end

local function PlayerHasAnyProfessions()
    local prof1, prof2 = GetProfessions()
    return prof1 or prof2
end

function PT:RefreshUI()
	if PT.UI then
		PT.UI.headers = {}
		PT.UI.itemRows = {}
	end
    if PT.config.showProfessionsWindow == false then
        if PT.UI and PT.UI.frame then
            PT.UI.frame:Hide()
        end
        return
    end

    if not PlayerHasAnyProfessions() then
        if PT.UI and PT.UI.frame then
            PT.UI.frame:Hide()
        end
        return
    end

    wipe(pendingRowsByItemId)

    local frame = UI:CreateMainFrame()
    frame:Show()

    local parent = UI.content
    if not parent then return end

    for _, child in ipairs({ parent:GetChildren() }) do
        child:SetParent(nil)
        child:Hide()
    end

local profs = PT.GetProfessions(true)

if not next(profs) then
    return
end

    local ordered = {}
    for _, p in pairs(profs) do
        table.insert(ordered, p)
    end

    table.sort(ordered, function(a, b)
        return a.index < b.index
    end)

    local y = -2

    for _, profession in ipairs(ordered) do
if PT.collapsedProfessions[profession.id] ~= nil then
    profession.expanded = not PT.collapsedProfessions[profession.id]
end
        local header = CreateProfessionHeader(parent, profession)
        header:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, y)
        y = y - 20

        if profession.expanded then
            local items = {}

local F = GetProfessionFilters()

for _, item in ipairs(profession.entries or {}) do
	item._distSq = GetItemDistanceSq(item)
    local show = true

    if item.Show and not item:Show() then
        show = false
    end

    if show and F.onlyMap and item.IsOnSameMap and not item:IsOnSameMap() then
        show = false
    end

    if show then
        local t = item.GetType and item:GetType()

if item.IsUniqueBook then
    if not F.uniqueBook then
        show = false
    end

elseif item.IsUniqueTreasure then
    if not F.uniqueTreasure then
        show = false
    end

elseif item.GetType and (item:GetType() == 2 or item:GetType() == 3) then
    if not F.weekly then
        show = false
    end

elseif item.GetType and item:GetType() == 4 then
    if not F.catchUp then
        show = false
    end
end

if show and item.IsTreatise then
    if not F.treatise then
        show = false
    end
end
    end

    if show then
        table.insert(items, item)
    end
end
           table.sort(items, SortItems)

if #items == 0 and PT.config.activeDB == "DF" then
    local skillInfo = profession:GetSkillLevel()
    local skill = skillInfo and skillInfo.skillLevel or 0

    if skill < 25 then
        local row = CreateFrame("Frame", nil, parent)
        row:SetSize(310, 18)

        local text = row:CreateFontString(nil, "OVERLAY")
        text:SetFont(FONT, 11)
        text:SetPoint("LEFT", 20, 0)
        text:SetTextColor(1, 0.8, 0.2)
        text:SetText(L["REQUIRES_PROFESSION_SKILL_25"])

        row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, y)
        y = y - 18
    end
end
            for _, item in ipairs(items) do
                local row = CreateItemRow(parent, item)
                if row then
                    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, y)
                    y = y - 18
                end
            end

            y = y - 4
        end
    end

    local contentHeight = math.abs(y) + 20
    parent:SetHeight(contentHeight)

	parent:SetHeight(contentHeight)

	frame:SetHeight(math.min(contentHeight + 35, 2000))
	if PT.UI and PT.UI.ApplyCustomization then
		PT.UI:ApplyCustomization()
	end

end
