local PT = AhUI
if not PT then return end
local L = PT.L

PT.AccountOverview_UI = PT.AccountOverview_UI or {}
local UI = PT.AccountOverview_UI

local FONT          = AhUI.FONT
local DEFAULT_SCALE = AhUI.SCALE_DEFAULT
local SCALE_STEP    = AhUI.SCALE_STEP
local SCALE_MIN     = 0.8 
local SCALE_MAX     = AhUI.SCALE_MAX
local SIZE_W, SIZE_H = 860, 420
local ROW_H = 34
local HEADER_H = 24
local TITLE_GAP = 15
local NOTIFY_MIN_W = 280
local NOTIFY_MAX_W = 520
local NOTIFY_LINE_H = 14
local NOTIFY_PAD_X = 16
local NOTIFY_PAD_Y = 10

local COLS = {
	{ key="char",  w=180, title="AO_HDR_CHAR",  tip="AO_TIP_CHAR" },
	{ key="c_df",  w=80,  title="AO_HDR_C_DF",  tip="AO_TIP_C_DF",  filterable=true },
	{ key="c_tww", w=80,  title="AO_HDR_C_TWW", tip="AO_TIP_C_TWW",  filterable=true },
	{ key="c_mid", w=80,  title="AO_HDR_C_MID", tip="AO_TIP_C_MID",  filterable=true },

	{ key="kp_df",  w=90, title="AO_HDR_KP_DF",  tip="AO_TIP_KP_DF",  filterable=true },
	{ key="kp_tww", w=90, title="AO_HDR_KP_TWW", tip="AO_TIP_KP_TWW",  filterable=true },
	{ key="kp_mid", w=90, title="AO_HDR_KP_MID", tip="AO_TIP_KP_MID",  filterable=true },

	{ key="skin", w=70, title="AO_HDR_SKIN", tip="AO_TIP_SKIN",  filterable=true },
	{ key="tw",   w=40, title="AO_HDR_TW",   tip="AO_TIP_TW",  filterable=true },
}

local CONCENTRATION_MAX = 1000

local EXP_LABEL = {
    df  = "DF",
    tww = "TWW",
    mid = "MID",
}

local COLOR_NORMAL = { 1, 1, 1 }
local COLOR_FULL   = { 0.2, 1.0, 0.2 }

local function GetProgressColor(done, total)
    if not total or total == 0 then
        return 1, 1, 1
    end

    local t = math.min(1, math.max(0, done / total))

    local r, g
    if t < 0.5 then
        r = 1
        g = t * 2
    else
        r = 2 - t * 2
        g = 1
    end

    return r, g, 0
end

local WEEKLY_ICONS = {
    trainer  = "quest-recurring-available",
    treasure = "VignetteLoot",
    treatise = "Levelup-Icon-Bag",
}

local WEEKLY_MARKS = {
    ok  = "common-icon-checkmark",    
    bad = "common-icon-redx",          
}

local SKIN_MARKS = {
    ok  = "common-icon-checkmark",
    bad = "common-icon-redx",
}

local function GetWeeklyStatus(charKey, expansion)
    if PT.AccountOverview_Data
       and PT.AccountOverview_Data.GetWeeklyStatus
    then
        return PT.AccountOverview_Data:GetWeeklyStatus(charKey, expansion)
    end
end

local CreateRow

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

local function KillScrollFrameTextures(sf)
    for _, region in ipairs({ sf:GetRegions() }) do
        if region and region.GetObjectType and region:GetObjectType() == "Texture" then
            region:SetTexture(nil)
            region:Hide()
        end
    end
end

local function CreateHeader(parent)
    local header = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    header:SetPoint("TOPLEFT", 12, -36)
    header:SetPoint("TOPRIGHT", -12, -36)
    header:SetHeight(HEADER_H)

    header:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 1,
    })
    header:SetBackdropColor(1, 1, 1, 0.06)
    header:SetBackdropBorderColor(1, 1, 1, 0.15)

	local INFO_ATLAS = "help-i"

	local filters = PT.AccountOverview_Data:GetColumnFilters()
	local x = 8

	for _, c in ipairs(COLS) do
		if c.filterable and filters[c.key] == false then

		else
		local holder = CreateFrame("Frame", nil, header)
		holder:SetPoint("LEFT", header, "LEFT", x, 0)
		holder:SetSize(c.w, HEADER_H)

		local fs = holder:CreateFontString(nil, "OVERLAY")
		fs:SetFont(FONT, 12, "")
		fs:SetPoint("LEFT", 0, 0)
		fs:SetJustifyH("LEFT")
		fs:SetText(L[c.title] or c.title)

		if c.tip then

			local info = CreateFrame("Frame", nil, holder)
			info:SetSize(20, 20)
			info:SetPoint("LEFT", fs, "RIGHT", -4, 5)
			info:EnableMouse(true)

			local icon = info:CreateTexture(nil, "OVERLAY")
			icon:SetAllPoints()
			icon:SetTexture("Interface\\COMMON\\help-i")

			if c.key == "c_df" or c.key == "c_tww" or c.key == "c_mid" then
				end

			info:SetScript("OnEnter", function(self)
				UI:ShowHeaderTooltip(self, L[c.tip] or c.tip)
			end)

			info:SetScript("OnLeave", function()
				UI:HideHeaderTooltip()
			end)
		end

			x = x + c.w
		end
	end

    return header
