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 MIN_W, MIN_H   = 500, 260
local MAX_W, MAX_H   = 1400, 900
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="dundun", w=70, title="AO_HDR_DUNDUN", tip="AO_TIP_DUNDUN", 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 GetVisibleColumns(frameWidth, filters)
    local visible = {}
    local totalWidth = 8

    for _, c in ipairs(COLS) do
        if not c.filterable or filters[c.key] ~= false then
            table.insert(visible, c)
            totalWidth = totalWidth + c.w
        end
    end

    local maxRight = math.max(0, frameWidth - 24)

    while #visible > 0 and totalWidth > maxRight do
        local removed = false

        for i = #visible, 1, -1 do
            local c = visible[i]
            if c.filterable then
                totalWidth = totalWidth - c.w
                table.remove(visible, i)
                removed = true
                break
            end
        end

        if not removed then
            break
        end
    end

    return visible
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 filters = PT.AccountOverview_Data:GetColumnFilters()
	local visibleCols = GetVisibleColumns(parent:GetWidth(), filters)
	local x = 8

for _, c in ipairs(visibleCols) do
    local holder = CreateFrame("Frame", nil, header)
    holder:SetPoint("LEFT", header, "LEFT", x, 0)
    holder:SetSize(c.w, HEADER_H)

    if c.key == "dundun" then
        local group = CreateFrame("Frame", nil, holder)
        group:SetHeight(HEADER_H)

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

        local textWidth = math.ceil(fs:GetStringWidth() or 0)

        if c.tip then
            local info = CreateFrame("Frame", nil, group)
            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")

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

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

            local groupWidth = math.min(c.w, textWidth + 16)
            group:SetWidth(groupWidth)
        else
            group:SetWidth(math.min(c.w, textWidth))
        end

        group:SetPoint("CENTER", holder, "CENTER", 0, 0)

    else
        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")

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

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

    x = x + c.w
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 GetDundunWeekly(charKey)
    if PT.AccountOverview_Data
       and PT.AccountOverview_Data.GetDundunWeekly
    then
        return PT.AccountOverview_Data:GetDundunWeekly(charKey)
    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")

    AhUI_Config = AhUI_Config or {}
    local savedW = AhUI_Config.accountOverviewWidth or SIZE_W
    local savedH = AhUI_Config.accountOverviewHeight or SIZE_H

    f:SetSize(savedW, savedH)
    f:SetPoint("CENTER")

    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:SetResizable(true)
    f:SetResizeBounds(MIN_W, MIN_H, MAX_W, MAX_H)
    f:SetUserPlaced(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)

    f.isResizing = false

    local resizeBtn = CreateFrame("Frame", nil, f)
    resizeBtn:SetSize(16, 16)
    resizeBtn:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -4, 4)
    resizeBtn:SetFrameStrata(f:GetFrameStrata())
    resizeBtn:SetFrameLevel(f:GetFrameLevel() + 50)
    resizeBtn:EnableMouse(true)

    local resizeTex = resizeBtn:CreateTexture(nil, "OVERLAY")
    resizeTex:SetPoint("TOPLEFT", resizeBtn, "TOPLEFT", 2, -2)
    resizeTex:SetPoint("BOTTOMRIGHT", resizeBtn, "BOTTOMRIGHT", -2, 2)
    resizeTex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
    resizeTex:SetAlpha(0.85)

    resizeBtn:SetScript("OnMouseDown", function(self, button)
        if button ~= "LeftButton" then return end
        if InCombatLockdown() then return end

        f.isResizing = true
        f:StartSizing("BOTTOMRIGHT")
        resizeTex:SetAlpha(1)
    end)

    resizeBtn:SetScript("OnMouseUp", function(self, button)
        if button ~= "LeftButton" then return end

        f:StopMovingOrSizing()
        f.isResizing = false

        AhUI_Config = AhUI_Config or {}
        AhUI_Config.accountOverviewWidth = math.floor(f:GetWidth())
        AhUI_Config.accountOverviewHeight = math.floor(f:GetHeight())

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

        UI:RebuildHeader()
        UI:Refresh()
        resizeTex:SetAlpha(0.85)
    end)

    resizeBtn:SetScript("OnEnter", function()
        resizeTex:SetAlpha(1)
    end)

    resizeBtn:SetScript("OnLeave", function()
        if not IsMouseButtonDown("LeftButton") then
            resizeTex:SetAlpha(0.85)
        end
    end)

    f.resizeBtn = resizeBtn

    f:SetScript("OnSizeChanged", function(self)
        if self.content and self.scroll then
            self.content:SetWidth(math.max(1, self.scroll:GetWidth()))
        end
        UI:RebuildHeader()
    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(40, 40)
	filter:SetPoint("RIGHT", reset, "LEFT", 3, -1)
	filter:EnableMouse(true)

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

	local filterIcon = filter:CreateTexture(nil, "ARTWORK")
	filterIcon:SetAllPoints()
	filterIcon:SetAtlas("GM-icon-settings")

	filter.icon = filterIcon
	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(math.max(1, parent:GetWidth()))
		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 visibleCols = GetVisibleColumns(f:GetWidth(), filters)
		local x = 8

		for _, c in ipairs(visibleCols) do
			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 == "char" then
					fs:SetFont(FONT, 12, "")
					fs:SetPoint("LEFT", row, "LEFT", x, 0)
					fs:SetWidth(c.w)
					fs:SetJustifyH("LEFT")

				elseif 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)

                elseif c.key == "dundun" then
                    fs:SetFont(FONT, 18, "OUTLINE")
                    fs:SetPoint("LEFT", row, "LEFT", x, 0)
                    fs:SetWidth(c.w)
                    fs:SetJustifyH("CENTER")
                    fs:EnableMouse(true)
                    fs:SetScript("OnEnter", function(self)
                        UI:ShowDundunTooltip(self, row.charKey)
                    end)
                    fs:SetScript("OnLeave", function()
                        UI:HideDundunTooltip()
                    end)

				else
					fs:SetFont(FONT, 12, "")
					fs:SetPoint("LEFT", row, "LEFT", x, 0)
					fs:SetWidth(c.w)
					fs:SetJustifyH("CENTER")
				end

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

			x = x + c.w
		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.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 _, 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:ShowAtCursor(16, 16)
                    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:AddIcon(12)
				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:ShowAtCursor(16, 16)