end

local function GetCharacterKey()
    local name = UnitName("player")
    local realm = GetNormalizedRealmName()
    if not name then return nil end
    return name .. "-" .. (realm or "")
end

local function GetKP(charKey, expansion)
    if not PT.AccountOverview_Data
       or not PT.AccountOverview_Data.GetKPByProfession
    then
        return nil
    end

    local data = PT.AccountOverview_Data:GetKPByProfession(charKey, expansion)
    if type(data) ~= "table" then
        return nil
    end

    if data.profs then
        return data.profs
    end

    return data
end

local function FormatConcentration(data)
    if type(data) ~= "table" then
        return nil
    end

    local sum = 0
    for _, info in pairs(data) do
        sum = sum + (info.value or 0)
    end

    return sum
end

local function GetConcentration(charKey, expansion)
    if PT.AccountOverview_Data
       and PT.AccountOverview_Data.GetConcentration
    then
        return PT.AccountOverview_Data:GetConcentration(charKey, expansion)
    end
end

local function CollectCharacters()
    local chars = {}
    local seen = {}

    local function add(key)
        if key and not seen[key] then
            seen[key] = true
            table.insert(chars, key)
        end
    end

    if AhUI_DB
       and AhUI_DB.accountOverview
       and AhUI_DB.accountOverview.chars
    then
        for charKey in pairs(AhUI_DB.accountOverview.chars) do
            if type(charKey) == "string" then
                local n, r = strsplit("-", charKey)
                if n and r and r ~= "" then
                    add(n .. "-" .. r)
                end
            end
        end
    end

    local current = GetCharacterKey()
    add(current)

	local function GetProfessionCount(charKey)
		local D = PT.AccountOverview_Data
		if not D then return 0 end

		local profs = {}

		for _, exp in ipairs({ "df", "tww", "mid" }) do
				local weekly = D:GetWeeklyStatus(charKey, exp)
				if type(weekly) == "table" then
					for profId in pairs(weekly) do
						profs[profId] = true
					end
				end
		end

		for _, exp in ipairs({ "df", "tww", "mid" }) do
				local conc = D:GetConcentration(charKey, exp)
				if type(conc) == "table" then
					for skillLine in pairs(conc) do
						profs[skillLine] = true
					end
				end
		end

		local count = 0
		for _ in pairs(profs) do
			count = count + 1
		end

		return count
	end

	local current = GetCharacterKey()

	table.sort(chars, function(a, b)

		if a == current then return true end
		if b == current then return false end

		local aCount = GetProfessionCount(a)
		local bCount = GetProfessionCount(b)

		if aCount ~= bCount then
			return aCount > bCount
		end

		return a < b
	end)

	return chars
end

local function BuildColumnFilterItems(ui)
    local filters = PT.AccountOverview_Data:GetColumnFilters()
    local items = {}

    table.insert(items, {
        text   = L["AO_FILTER_TITLE"] or "Columns",
        header = true,
    })

    for _, c in ipairs(COLS) do
        if c.filterable then
            local enabled = filters[c.key] ~= false

            table.insert(items, {
                text    = L[c.title] or c.title,
                icon    = enabled
                    and "Interface\\Buttons\\UI-CheckBox-Check"
                    or "Interface\\Buttons\\UI-CheckBox-Up",
                checked = enabled,
			onClick = function()
				filters[c.key] = not enabled

				ui:RebuildHeader()
				ui:Refresh()

				PT.Dropdown:Show({
					owner    = ui.frame.filterButton,
					keepOpen = true,
					items    = BuildColumnFilterItems(ui),
				})
			end,
            })
        end
    end

    return items
end

function UI:RebuildHeader()
    local f = self.frame
    if not f then return end

    if f.header then
        f.header:Hide()
        f.header:SetParent(nil)
        f.header = nil
    end

    f.header = CreateHeader(f)

    if f.scroll then
        f.scroll:ClearAllPoints()
        f.scroll:SetPoint("TOPLEFT", f.header, "BOTTOMLEFT", 0, -6)
        f.scroll:SetPoint("BOTTOMRIGHT", -12, 12)
        f.scroll:SetVerticalScroll(0) 
    end
end

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

    local cur = AhUI_Config.accountOverviewScale or DEFAULT_SCALE
    local nextScale = cur + delta

    nextScale = math.max(SCALE_MIN, math.min(SCALE_MAX, nextScale))
    AhUI_Config.accountOverviewScale = nextScale

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

function UI:ResetScale()
    AhUI_Config = AhUI_Config or {}
    AhUI_Config.accountOverviewScale = 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", "AhUIAccountOverviewFrame", UIParent, "BackdropTemplate")
    f:SetSize(SIZE_W, SIZE_H)
    f:SetPoint("CENTER")
	AhUI_Config = AhUI_Config or {}
	local scale = AhUI_Config.accountOverviewScale or DEFAULT_SCALE
	f:SetScale(scale)
    f:SetFrameStrata("DIALOG")
    f:SetClampedToScreen(true)
	f:EnableMouse(true)
	f:SetMouseMotionEnabled(true)
	f:EnableMouseWheel(true)

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

	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)

    ApplyBackdrop(f)
    f:Hide()
    tinsert(UISpecialFrames, f:GetName())

	f:SetMovable(true)
	f:EnableMouse(true)
	f:RegisterForDrag("LeftButton")

	f:SetScript("OnDragStart", function(self)
		if not InCombatLockdown() then
			self:StartMoving()
		end
	end)

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

    local title = f:CreateFontString(nil, "OVERLAY")
    title:SetFont(FONT, 14, "OUTLINE")
    title:SetPoint("TOPLEFT", 12, -10)
    title:SetText(L["AO_TITLE"] or "Account Overview")
    f.title = title

	local info = CreateFrame("Frame", nil, f)
	info:SetSize(20, 20)
	info:SetPoint("LEFT", title, "RIGHT", -2, 4)
	info:EnableMouse(true)

	local icon = info:CreateTexture(nil, "OVERLAY")
	icon:SetAllPoints()
	icon:SetTexture("Interface\\COMMON\\help-i")

	info:SetScript("OnEnter", function(self)
		UI:ShowHeaderTooltip(self, L["AO_SCALE_TIP"])
	end)

	info:SetScript("OnLeave", function()
		UI:HideHeaderTooltip()
	end)

    local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    close:SetPoint("TOPRIGHT", -4, -4)
	
	local reset = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
	reset:SetSize(80, 20)
	reset:SetPoint("RIGHT", close, "LEFT", -6, 0)
	reset:SetText(L["AO_RESET"] or "Reset")

	if reset.Text then
		reset.Text:SetFont(FONT, 12, "OUTLINE")
		reset.Text:SetTextColor(1, 1, 1)
	end

	reset:SetScript("OnClick", function()
		UI:ConfirmReset()
	end)

	f.resetButton = reset

	local filter = CreateFrame("Button", nil, f)
	filter:SetSize(18, 18)
	filter:SetPoint("RIGHT", reset, "LEFT", -6, 0)
	filter:EnableMouse(true)

	filter:SetScript("OnClick", function(self)
		PT.Dropdown:Show({
			owner    = self,
			keepOpen = true,
			items    = BuildColumnFilterItems(UI),
		})
	end)

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

	filter.icon = icon
	f.filterButton = filter

	filter:SetScript("OnEnter", function(self)
		UI:ShowHeaderTooltip(self, L["AO_FILTER_TIP"] or "Column filters")
	end)

	filter:SetScript("OnLeave", function()
		UI:HideHeaderTooltip()
	end)

    f.header = CreateHeader(f)

	CreateRow = function(parent)
		local row = CreateFrame("Frame", nil, parent)
		row:SetHeight(ROW_H)
		row:SetWidth(SIZE_W - 24)
		row:EnableMouse(true)
		row:SetMouseMotionEnabled(true)

		row.highlight = row:CreateTexture(nil, "BORDER")  

		row.highlight:SetAllPoints()
		row.highlight:SetColorTexture(0.4, 0.6, 1.0)   
		row.highlight:SetAlpha(0)                      

		row.isCurrent = false
		row.cells = {}

		local filters = PT.AccountOverview_Data:GetColumnFilters()
		local x = 8

		for _, c in ipairs(COLS) do
			if c.filterable and filters[c.key] == false then

			else
			if c.key == "c_df"
			or c.key == "c_tww"
			or c.key == "c_mid"
			then

				local cell = CreateFrame("Frame", nil, row)
				cell:SetPoint("LEFT", row, "LEFT", x, 0)
				cell:SetSize(c.w, ROW_H)

				cell.lines = {}
				for i = 1, 2 do
					local line = CreateFrame("Frame", nil, cell)
					line:SetSize(c.w, ROW_H / 2)
					line:SetPoint("TOPLEFT", 0, i == 1 and -2 or -(ROW_H / 2))

					local notifyBtn = CreateFrame("Button", nil, line)
					notifyBtn:SetSize(14, 14)
					notifyBtn:SetPoint("LEFT", 0, 0)
					notifyBtn:Hide()

					local notifyChat = notifyBtn:CreateTexture(nil, "ARTWORK")
					notifyChat:SetAllPoints()
					notifyChat:SetAtlas("transmog-icon-chat")
					notifyBtn.chatTex = notifyChat

					local notifyMark = notifyBtn:CreateTexture(nil, "OVERLAY")
					notifyMark:SetSize(8, 8)
					notifyMark:SetPoint("BOTTOMRIGHT", 1, -1)
					notifyBtn.markTex = notifyMark

					line.notifyBtn = notifyBtn

					line.icon = line:CreateTexture(nil, "ARTWORK")
					line.icon:SetSize(14, 14)
					line.icon:SetPoint("LEFT", notifyBtn, "RIGHT", 2, 0)
					line.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
					line.icon:Hide()

					line.text = line:CreateFontString(nil, "OVERLAY")
					line.text:SetFont(FONT, 11, "")
					line.text:SetPoint("LEFT", line.icon, "RIGHT", 4, 0)
					line.text:SetJustifyH("LEFT")
					line.text:SetText("—")
					line.text:SetTextColor(unpack(COLOR_NORMAL))

					cell.lines[i] = line
				end

				row.cells[c.key] = cell

			elseif c.key == "kp_df"
				or c.key == "kp_tww"
				or c.key == "kp_mid"
			then

				local cell = CreateFrame("Frame", nil, row)
				cell:SetPoint("LEFT", row, "LEFT", x, 0)
				cell:SetSize(c.w, ROW_H)

				cell.lines = {}

				for i = 1, 2 do
					local line = CreateFrame("Frame", nil, cell)
					line:SetSize(c.w, ROW_H / 2)
					line:SetPoint("TOPLEFT", 0, i == 1 and -2 or -(ROW_H / 2))

					line.icon = line:CreateTexture(nil, "ARTWORK")
					line.icon:SetSize(14, 14)
					line.icon:SetPoint("LEFT", 0, 0)
					line.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
					line.icon:Hide()

					line.text = line:CreateFontString(nil, "OVERLAY")
					line.text:SetFont(FONT, 11, "")
					line.text:SetPoint("LEFT", line.icon, "RIGHT", 4, 0)
					line.text:SetText("—")
					line.text:SetTextColor(unpack(COLOR_NORMAL))
					line.text:Hide()

					line.weekly = {}

					local order = { "trainer", "treasure", "treatise" }
					local xOff = 18

					for _, key in ipairs(order) do
						local wrap = CreateFrame("Frame", nil, line)
						wrap:SetSize(14, 14)
						wrap:SetPoint("LEFT", xOff, 0)

						wrap:EnableMouse(true)

						wrap:SetScript("OnEnter", function(self)
							if line.info and line.info.weekly then
								UI:ShowWeeklyKPTooltip(self, line.info, line.info.weekly)
							end
						end)

						wrap:SetScript("OnLeave", function()
							UI:HideWeeklyKPTooltip()
						end)

						local icon = wrap:CreateTexture(nil, "ARTWORK")
						icon:SetAllPoints()
						icon:SetAtlas(WEEKLY_ICONS[key])
						icon:Hide()

						local mark = wrap:CreateTexture(nil, "OVERLAY")
						mark:SetSize(10, 10)
						mark:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 3, -3)
						mark:Hide()

						line.weekly[key] = {
							icon = icon,
							mark = mark,
							key  = key,      
							line = line,
						}

						xOff = xOff + 16
					end

					cell.lines[i] = line
				end

				row.cells[c.key] = cell

			elseif c.key == "tw" then

				local cell = CreateFrame("Frame", nil, row)
				cell:SetPoint("LEFT", row, "LEFT", x, 0)
				cell:SetSize(c.w, ROW_H)

				cell.text = cell:CreateFontString(nil, "OVERLAY")
				cell.text:SetFont(FONT, 12, "")
				cell.text:SetPoint("CENTER")
				cell.text:SetText("—")
				cell.text:Show()

				cell.icon = cell:CreateTexture(nil, "OVERLAY")
				cell.icon:SetSize(18, 18)
				cell.icon:SetPoint("CENTER", 5, -6)

				cell.icon:Hide()

				cell.mark = cell:CreateTexture(nil, "OVERLAY")
				cell.mark:SetSize(25, 25)
				cell.mark:ClearAllPoints()
				cell.mark:SetPoint("BOTTOMRIGHT", cell.icon, "BOTTOMRIGHT", 10, 5)
				cell.mark:SetDrawLayer("OVERLAY", 7)
				cell.mark:Hide()

				row.cells.tw = cell

				else
					local fs = row:CreateFontString(nil, "OVERLAY")

					if c.key == "skin" then
						fs:SetFont(FONT, 18, "OUTLINE")
						fs:SetPoint("LEFT", row, "LEFT", x - 12, -2)
						fs:SetWidth(c.w - 4)
						fs:SetJustifyH("CENTER") 
						fs:EnableMouse(true)
						fs:SetScript("OnEnter", function(self)
							UI:ShowSkinTooltip(self, row.charKey)
						end)
						fs:SetScript("OnLeave", function()
							UI:HideSkinTooltip()
						end)
					else
						fs:SetFont(FONT, 12, "")
						fs:SetPoint("LEFT", row, "LEFT", x, 0)
						fs:SetWidth(c.w)
						fs:SetJustifyH("LEFT")
					end

					fs:SetText("—")
					row.cells[c.key] = fs
				end

				x = x + c.w
			end
		end

		return row
	end

	local scroll = CreateFrame("ScrollFrame", nil, f)
	scroll:SetPoint("TOPLEFT", f.header, "BOTTOMLEFT", 0, -6)
	scroll:SetPoint("BOTTOMRIGHT", -12, 12)

	scroll:EnableMouse(true)                 
	scroll:SetMouseMotionEnabled(true)       
	scroll:SetScript("OnUpdate", function(self)
		if not self:IsMouseOver() then
			if self.hoveredRow then
				local r = self.hoveredRow
				r.highlight:SetAlpha(r.isCurrent and 0.06 or 0)
				self.hoveredRow = nil
			end
			return
		end

		local mx, my = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		mx, my = mx / scale, my / scale

		local cx, cy = self.content:GetCenter()
		if not cx then return end

		local top = cy + (self.content:GetHeight() / 2)
		local offset = (top - my) + self:GetVerticalScroll()

		local index = math.floor(offset / ROW_H) + 1

		local row = self.content.rows and self.content.rows[index]
		if row ~= self.hoveredRow then
			if self.hoveredRow then
				local r = self.hoveredRow
				r.highlight:SetAlpha(r.isCurrent and 0.06 or 0)
			end

			self.hoveredRow = row

			if row then
				row.highlight:SetAlpha(row.isCurrent and 0.15 or 0.10)
			end
		end
	end)

	scroll.bg = scroll:CreateTexture(nil, "BACKGROUND")
	scroll.bg:SetAllPoints()
	scroll.bg:SetColorTexture(0, 0, 0, 0.001) 

	scroll:EnableMouseWheel(true)
	KillScrollFrameTextures(scroll)

    local content = CreateFrame("Frame", nil, scroll)
    content:SetSize(1, 1)
	content:EnableMouse(true)              
	content:SetMouseMotionEnabled(true)
    scroll:SetScrollChild(content)
	scroll.content = content
	scroll.hoveredRow = nil

	scroll:SetScript("OnUpdate", function(self)
		local content = self.content
		if not content or not content.rows then return end

		if not self:IsMouseOver() then
			if self.hoveredRow then
				local r = self.hoveredRow
				r.highlight:SetAlpha(r.isCurrent and 0.06 or 0)
				self.hoveredRow = nil
			end
			return
		end

		local mx, my = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		mx, my = mx / scale, my / scale

		local left, top = self:GetLeft(), self:GetTop()
		if not top then return end

		local yFromTop = (top - my) + self:GetVerticalScroll()

		local index = math.floor(yFromTop / ROW_H) + 1

		local row = content.rows[index]

		if row ~= self.hoveredRow then

			if self.hoveredRow then
				local r = self.hoveredRow
				r.highlight:SetAlpha(r.isCurrent and 0.06 or 0)
			end

			self.hoveredRow = row

			if row then
				row.highlight:SetAlpha(row.isCurrent and 0.15 or 0.10)
			end
		end
	end)
		
    scroll:SetScript("OnMouseWheel", function(self, delta)
        local cur = self:GetVerticalScroll()
        self:SetVerticalScroll(math.max(0, cur - delta * ROW_H))
    end)

    f.scroll = scroll
    f.content = content

    self.frame = f
    return f