end

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

function UI:ShowDundunTooltip(cell, charKey)
    local Tooltip = PT.Tooltip
    local D = PT.AccountOverview_Data

    if not Tooltip or not D or not charKey then
        return
    end

    local data = D:GetDundunWeekly(charKey)
    if type(data) ~= "table" then
        return
    end

    local amount   = tonumber(data.value) or 0
    local maxVal   = tonumber(data.max) or 8
    local quantity = tonumber(data.quantity) or 0

    Tooltip:Clear()
    Tooltip:AddLine(L["AO_HDR_DUNDUN"] or "Dundun", 14, 1, 1, 1)
    Tooltip:AddLine((L["AO_DUNDUN_EARNED"] or "Earned this week") .. ": " .. amount, 12, 1, 1, 1)
    Tooltip:AddLine((L["AO_DUNDUN_MAX"] or "Weekly maximum") .. ": " .. maxVal, 12, 1, 1, 1)
    Tooltip:AddLine((L["AO_DUNDUN_CURRENT"] or "Current currency") .. ": " .. quantity, 12, 1, 1, 1)

    Tooltip:ShowAtCursor(16, 16)
end

function UI:HideDundunTooltip()
    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:ShowAtCursor(16, 16)
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 not self.concNotifyFrame then
            local f = CreateFrame("Button", nil, UIParent, "BackdropTemplate")
            f:SetFrameStrata("DIALOG")
            f:SetClampedToScreen(true)
            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()

                AhUI_DB = AhUI_DB or {}
                AhUI_DB.concNotifyPos = {
                    point = { self:GetPoint() }
                }
            end)

            self.concNotifyFrame = f
        end

        local f = self.concNotifyFrame

        f:ClearAllPoints()
        if AhUI_DB and AhUI_DB.concNotifyPos and AhUI_DB.concNotifyPos.point then
            f:SetPoint(unpack(AhUI_DB.concNotifyPos.point))
        else
            f:SetPoint("TOP", UIParent, "TOP", 0, -120)
        end

        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)

        if f._regions then
            for _, r in ipairs(f._regions) do
                r:Hide()
                r:SetParent(nil)
            end
        end
        f._regions = {}

        local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
        close:SetPoint("TOPRIGHT", -4, -4)
        close:SetScale(0.8)
        close:SetScript("OnClick", function() f:Hide() end)
        table.insert(f._regions, close)

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

        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)
        table.insert(f._regions, title)

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

        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"))
        text:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -TITLE_GAP)

        table.insert(f._regions, text)

        local finalW = math.max(NOTIFY_MIN_W, math.min(NOTIFY_MAX_W, text:GetStringWidth() + 100))
        local finalH = title:GetStringHeight() + TITLE_GAP + text:GetStringHeight() + NOTIFY_PAD_Y * 2

        f:SetSize(finalW, finalH)

        f:SetScript("OnClick", function()
            UI:Toggle()
            f:Hide()
        end)

        f:Show()

        if self._notifyTimer then
            self._notifyTimer:Cancel()
        end

        self._notifyTimer = C_Timer.NewTimer(18, function()
            f:Hide()
        end)
    end