end

local function SetConcCell(cell, data, charKey, exp)
    if not cell or not cell.lines then return end

    for i = 1, 2 do
        local line = cell.lines[i]
        if line.icon then
            line.icon:Hide()
            line.icon:SetTexture(nil)
        end
        if line.notifyBtn then
            line.notifyBtn:Hide()
        end
        if line.text then
            line.text:SetText("—")
            line.text:SetTextColor(unpack(COLOR_NORMAL))
        end
    end

	if type(data) ~= "table" or not next(data) then
		return
	end

    local items = {}
    for skillLine, info in pairs(data) do
        table.insert(items, { skillLine = skillLine, info = info })
    end
    table.sort(items, function(a, b)
        return (a.info.name or "") < (b.info.name or "")
    end)

    local D = PT.AccountOverview_Data

    for i = 1, 2 do
        local entry = items[i]
        local line  = cell.lines[i]
        if not entry or not line then break end

        local info      = entry.info
        local skillLine = entry.skillLine

        if info and line.icon then
            if info.icon then
                line.icon:SetTexture(info.icon)
                line.icon:Show()
            end

            if line.text then
                if info.value == nil then
                    line.text:SetText("…")
                else
                    line.text:SetText(tostring(info.value))
                end
                if info.value and info.value >= CONCENTRATION_MAX then
                    line.text:SetTextColor(unpack(COLOR_FULL))
                else
                    line.text:SetTextColor(unpack(COLOR_NORMAL))
                end
            end

            if line.notifyBtn and charKey and skillLine and D then
                local btn = line.notifyBtn

                local function RefreshNotifyBtn()
                    local enabled = D:IsConcentrationProfEnabled(charKey, exp, skillLine)
                    btn.chatTex:SetDesaturated(not enabled)
                    btn.chatTex:SetAlpha(enabled and 1 or 0.5)
                    btn.markTex:SetTexture(nil)
                    if enabled then
                        btn.markTex:SetAtlas("common-icon-checkmark")
                        btn.markTex:SetVertexColor(0.2, 0.9, 0.2, 1)
                    else
                        btn.markTex:SetAtlas("common-icon-redx")
                        btn.markTex:SetVertexColor(0.9, 0.2, 0.2, 0.8)
                    end
                    btn.markTex:Show()
                end

                btn:SetScript("OnClick", function()
                    local cur = D:IsConcentrationProfEnabled(charKey, exp, skillLine)
                    D:SetConcentrationProfEnabled(charKey, exp, skillLine, not cur)
                    RefreshNotifyBtn()
                end)

                btn:SetScript("OnEnter", function()
                    local enabled = D:IsConcentrationProfEnabled(charKey, exp, skillLine)
                    local Tooltip = PT.Tooltip
                    if Tooltip then
                        Tooltip:Clear()
                        Tooltip:AddLine(info.name or "", 12, 1, 1, 1)
                        if enabled then
                            Tooltip:AddLine(L["AO_CONC_NOTIFY_ON"]  or "Notification enabled",  11, 0.2, 0.9, 0.2)
                        else
                            Tooltip:AddLine(L["AO_CONC_NOTIFY_OFF"] or "Notification disabled", 11, 0.6, 0.6, 0.6)
                        end
                        Tooltip:AddLine(L["AO_CONC_NOTIFY_CLICK"] or "Click to toggle", 11, 0.5, 0.5, 0.5)
                        Tooltip:ShowAt(btn, "TOPLEFT", "BOTTOMLEFT", 0, -4)
                    end
                end)

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

                RefreshNotifyBtn()
                btn:Show()
            end
        end
    end
end

local function SetWeeklyKPCell(cell, data)
    if not cell or not cell.lines then return end

	if type(data) ~= "table" or not next(data) then
		for i = 1, 2 do
			local line = cell.lines[i]

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

			for _, w in pairs(line.weekly) do
				if w.icon then w.icon:Hide() end
				if w.mark then w.mark:Hide() end
			end

			if line.text then
				line.text:SetText("—")
				line.text:Show()
			end
		end
		return
	end

    local items = {}
    for _, v in pairs(data) do
        table.insert(items, v)
    end

    table.sort(items, function(a, b)
        return (a.name or "") < (b.name or "")
    end)

    for i = 1, 2 do
        local info = items[i]
        local line = cell.lines[i]

		line.info = nil
        line.icon:Hide()
        for _, w in pairs(line.weekly) do
            w.mark:Hide()
        end

        if info then
			line.info = info

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

            if info.icon then
                line.icon:SetTexture(info.icon)
                line.icon:Show()
            end


			if info.weekly then
				for key, w in pairs(line.weekly) do

					if w.icon then
						w.icon:Show()
					end

					local done = info.weekly[key]
					if w.mark then
						w.mark:SetAtlas(done and WEEKLY_MARKS.ok or WEEKLY_MARKS.bad)
						w.mark:Show()
					end
				end
			end
        end
    end