end

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

    Tooltip:Clear()

    Tooltip.frame.icons = Tooltip.frame.icons or {}

    for _, tex in ipairs(Tooltip.frame.icons) do
        if tex then
            tex:Hide()
            tex:SetParent(nil)
        end
    end
    wipe(Tooltip.frame.icons)

    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:AddIcon(12)
        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)
        icon:Show()
    end

    Tooltip:ShowAtCursor(16, 16)
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
            local isCurrent = (charKey == GetCharacterKey())

            if not row.deleteBtn then
                local btn = CreateFrame("Button", nil, row)
                btn:SetSize(14, 14)

                local tex = btn:CreateTexture(nil, "OVERLAY")
                tex:SetAllPoints()
                tex:SetAtlas("common-icon-redx")

                btn.tex = tex
                row.deleteBtn = btn
            end

            if not isCurrent then
                row.deleteBtn:ClearAllPoints()
                row.deleteBtn:SetPoint("LEFT", row.cells.char, "LEFT", -10, 0)
                row.deleteBtn:Show()

                row.deleteBtn:SetScript("OnClick", function()
                    UI:ConfirmDeleteCharacter(charKey)
                end)

                row.cells.char:SetText("   " .. charKey)
            else
                row.deleteBtn:Hide()
                row.cells.char:SetText(charKey)
            end
        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_tww 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")
            if row.cells.kp_df then
                SetWeeklyKPCell(row.cells.kp_df, data)
            end
        end

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

        do
            local data = GetWeeklyStatus(charKey, "mid")
            if row.cells.kp_mid then
                SetWeeklyKPCell(row.cells.kp_mid, data)
            end
        end

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

        do
            local cell = row.cells.dundun
            if cell then
                local data = GetDundunWeekly(charKey)

                if type(data) ~= "table" then
                    cell:SetText("—")
                    cell:SetTextColor(1, 1, 1)
                else
                    local amount = tonumber(data.value) or 0
                    local maxVal = tonumber(data.max) or 8
                    local quantity = tonumber(data.quantity) or 0

                    cell:SetText(string.format("%d/%d(%d)", amount, maxVal, quantity))

                    if amount >= maxVal then
                        cell:SetTextColor(0.2, 1.0, 0.2)
                    else
                        local r, g, b = GetProgressColor(amount, maxVal)
                        cell:SetTextColor(r, g, b)
                    end
                end
            end
        end

        do
            local data = PT.AccountOverview_Data:GetTimewalkingStatus(charKey)
            if row.cells.tw then
                SetTWCell(row.cells.tw, data)
            end
        end

        y = y - ROW_H
    end

    f.content:SetWidth(math.max(1, f.scroll:GetWidth()))
    f.content:SetHeight(-y + 4)
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

function UI:ConfirmDeleteCharacter(charKey)
    StaticPopupDialogs["AHUI_AO_DELETE_CHAR"] = {
        text = string.format(
            L["AO_DELETE_CHAR_TEXT"] or "Remove character?\n\n%s",
            charKey or ""
        ),
        button1 = YES,
        button2 = NO,
        OnAccept = function()
            if AhUI_DB
               and AhUI_DB.accountOverview
               and AhUI_DB.accountOverview.chars
            then
                AhUI_DB.accountOverview.chars[charKey] = nil
            end
            UI:Refresh()
        end,
        timeout = 0,
        whileDead = true,
        hideOnEscape = true,
        preferredIndex = 3,
    }

    StaticPopup_Show("AHUI_AO_DELETE_CHAR")
end