end

local function SetTWCell(cell, data)
    if not cell then return end

    if not data then
        cell.icon:Hide()
        cell.mark:Hide()
        if cell.text then
            cell.text:SetText("—")
            cell.text:Show()
        end
        return
    end

    if cell.text then
        cell.text:Hide()
    end

	if C_Texture.GetAtlasInfo(data.atlas) then
        cell.icon:SetAtlas(data.atlas, true) 
        cell.icon:SetSize(60, 40)            
        cell.icon:SetTexCoord(0, 1, 0, 1) 
        cell.icon:SetVertexColor(1, 1, 1, 1)
        cell.icon:Show()
    else
        cell.icon:Hide()
    end

    cell.mark:SetAtlas(data.done and WEEKLY_MARKS.ok or WEEKLY_MARKS.bad)
    cell.mark:Show()
end


function UI:SetSkinCell(cell, charKey)
    if not cell or not charKey then return end

    cell:SetText("—")
    cell:SetTextColor(1, 1, 1)

    local D = PT.AccountOverview_Data
    if not D or not D.GetSkinningProgress then
        return
    end

    local done, total = D:GetSkinningProgress(charKey)
    if not done or not total then
        return
    end

    cell:SetText(done .. "/" .. total)

    local r, g, b = GetProgressColor(done, total)
    cell:SetTextColor(r, g, b)
end

function UI:ShowSkinTooltip(cell, charKey)
    local Tooltip = PT.Tooltip
    local D  = PT.AccountOverview_Data
    local SC = PT.SkinningCheck

    if not Tooltip or not D then return end

    local done, total = D:GetSkinningProgress(charKey)
    if not done or not total then
        return
    end

    Tooltip:Clear()

    Tooltip:AddLine(L["SKINNING_TITLE"] or "Skinning", 14, 1, 1, 1)

    local r, g, b = GetProgressColor(done, total)
    Tooltip:AddLine(" " .. string.format("%d / %d", done, total), 12, r, g, b)

    if charKey == GetCharacterKey() and SC then
        for _, target in ipairs(SC.TARGETS) do
            if SC:IsTargetAvailable(target)
               and SC:IsTargetEnabled(target)
            then
                local completed = SC:IsTargetCompleted(target)
                local name = SC:GetTargetName(target)

				local fs = Tooltip:AddLine("      " .. name, 12, 1, 1, 1)

                local icon = Tooltip.frame:CreateTexture(nil, "OVERLAY")
				Tooltip.frame.icons = Tooltip.frame.icons or {}
				table.insert(Tooltip.frame.icons, icon)
                icon:SetSize(12, 12)
                icon:SetPoint("LEFT", fs, "LEFT", 1, 0)
                icon:SetAtlas(completed and SKIN_MARKS.ok or SKIN_MARKS.bad)
            end
        end
    end

    Tooltip:ShowAt(cell, "TOPLEFT", "BOTTOMLEFT", 6, -4)
end

function UI:HideSkinTooltip()
    if PT.Tooltip then
        PT.Tooltip:Clear()
    end
end

function UI:ShowHeaderTooltip(anchor, text)
    local Tooltip = PT.Tooltip
    if not Tooltip or not text then return end

    Tooltip:Clear()
    Tooltip:AddLine(text, 12, 1, 1, 1)
    Tooltip:ShowAt(anchor, "TOP", "BOTTOM", 0, -6)
end

function UI:HideHeaderTooltip()
    if PT.Tooltip then
        PT.Tooltip:Clear()
    end
end

do
    local _notifyFrame = nil
    local _notifyTimer = nil

    function UI:ShowConcentrationNotification()
        local D = PT.AccountOverview_Data
        if not D or not D.pendingFullConc then return end

        local queue = {}
        for charKey, list in pairs(D.pendingFullConc) do
            for _, data in ipairs(list) do
                table.insert(queue, data)
            end
            D.pendingFullConc[charKey] = nil
        end

        if #queue == 0 then return end

        if _notifyFrame then
            _notifyFrame:Hide()
            _notifyFrame = nil
        end
        if _notifyTimer then
            _notifyTimer:Cancel()
            _notifyTimer = nil
        end

        local f = CreateFrame("Button", nil, UIParent, "BackdropTemplate")
        f:SetPoint("TOP", UIParent, "TOP", 0, -120)
        f:SetFrameStrata("DIALOG")
        f:EnableMouse(true)
        f:SetBackdrop({
            bgFile   = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Buttons\\WHITE8x8",
            edgeSize = 2,
        })
        f:SetBackdropColor(0, 0, 0, 0.95)
        f:SetBackdropBorderColor(0.2, 1.0, 0.2, 1)

        local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
        close:SetPoint("TOPRIGHT", -4, -4)
        close:SetScale(0.8)
        close:SetScript("OnClick", function()
            if InCombatLockdown() then
                UIErrorsFrame:AddMessage(ERR_NOT_IN_COMBAT, 1, 0.2, 0.2)
                return
            end
            f:Hide()
        end)

        local checkIcon = f:CreateTexture(nil, "ARTWORK")
        checkIcon:SetSize(20, 20)
        checkIcon:SetPoint("LEFT", 12, 0)
        checkIcon:SetAtlas("common-icon-checkmark")

        local title = f:CreateFontString(nil, "OVERLAY")
        title:SetFont(FONT, 14, "OUTLINE")
        title:SetPoint("TOPLEFT", 44, -10)
        title:SetText(L["AO_CONC_FULL_TITLE"] or "Concentration restored")
        title:SetTextColor(0.2, 1.0, 0.2)

        local text = f:CreateFontString(nil, "OVERLAY")
        text:SetFont(FONT, 11, "")
        text:SetJustifyH("LEFT")
        text:SetJustifyV("TOP")
        text:SetWordWrap(true)
        text:SetSpacing(4)

        local lines = {}
        for i, data in ipairs(queue) do
            if i > 5 then break end
            table.insert(lines, string.format(
                "%s — %s (%s, %d)",
                data.charKey,
                data.name or "",
                EXP_LABEL[data.exp] or data.exp,
                data.value or 0
            ))
        end
        if #queue > 5 then
            table.insert(lines, L["AO_CONC_MORE"] or "and others…")
        end
        text:SetText(table.concat(lines, "\n"))

        local PAD_X, PAD_Y = 16, 10
        text:SetWidth(NOTIFY_MAX_W - PAD_X * 2)
        local textW = math.max(NOTIFY_MIN_W - PAD_X * 2, text:GetStringWidth())
        local finalW = math.min(NOTIFY_MAX_W, math.max(NOTIFY_MIN_W, textW + PAD_X * 2 + 44))
        local titleH = title:GetStringHeight() or 16
        local finalH = titleH + TITLE_GAP + text:GetStringHeight() + PAD_Y * 2

        f:SetSize(finalW, finalH)
        text:ClearAllPoints()
        text:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -TITLE_GAP)
        text:SetWidth(finalW - PAD_X * 2 - 44)

        f:SetScript("OnClick", function()
            if PT.AccountOverview_UI then PT.AccountOverview_UI:Toggle() end
            f:Hide()
        end)

        f:Show()
        _notifyFrame = f
        _notifyTimer = C_Timer.NewTimer(18, function()
            if _notifyFrame == f then
                f:Hide()
                _notifyFrame = nil
            end
        end)
    end
end

function UI:ShowWeeklyKPTooltip(anchor, info, weekly)
    local Tooltip = PT.Tooltip
    if not Tooltip then return end

    Tooltip:Clear()

    Tooltip:AddLine(L["AO_KP_TITLE"] or "Weekly Knowledge", 14, 1, 1, 1)

	local order = {
		{ key = "trainer",  label = L["AO_KP_TRAINER"]  or "Trainer"  },
		{ key = "treasure", label = L["AO_KP_TREASURE"] or "Treasure" },
		{ key = "treatise", label = L["AO_KP_TREATISE"] or "Treatise" },
	}

    for _, o in ipairs(order) do
        local done = weekly and weekly[o.key]
        local fs = Tooltip:AddLine(
            "      " .. o.label,
            12, 1, 1, 1
        )

        local icon = Tooltip.frame:CreateTexture(nil, "OVERLAY")
        Tooltip.frame.icons = Tooltip.frame.icons or {}
        table.insert(Tooltip.frame.icons, icon)

        icon:SetSize(12, 12)
        icon:SetPoint("LEFT", fs, "LEFT", 1, 0)
        icon:SetAtlas(done and WEEKLY_MARKS.ok or WEEKLY_MARKS.bad)
    end

    Tooltip:ShowAt(anchor, "TOPLEFT", "BOTTOMLEFT", 6, -4)
end

function UI:HideWeeklyKPTooltip()
    if PT.Tooltip then
        PT.Tooltip:Clear()
    end
end

local function SetWeeklyCell(cell, status)
    if not cell or not cell.weekly then return end

    for key, pack in pairs(cell.weekly) do
        if pack.icon then
            pack.icon:Show()
        end

        if pack.mark then
            if status == nil then
                pack.mark:Hide()
            else
                local done = status[key] and true or false
                pack.mark:SetAtlas(done and WEEKLY_MARKS.ok or WEEKLY_MARKS.bad)
                pack.mark:Show()
            end
        end
    end
end

function UI:Refresh()

    local f = self.frame
	if f.scroll then
		f.scroll.hoveredRow = nil
	end
    if not f then return end
	f.content.rows = {}

    for _, c in ipairs({ f.content:GetChildren() }) do
        c:Hide()
    end

    local chars = CollectCharacters()
    local y = -2

    for _, charKey in ipairs(chars) do
        local row = CreateRow(f.content)	
		row.charKey = charKey
		table.insert(f.content.rows, row)

		if charKey == GetCharacterKey() then
			row.isCurrent = true
			row.highlight:SetAlpha(0.06)        
			row.cells.char:SetFont(FONT, 12, "OUTLINE")
		else
			row.isCurrent = false
			row.highlight:SetAlpha(0)
		end
        row:SetPoint("TOPLEFT", 0, y)
        row:SetPoint("RIGHT", f.content, "RIGHT", 0, 0)

		if row.cells.char then
			row.cells.char:SetText(charKey)
		end

		do
			if row.cells.c_df then
				local v = GetConcentration(charKey, "df")
				SetConcCell(row.cells.c_df, v, charKey, "df")
			end
		end

		do
			if row.cells.c_df then
				local v = GetConcentration(charKey, "tww")
				SetConcCell(row.cells.c_tww, v, charKey, "tww")
			end
		end

		do
			local cell = row.cells.c_mid
			if cell then
				local v = GetConcentration(charKey, "mid")
					SetConcCell(cell, v, charKey, "mid")
			end
		end

		do
			local data = GetWeeklyStatus(charKey, "df")
			SetWeeklyKPCell(row.cells.kp_df, data)
		end

		do
			local data = GetWeeklyStatus(charKey, "tww")
			SetWeeklyKPCell(row.cells.kp_tww, data)
		end

		local data = GetWeeklyStatus(charKey, "mid")
			SetWeeklyKPCell(row.cells.kp_mid, data)

		do
			self:SetSkinCell(row.cells.skin, charKey)
		end

		do
			local data = PT.AccountOverview_Data:GetTimewalkingStatus(charKey)
			SetTWCell(row.cells.tw, data)
		end
		
        y = y - ROW_H
    end

    f.content:SetHeight(-y + 4)
	f.content:SetWidth(f.scroll:GetWidth())
end

function UI:Show()
    local f = self:Create()
    f:Show()

    if PT.professionsReady then
        self:Refresh()
    else
        C_Timer.After(0.2, function()
            if f:IsShown() then
                self:Refresh()
            end
        end)
    end
end

function UI:Hide()
    if self.frame then
        self.frame:Hide()
    end
end

function UI:Toggle()
    local f = self:Create()
    if f:IsShown() then
        self:Hide()
    else
        self:Show()
    end
end

function UI:ConfirmReset()

    StaticPopupDialogs["AHUI_AO_RESET_1"] = {
        text = L["AO_RESET_1_TEXT"],
        button1 = YES,
        button2 = NO,
        OnAccept = function()
            StaticPopup_Show("AHUI_AO_RESET_2")
        end,
        timeout = 0,
        whileDead = true,
        hideOnEscape = true,
        preferredIndex = 3,
    }

StaticPopupDialogs["AHUI_AO_RESET_2"] = {
    text = L["AO_RESET_2_TEXT"] or "This action cannot be undone.",
    button1 = L["AO_RESET_2_YES"] or YES,
    button2 = L["AO_RESET_2_NO"]  or NO,

    OnShow = function(self)
        local btn1 = _G[self:GetName() .. "Button1"]
        if not btn1 then return end

        btn1:Disable()

        C_Timer.After(1.2, function()
            if self:IsShown() and btn1 then
                btn1:Enable()
            end
        end)
    end,

    OnAccept = function()
        if PT.AccountOverview_Data and PT.AccountOverview_Data.Reset then
            PT.AccountOverview_Data:Reset()
        end
        UI:Refresh()
    end,

    timeout = 0,
    whileDead = true,
    hideOnEscape = true,
    preferredIndex = 3,
}

    StaticPopup_Show("AHUI_AO_RESET_1")
end