--------------------------------------------------------------------------------
-- Nanami-UI: Quest & NPC Interaction UI (QuestUI.lua)
-- Replaces GossipFrame and QuestFrame with Nanami-UI styled interface
--------------------------------------------------------------------------------

SFrames = SFrames or {}
SFrames.QuestUI = {}
local QUI = SFrames.QuestUI
SFramesDB = SFramesDB or {}

--------------------------------------------------------------------------------
-- Theme: Pink Cat-Paw (matching Nanami-UI)
--------------------------------------------------------------------------------
local T = SFrames.Theme:Extend({
    questAvail  = { 0.9, 0.82, 0.18 },
    questActive = { 0.65, 0.65, 0.65 },
    questComplete = { 0.9, 0.82, 0.18 },
    moneyGold   = { 1, 0.84, 0.0 },
    moneySilver = { 0.78, 0.78, 0.78 },
    moneyCopper = { 0.71, 0.43, 0.18 },
})

local QUALITY_COLORS = {
    [0] = { 0.62, 0.62, 0.62 }, [1] = { 1, 1, 1 },
    [2] = { 0.12, 1, 0 },       [3] = { 0.0, 0.44, 0.87 },
    [4] = { 0.64, 0.21, 0.93 }, [5] = { 1, 0.5, 0 },
}

local GOSSIP_ICONS = {
    ["gossip"]       = "Interface\\GossipFrame\\GossipGossipIcon",
    ["vendor"]       = "Interface\\GossipFrame\\VendorGossipIcon",
    ["taxi"]         = "Interface\\GossipFrame\\TaxiGossipIcon",
    ["trainer"]      = "Interface\\GossipFrame\\TrainerGossipIcon",
    ["banker"]       = "Interface\\GossipFrame\\BankerGossipIcon",
    ["petition"]     = "Interface\\GossipFrame\\PetitionGossipIcon",
    ["tabard"]       = "Interface\\GossipFrame\\TabardGossipIcon",
    ["battlemaster"] = "Interface\\GossipFrame\\BattleMasterGossipIcon",
}

local QUEST_ICON_AVAILABLE = "Interface\\GossipFrame\\AvailableQuestIcon"
local QUEST_ICON_ACTIVE    = "Interface\\GossipFrame\\ActiveQuestIcon"

local GOSSIP_TRIGGER_TEXTS = {
    ["TRANSMOG_TRIGGER"] = true,
}

--------------------------------------------------------------------------------
-- Layout
--------------------------------------------------------------------------------
local FRAME_W       = 340
local FRAME_H       = 400
local HEADER_H      = 34
local BOTTOM_H      = 42
local SIDE_PAD      = 14
local CONTENT_W     = FRAME_W - SIDE_PAD * 2
local ITEM_SIZE     = 36
local ITEM_GAP      = 4
local SCROLL_STEP   = 40
local MAX_OPTIONS   = 24
local MAX_ITEMS     = 10

--------------------------------------------------------------------------------
-- State
--------------------------------------------------------------------------------
local MainFrame     = nil
local currentPage   = nil
local previousPage  = nil
local selectedReward = 0
local pendingClose  = false
local closeTimer    = 0
local pages = {}

--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
local function GetFont()
    if SFrames and SFrames.GetFont then return SFrames:GetFont() end
    return "Fonts\\ARIALN.TTF"
end

local function SetRoundBackdrop(frame)
    frame:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    frame:SetBackdropColor(T.panelBg[1], T.panelBg[2], T.panelBg[3], T.panelBg[4])
    frame:SetBackdropBorderColor(T.panelBorder[1], T.panelBorder[2], T.panelBorder[3], T.panelBorder[4])
end

local function CreateShadow(parent)
    local s = CreateFrame("Frame", nil, parent)
    s:SetPoint("TOPLEFT", parent, "TOPLEFT", -4, 4)
    s:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 4, -4)
    s:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 16,
        insets = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    s:SetBackdropColor(0, 0, 0, 0.45)
    s:SetBackdropBorderColor(0, 0, 0, 0.6)
    s:SetFrameLevel(math.max(0, parent:GetFrameLevel() - 1))
    return s
end

local function MakeSep(parent, width)
    local sep = parent:CreateTexture(nil, "ARTWORK")
    sep:SetTexture("Interface\\Buttons\\WHITE8X8")
    sep:SetWidth(width or CONTENT_W)
    sep:SetHeight(1)
    sep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])
    return sep
end

local function GetDiffColor(level)
    if GetDifficultyColor then
        local c = GetDifficultyColor(level)
        if c then return c.r, c.g, c.b end
    end
    local pLvl = UnitLevel("player") or 60
    local diff = (level or pLvl) - pLvl
    if diff >= 5 then return 1, 0.1, 0.1
    elseif diff >= 3 then return 1, 0.5, 0.25
    elseif diff >= -2 then return 1, 1, 0
    else return 0.25, 0.75, 0.25
    end
end

local function TextHeight(fs, fallback)
    if not fs then return fallback or 14 end
    local h = fs.GetStringHeight and fs:GetStringHeight()
    if h and h > 0 then return h end
    h = fs:GetHeight()
    if h and h > 1 then return h end
    return fallback or 14
end

local function ForwardScrollWheel(frame)
    frame:EnableMouseWheel(true)
    frame:SetScript("OnMouseWheel", function()
        local p = this:GetParent()
        while p do
            if p:GetObjectType() == "ScrollFrame" then
                local cur = p:GetVerticalScroll()
                local maxVal = p:GetVerticalScrollRange()
                if arg1 > 0 then
                    p:SetVerticalScroll(math.max(0, cur - SCROLL_STEP))
                else
                    p:SetVerticalScroll(math.min(maxVal, cur + SCROLL_STEP))
                end
                return
            end
            p = p:GetParent()
        end
    end)
end

local function FormatMoney(copper)
    if not copper or copper <= 0 then return nil end
    local g = math.floor(copper / 10000)
    local s = math.floor(math.mod(copper, 10000) / 100)
    local c = math.mod(copper, 100)
    return g, s, c
end

local LINK_QUALITY_MAP = {
    ["9d9d9d"] = 0, ["ffffff"] = 1, ["1eff00"] = 2,
    ["0070dd"] = 3, ["a335ee"] = 4, ["ff8000"] = 5,
    ["e6cc80"] = 6,
}

local function QualityFromLink(link)
    if not link then return nil end
    local _, _, hex = string.find(link, "|c(%x%x%x%x%x%x%x%x)|")
    if hex and string.len(hex) == 8 then
        local color = string.lower(string.sub(hex, 3, 8))
        return LINK_QUALITY_MAP[color]
    end
    return nil
end

local function GetItemQuality(itemType, index)
    local name, texture, numItems, quality, isUsable = GetQuestItemInfo(itemType, index)
    if quality and quality > 0 then return quality end
    local link
    if GetQuestItemLink then
        link = GetQuestItemLink(itemType, index)
    end
    if link then
        if GetItemInfo then
            local _, _, q = GetItemInfo(link)
            if q and q > 0 then return q end
        end
        local q = QualityFromLink(link)
        if q then return q end
    end
    return nil
end
--------------------------------------------------------------------------------
-- Scroll Area Factory
--------------------------------------------------------------------------------
local function CreateScrollArea(parent, name)
    local scroll = CreateFrame("ScrollFrame", name, parent)
    local content = CreateFrame("Frame", name .. "Content", scroll)
    content:SetWidth(CONTENT_W)
    content:SetHeight(1)
    scroll:SetScrollChild(content)

    scroll:EnableMouseWheel(true)
    scroll:SetScript("OnMouseWheel", function()
        local cur = this:GetVerticalScroll()
        local maxVal = this:GetVerticalScrollRange()
        if arg1 > 0 then
            this:SetVerticalScroll(math.max(0, cur - SCROLL_STEP))
        else
            this:SetVerticalScroll(math.min(maxVal, cur + SCROLL_STEP))
        end
    end)

    scroll.content = content
    return scroll
end

--------------------------------------------------------------------------------
-- Action Button Factory
--------------------------------------------------------------------------------
local function CreateActionBtn(parent, text, w)
    local btn = CreateFrame("Button", nil, parent)
    btn:SetWidth(w or 100)
    btn:SetHeight(28)
    btn:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    btn:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
    btn:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])

    local fs = btn:CreateFontString(nil, "OVERLAY")
    fs:SetFont(GetFont(), 11, "OUTLINE")
    fs:SetPoint("CENTER", 0, 0)
    fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
    fs:SetText(text)
    btn.label = fs

    btn.disabled = false
    function btn:SetDisabled(flag)
        self.disabled = flag
        if flag then
            self.label:SetTextColor(T.btnDisabledText[1], T.btnDisabledText[2], T.btnDisabledText[3])
            self:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], 0.5)
            self:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], 0.5)
        else
            self.label:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
            self:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
            self:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
        end
    end

    btn:SetScript("OnEnter", function()
        if not this.disabled then
            this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
            this:SetBackdropBorderColor(T.btnHoverBd[1], T.btnHoverBd[2], T.btnHoverBd[3], T.btnHoverBd[4])
            this.label:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3])
        end
    end)
    btn:SetScript("OnLeave", function()
        if not this.disabled then
            this:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
            this:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
            this.label:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
        end
    end)
    btn:SetScript("OnMouseDown", function()
        if not this.disabled then
            this:SetBackdropColor(T.btnDownBg[1], T.btnDownBg[2], T.btnDownBg[3], T.btnDownBg[4])
        end
    end)
    btn:SetScript("OnMouseUp", function()
        if not this.disabled then
            this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
        end
    end)

    return btn
end

--------------------------------------------------------------------------------
-- Gossip / Quest Option Button Factory
--------------------------------------------------------------------------------
local function CreateOptionButton(parent)
    local btn = CreateFrame("Button", nil, parent)
    btn:SetHeight(22)
    btn:SetWidth(CONTENT_W)

    local icon = btn:CreateTexture(nil, "ARTWORK")
    icon:SetWidth(16)
    icon:SetHeight(16)
    icon:SetPoint("LEFT", btn, "LEFT", 2, 0)
    btn.icon = icon

    local text = btn:CreateFontString(nil, "OVERLAY")
    text:SetFont(GetFont(), 12)
    text:SetPoint("LEFT", icon, "RIGHT", 6, 0)
    text:SetPoint("RIGHT", btn, "RIGHT", -4, 0)
    text:SetJustifyH("LEFT")
    text:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
    btn.text = text

    btn:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight", "ADD")

    btn.normalR = T.nameText[1]
    btn.normalG = T.nameText[2]
    btn.normalB = T.nameText[3]

    btn:SetScript("OnEnter", function()
        this.text:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
    end)
    btn:SetScript("OnLeave", function()
        this.text:SetTextColor(this.normalR, this.normalG, this.normalB)
    end)

    ForwardScrollWheel(btn)
    return btn
end
--------------------------------------------------------------------------------
-- Item Slot Factory
--------------------------------------------------------------------------------
local function CreateItemSlot(parent)
    local slot = CreateFrame("Button", nil, parent)
    slot:SetWidth(CONTENT_W)
    slot:SetHeight(ITEM_SIZE + 4)

    -- Selection highlight background (covers entire row)
    local selBg = slot:CreateTexture(nil, "BACKGROUND")
    selBg:SetTexture("Interface\\Buttons\\WHITE8X8")
    selBg:SetAllPoints(slot)
    selBg:SetVertexColor(T.questSelected[1], T.questSelected[2], T.questSelected[3], T.questSelected[4] or 0.45)
    selBg:Hide()
    slot.selBg = selBg

    -- Selection glow border around entire row
    local selGlow = CreateFrame("Frame", nil, slot)
    selGlow:SetPoint("TOPLEFT", slot, "TOPLEFT", -2, 2)
    selGlow:SetPoint("BOTTOMRIGHT", slot, "BOTTOMRIGHT", 2, -2)
    selGlow:SetBackdrop({
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 12,
        insets = { left = 2, right = 2, top = 2, bottom = 2 },
    })
    selGlow:SetBackdropBorderColor(T.questSelBorder[1], T.questSelBorder[2], T.questSelBorder[3], T.questSelBorder[4] or 0.9)
    selGlow:Hide()
    slot.selGlow = selGlow

    local iconFrame = CreateFrame("Frame", nil, slot)
    iconFrame:SetWidth(ITEM_SIZE)
    iconFrame:SetHeight(ITEM_SIZE)
    iconFrame:SetPoint("LEFT", slot, "LEFT", 0, 0)
    iconFrame:SetBackdrop({
        bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 2, right = 2, top = 2, bottom = 2 },
    })
    iconFrame:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
    iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
    slot.iconFrame = iconFrame

    local icon = iconFrame:CreateTexture(nil, "ARTWORK")
    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    icon:SetPoint("TOPLEFT", iconFrame, "TOPLEFT", 3, -3)
    icon:SetPoint("BOTTOMRIGHT", iconFrame, "BOTTOMRIGHT", -3, 3)
    slot.icon = icon

    local qualGlow = iconFrame:CreateTexture(nil, "OVERLAY")
    qualGlow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
    qualGlow:SetBlendMode("ADD")
    qualGlow:SetAlpha(0.7)
    qualGlow:SetWidth(ITEM_SIZE * 1.8)
    qualGlow:SetHeight(ITEM_SIZE * 1.8)
    qualGlow:SetPoint("CENTER", iconFrame, "CENTER", 0, 0)
    qualGlow:Hide()
    slot.qualGlow = qualGlow

    local countFS = iconFrame:CreateFontString(nil, "OVERLAY")
    countFS:SetFont("Fonts\\ARIALN.TTF", 11, "OUTLINE")
    countFS:SetPoint("BOTTOMRIGHT", iconFrame, "BOTTOMRIGHT", -2, 2)
    countFS:SetJustifyH("RIGHT")
    slot.countFS = countFS

    local nameFS = slot:CreateFontString(nil, "OVERLAY")
    nameFS:SetFont(GetFont(), 12, "OUTLINE")
    nameFS:SetPoint("LEFT", iconFrame, "RIGHT", 8, 0)
    nameFS:SetPoint("RIGHT", slot, "RIGHT", -4, 0)
    nameFS:SetJustifyH("LEFT")
    nameFS:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
    slot.nameFS = nameFS

    slot.itemType = nil
    slot.itemIndex = nil

    slot:SetScript("OnEnter", function()
        if this.itemType and this.itemIndex then
            GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
            GameTooltip:SetQuestItem(this.itemType, this.itemIndex)
            GameTooltip:Show()
        end
        this.iconFrame:SetBackdropBorderColor(T.slotHover[1], T.slotHover[2], T.slotHover[3], T.slotHover[4])
    end)
    slot:SetScript("OnLeave", function()
        GameTooltip:Hide()
        if this.selected then
            this.iconFrame:SetBackdropBorderColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], T.slotSelected[4])
        else
            local qc = QUALITY_COLORS[this._quality]
            if qc and this._quality and this._quality >= 2 then
                this.iconFrame:SetBackdropBorderColor(qc[1], qc[2], qc[3], 1)
            else
                this.iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
            end
        end
    end)

    function slot:ApplyItemData(name, texture, numItems, quality)
        self.icon:SetTexture(texture)
        self.nameFS:SetText(name or "")
        if numItems and numItems > 1 then
            self.countFS:SetText(numItems)
        else
            self.countFS:SetText("")
        end
        self._quality = quality
        local qc = QUALITY_COLORS[quality]
        if qc and quality and quality >= 2 then
            self.qualGlow:SetVertexColor(qc[1], qc[2], qc[3])
            self.qualGlow:Show()
            self.nameFS:SetTextColor(qc[1], qc[2], qc[3])
            self.iconFrame:SetBackdropBorderColor(qc[1], qc[2], qc[3], 1)
        else
            self.qualGlow:Hide()
            self.nameFS:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
            self.iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
        end
    end

    function slot:SetItemInfo(iType, idx)
        self.itemType = iType
        self.itemIndex = idx
        local name, texture, numItems, quality, isUsable = GetQuestItemInfo(iType, idx)
        if not quality or quality <= 0 then
            quality = GetItemQuality(iType, idx)
        end
        self:ApplyItemData(name, texture, numItems, quality or 1)
        self:Show()

        self:SetScript("OnUpdate", nil)
        local needRetry = (not name or name == "") or (quality == nil) or (not texture or texture == "")
        if needRetry then
            self._retryElapsed = 0
            self._retryAttempts = 0
            self:SetScript("OnUpdate", function()
                this._retryElapsed = (this._retryElapsed or 0) + (arg1 or 0)
                if this._retryElapsed < 0.2 then return end
                this._retryElapsed = 0
                this._retryAttempts = (this._retryAttempts or 0) + 1
                if this._retryAttempts > 15 then
                    this:SetScript("OnUpdate", nil)
                    return
                end
                if this.itemType and this.itemIndex then
                    local n, t, ni, q = GetQuestItemInfo(this.itemType, this.itemIndex)
                    if not q or q <= 0 then q = GetItemQuality(this.itemType, this.itemIndex) end
                    local nameOk = n and n ~= ""
                    local texOk = t and t ~= ""
                    local qualOk = q ~= nil
                    if nameOk and texOk and qualOk then
                        this:ApplyItemData(n, t, ni, q)
                        this:SetScript("OnUpdate", nil)
                    elseif this._retryAttempts >= 15 then
                        if nameOk then
                            this:ApplyItemData(n, t, ni, q or 1)
                        end
                        this:SetScript("OnUpdate", nil)
                    end
                end
            end)
        end
    end

    function slot:SetSelected(flag)
        self.selected = flag
        if flag then
            self.iconFrame:SetBackdropBorderColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], T.slotSelected[4])
            self.iconFrame:SetBackdropColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], T.slotSelected[4] or 0.95)
            self.selBg:Show()
            self.selGlow:Show()
            self.nameFS:SetTextColor(T.title[1], T.title[2], T.title[3])
        else
            local qc = QUALITY_COLORS[self._quality]
            if qc and self._quality and self._quality >= 2 then
                self.iconFrame:SetBackdropBorderColor(qc[1], qc[2], qc[3], 1)
                self.nameFS:SetTextColor(qc[1], qc[2], qc[3])
            else
                self.iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
                self.nameFS:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
            end
            self.iconFrame:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
            self.selBg:Hide()
            self.selGlow:Hide()
        end
    end

    function slot:Clear()
        self:SetScript("OnUpdate", nil)
        self.itemType = nil
        self.itemIndex = nil
        self._quality = nil
        self.icon:SetTexture(nil)
        self.nameFS:SetText("")
        self.countFS:SetText("")
        self.qualGlow:Hide()
        self.nameFS:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
        self.selected = false
        self.iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
        self.iconFrame:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
        self.selBg:Hide()
        self.selGlow:Hide()
        self:Hide()
    end

    ForwardScrollWheel(slot)
    return slot
end
--------------------------------------------------------------------------------
-- Money Display
--------------------------------------------------------------------------------
local function CreateMoneyLine(parent)
    local frame = CreateFrame("Frame", nil, parent)
    frame:SetWidth(CONTENT_W)
    frame:SetHeight(16)

    local font = GetFont()

    local gTxt = frame:CreateFontString(nil, "OVERLAY")
    gTxt:SetFont(font, 12, "OUTLINE")
    local gTex = frame:CreateTexture(nil, "ARTWORK")
    gTex:SetTexture("Interface\\MoneyFrame\\UI-MoneyIcons")
    gTex:SetTexCoord(0, 0.25, 0, 1)
    gTex:SetWidth(13); gTex:SetHeight(13)

    local sTxt = frame:CreateFontString(nil, "OVERLAY")
    sTxt:SetFont(font, 12, "OUTLINE")
    local sTex = frame:CreateTexture(nil, "ARTWORK")
    sTex:SetTexture("Interface\\MoneyFrame\\UI-MoneyIcons")
    sTex:SetTexCoord(0.25, 0.5, 0, 1)
    sTex:SetWidth(13); sTex:SetHeight(13)

    local cTxt = frame:CreateFontString(nil, "OVERLAY")
    cTxt:SetFont(font, 12, "OUTLINE")
    local cTex = frame:CreateTexture(nil, "ARTWORK")
    cTex:SetTexture("Interface\\MoneyFrame\\UI-MoneyIcons")
    cTex:SetTexCoord(0.5, 0.75, 0, 1)
    cTex:SetWidth(13); cTex:SetHeight(13)

    frame.gTxt = gTxt; frame.gTex = gTex
    frame.sTxt = sTxt; frame.sTex = sTex
    frame.cTxt = cTxt; frame.cTex = cTex

    function frame:SetMoney(copper)
        local g, s, c = FormatMoney(copper)
        if not g then self:Hide(); return end
        local x = 0
        if g > 0 then
            self.gTxt:SetText(g)
            self.gTxt:SetTextColor(T.moneyGold[1], T.moneyGold[2], T.moneyGold[3])
            self.gTxt:ClearAllPoints()
            self.gTxt:SetPoint("LEFT", self, "LEFT", x, 0)
            x = x + self.gTxt:GetStringWidth() + 1
            self.gTex:ClearAllPoints()
            self.gTex:SetPoint("LEFT", self, "LEFT", x, 0)
            x = x + 15
            self.gTxt:Show(); self.gTex:Show()
        else
            self.gTxt:Hide(); self.gTex:Hide()
        end
        if s > 0 or g > 0 then
            self.sTxt:SetText(s)
            self.sTxt:SetTextColor(T.moneySilver[1], T.moneySilver[2], T.moneySilver[3])
            self.sTxt:ClearAllPoints()
            self.sTxt:SetPoint("LEFT", self, "LEFT", x, 0)
            x = x + self.sTxt:GetStringWidth() + 1
            self.sTex:ClearAllPoints()
            self.sTex:SetPoint("LEFT", self, "LEFT", x, 0)
            x = x + 15
            self.sTxt:Show(); self.sTex:Show()
        else
            self.sTxt:Hide(); self.sTex:Hide()
        end
        if c > 0 or (g == 0 and s == 0) then
            self.cTxt:SetText(c)
            self.cTxt:SetTextColor(T.moneyCopper[1], T.moneyCopper[2], T.moneyCopper[3])
            self.cTxt:ClearAllPoints()
            self.cTxt:SetPoint("LEFT", self, "LEFT", x, 0)
            x = x + self.cTxt:GetStringWidth() + 1
            self.cTex:ClearAllPoints()
            self.cTex:SetPoint("LEFT", self, "LEFT", x, 0)
            self.cTxt:Show(); self.cTex:Show()
        else
            self.cTxt:Hide(); self.cTex:Hide()
        end
        self:Show()
    end

    return frame
end

--------------------------------------------------------------------------------
-- Gossip data parsers
--------------------------------------------------------------------------------
local function ParseGossipAvailableQuests()
    local data = { GetGossipAvailableQuests() }
    local out = {}
    local i = 1
    while i <= table.getn(data) do
        if type(data[i]) == "string" then
            table.insert(out, { title = data[i], level = tonumber(data[i + 1]) or 0 })
            i = i + 2
        else
            i = i + 1
        end
    end
    return out
end

local function ParseGossipActiveQuests()
    local data = { GetGossipActiveQuests() }
    local out = {}
    local i = 1
    while i <= table.getn(data) do
        if type(data[i]) == "string" then
            local q = { title = data[i], level = tonumber(data[i + 1]) or 0 }
            if i + 2 <= table.getn(data) and type(data[i + 2]) ~= "string" then
                q.isComplete = data[i + 2]
                i = i + 3
            else
                i = i + 2
            end
            table.insert(out, q)
        else
            i = i + 1
        end
    end
    return out
end

local function ParseGossipOptions()
    local data = { GetGossipOptions() }
    local out = {}
    local i = 1
    while i <= table.getn(data) do
        if i + 1 <= table.getn(data) then
            table.insert(out, { title = data[i], gtype = data[i + 1] or "gossip" })
        end
        i = i + 2
    end
    return out
end

--------------------------------------------------------------------------------
-- AutoGossip logic (integrated from AutoGossip.lua)
--------------------------------------------------------------------------------
local function TryAutoGossip()
    if SFramesDB and SFramesDB.autoGossip == false then return false end
    if IsShiftKeyDown() then return false end

    local active = { GetGossipActiveQuests() }
    local avail  = { GetGossipAvailableQuests() }
    local opts   = { GetGossipOptions() }

    local nActive = table.getn(active) or 0
    local nAvail  = table.getn(avail)  or 0
    local nOpts   = table.getn(opts)   or 0

    if nActive == 0 and nAvail == 0 and nOpts == 2 then
        SelectGossipOption(1)
        return true
    end
    return false
end
--------------------------------------------------------------------------------
-- Page Management
--------------------------------------------------------------------------------
local function HideAllPages()
    for _, p in pairs(pages) do
        if p and p.Hide then p:Hide() end
    end
end

local function ShowPage(name)
    HideAllPages()
    currentPage = name
    if pages[name] then
        pages[name]:Show()
        if pages[name].scroll then
            pages[name].scroll:SetVerticalScroll(0)
        end
    end
    if MainFrame then
        MainFrame.btn1:Hide()
        MainFrame.btn2:Hide()
    end
end

local function GoBack()
    if DeclineQuest then DeclineQuest() end
end

local function CloseFrame()
    currentPage = nil
    MainFrame:Hide()
end

local function UpdateBottomButtons()
    if not MainFrame then return end
    local bL = MainFrame.btn1
    local bR = MainFrame.btn2
    local hasBack = previousPage and (currentPage ~= "gossip") and (currentPage ~= "greeting")

    if currentPage == "gossip" or currentPage == "greeting" then
        bL:Hide()
        bR.label:SetText("关闭")
        bR:SetDisabled(false)
        bR:SetScript("OnClick", function()
            if currentPage == "gossip" then
                if CloseGossip then CloseGossip() end
            end
            currentPage = nil
            MainFrame:Hide()
        end)
        bR:Show()

    elseif currentPage == "detail" then
        bL.label:SetText("接受")
        bL:SetDisabled(false)
        bL:SetScript("OnClick", function()
            if AcceptQuest then AcceptQuest() end
        end)
        bL:Show()

        bR.label:SetText(hasBack and "返回" or "拒绝")
        bR:SetDisabled(false)
        bR:SetScript("OnClick", function()
            if hasBack then
                GoBack()
            else
                if DeclineQuest then DeclineQuest() end
            end
        end)
        bR:Show()

    elseif currentPage == "progress" then
        local canComplete = IsQuestCompletable and IsQuestCompletable()
        bL.label:SetText("继续")
        bL:SetDisabled(not canComplete)
        bL:SetScript("OnClick", function()
            if not this.disabled and CompleteQuest then
                CompleteQuest()
            end
        end)
        bL:Show()

        bR.label:SetText(hasBack and "返回" or "关闭")
        bR:SetDisabled(false)
        bR:SetScript("OnClick", function()
            if hasBack then
                GoBack()
            else
                CloseFrame()
            end
        end)
        bR:Show()

    elseif currentPage == "complete" then
        local numChoices = GetNumQuestChoices and GetNumQuestChoices() or 0
        local canFinish = (numChoices == 0) or (selectedReward > 0)
        bL.label:SetText("完成任务")
        bL:SetDisabled(not canFinish)
        bL:SetScript("OnClick", function()
            if this.disabled then return end
            if GetQuestReward then
                local nc = GetNumQuestChoices and GetNumQuestChoices() or 0
                if nc > 0 then
                    GetQuestReward(selectedReward)
                else
                    GetQuestReward()
                end
            end
        end)
        bL:Show()

        bR.label:SetText(hasBack and "返回" or "关闭")
        bR:SetDisabled(false)
        bR:SetScript("OnClick", function()
            if hasBack then
                GoBack()
            else
                CloseFrame()
            end
        end)
        bR:Show()
    end
end
--------------------------------------------------------------------------------
-- GOSSIP PAGE
--------------------------------------------------------------------------------
local gossipOptionBtns = {}
local gossipSectionLabels = {}

local function CreateGossipPage(parent)
    local page = CreateFrame("Frame", nil, parent)
    page:SetAllPoints(parent)
    page:Hide()

    local scroll = CreateScrollArea(page, "SFramesQuestGossipScroll")
    scroll:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    scroll:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
    page.scroll = scroll

    local content = scroll.content

    local npcText = content:CreateFontString(nil, "OVERLAY")
    npcText:SetFont(GetFont(), 12)
    npcText:SetWidth(CONTENT_W - 4)
    npcText:SetJustifyH("LEFT")
    npcText:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])
    page.npcText = npcText

    for i = 1, 3 do
        local lbl = content:CreateFontString(nil, "OVERLAY")
        lbl:SetFont(GetFont(), 11, "OUTLINE")
        lbl:SetWidth(CONTENT_W)
        lbl:SetJustifyH("LEFT")
        lbl:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
        gossipSectionLabels[i] = lbl
    end

    for i = 1, MAX_OPTIONS do
        gossipOptionBtns[i] = CreateOptionButton(content)
    end

    page.Update = function()
        local y = 6
        local gossipText = GetGossipText and GetGossipText() or ""
        page.npcText:SetText(gossipText)
        page.npcText:ClearAllPoints()
        page.npcText:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.npcText, 14) + 12

        local availQuests = ParseGossipAvailableQuests()
        local activeQuests = ParseGossipActiveQuests()
        local gossipOpts = ParseGossipOptions()

        local btnIdx = 1
        for i = 1, 3 do gossipSectionLabels[i]:Hide() end
        for i = 1, MAX_OPTIONS do gossipOptionBtns[i]:Hide() end

        if table.getn(availQuests) > 0 then
            gossipSectionLabels[1]:SetText("可接任务")
            gossipSectionLabels[1]:ClearAllPoints()
            gossipSectionLabels[1]:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            gossipSectionLabels[1]:Show()
            y = y + 16

            for qi = 1, table.getn(availQuests) do
                local q = availQuests[qi]
                if btnIdx <= MAX_OPTIONS then
                    local btn = gossipOptionBtns[btnIdx]
                    btn.icon:SetTexture(QUEST_ICON_AVAILABLE)
                    local levelStr = q.level > 0 and ("[" .. q.level .. "] ") or ""
                    btn.text:SetText(levelStr .. (q.title or ""))
                    if q.level > 0 then
                        local r, g, b = GetDiffColor(q.level)
                        btn.normalR = r; btn.normalG = g; btn.normalB = b
                        btn.text:SetTextColor(r, g, b)
                    else
                        btn.normalR = T.questAvail[1]; btn.normalG = T.questAvail[2]; btn.normalB = T.questAvail[3]
                        btn.text:SetTextColor(T.questAvail[1], T.questAvail[2], T.questAvail[3])
                    end
                    btn:ClearAllPoints()
                    btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
                    local idx = qi
                    btn:SetScript("OnClick", function()
                        currentPage = nil
                        MainFrame:Hide()
                        SelectGossipAvailableQuest(idx)
                    end)
                    btn:Show()
                    y = y + 22
                    btnIdx = btnIdx + 1
                end
            end
            y = y + 4
        end

        if table.getn(activeQuests) > 0 then
            gossipSectionLabels[2]:SetText("进行中任务")
            gossipSectionLabels[2]:ClearAllPoints()
            gossipSectionLabels[2]:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            gossipSectionLabels[2]:Show()
            y = y + 16

            for qi = 1, table.getn(activeQuests) do
                local q = activeQuests[qi]
                if btnIdx <= MAX_OPTIONS then
                    local btn = gossipOptionBtns[btnIdx]
                    btn.icon:SetTexture(QUEST_ICON_ACTIVE)
                    local levelStr = q.level > 0 and ("[" .. q.level .. "] ") or ""
                    btn.text:SetText(levelStr .. (q.title or ""))
                    if q.isComplete then
                        btn.normalR = T.questComplete[1]; btn.normalG = T.questComplete[2]; btn.normalB = T.questComplete[3]
                        btn.text:SetTextColor(T.questComplete[1], T.questComplete[2], T.questComplete[3])
                    else
                        btn.normalR = T.questActive[1]; btn.normalG = T.questActive[2]; btn.normalB = T.questActive[3]
                        btn.text:SetTextColor(T.questActive[1], T.questActive[2], T.questActive[3])
                    end
                    btn:ClearAllPoints()
                    btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
                    local idx = qi
                    btn:SetScript("OnClick", function()
                        currentPage = nil
                        MainFrame:Hide()
                        SelectGossipActiveQuest(idx)
                    end)
                    btn:Show()
                    y = y + 22
                    btnIdx = btnIdx + 1
                end
            end
            y = y + 4
        end

        if table.getn(gossipOpts) > 0 then
            gossipSectionLabels[3]:SetText("对话")
            gossipSectionLabels[3]:ClearAllPoints()
            gossipSectionLabels[3]:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            gossipSectionLabels[3]:Show()
            y = y + 16

            for oi = 1, table.getn(gossipOpts) do
                local opt = gossipOpts[oi]
                if btnIdx <= MAX_OPTIONS then
                    local btn = gossipOptionBtns[btnIdx]
                    btn.icon:SetTexture(GOSSIP_ICONS[opt.gtype] or GOSSIP_ICONS["gossip"])
                    btn.text:SetText(opt.title or "")
                    btn.normalR = T.nameText[1]; btn.normalG = T.nameText[2]; btn.normalB = T.nameText[3]
                    btn.text:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
                    btn:ClearAllPoints()
                    btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
                    local idx = oi
                    btn:SetScript("OnClick", function()
                        currentPage = nil
                        MainFrame:Hide()
                        SelectGossipOption(idx)
                    end)
                    btn:Show()
                    y = y + 22
                    btnIdx = btnIdx + 1
                end
            end
        end

        content:SetHeight(y + 10)
    end

    return page
end
--------------------------------------------------------------------------------
-- GREETING PAGE (multi-quest NPC via QuestFrame)
--------------------------------------------------------------------------------
local greetingOptionBtns = {}
local greetingSectionLabels = {}

local function CreateGreetingPage(parent)
    local page = CreateFrame("Frame", nil, parent)
    page:SetAllPoints(parent)
    page:Hide()

    local scroll = CreateScrollArea(page, "SFramesQuestGreetingScroll")
    scroll:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    scroll:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
    page.scroll = scroll

    local content = scroll.content

    local npcText = content:CreateFontString(nil, "OVERLAY")
    npcText:SetFont(GetFont(), 12)
    npcText:SetWidth(CONTENT_W - 4)
    npcText:SetJustifyH("LEFT")
    npcText:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])
    page.npcText = npcText

    for i = 1, 2 do
        local lbl = content:CreateFontString(nil, "OVERLAY")
        lbl:SetFont(GetFont(), 11, "OUTLINE")
        lbl:SetWidth(CONTENT_W)
        lbl:SetJustifyH("LEFT")
        lbl:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
        greetingSectionLabels[i] = lbl
    end

    for i = 1, MAX_OPTIONS do
        greetingOptionBtns[i] = CreateOptionButton(content)
    end

    page.Update = function()
        local y = 6
        local greeting = GetGreetingText and GetGreetingText() or ""
        page.npcText:SetText(greeting)
        page.npcText:ClearAllPoints()
        page.npcText:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.npcText, 14) + 12

        local numAvail  = GetNumAvailableQuests and GetNumAvailableQuests() or 0
        local numActive = GetNumActiveQuests and GetNumActiveQuests() or 0

        local btnIdx = 1
        for i = 1, 2 do greetingSectionLabels[i]:Hide() end
        for i = 1, MAX_OPTIONS do greetingOptionBtns[i]:Hide() end

        if numAvail > 0 then
            greetingSectionLabels[1]:SetText("可接任务")
            greetingSectionLabels[1]:ClearAllPoints()
            greetingSectionLabels[1]:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            greetingSectionLabels[1]:Show()
            y = y + 16

            for qi = 1, numAvail do
                if btnIdx <= MAX_OPTIONS then
                    local title = GetAvailableTitle and GetAvailableTitle(qi) or ("Quest " .. qi)
                    local btn = greetingOptionBtns[btnIdx]
                    btn.icon:SetTexture(QUEST_ICON_AVAILABLE)
                    btn.text:SetText(title)
                    btn.normalR = T.questAvail[1]; btn.normalG = T.questAvail[2]; btn.normalB = T.questAvail[3]
                    btn.text:SetTextColor(T.questAvail[1], T.questAvail[2], T.questAvail[3])
                    btn:ClearAllPoints()
                    btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
                    local idx = qi
                    btn:SetScript("OnClick", function()
                        currentPage = nil
                        MainFrame:Hide()
                        SelectAvailableQuest(idx)
                    end)
                    btn:Show()
                    y = y + 22
                    btnIdx = btnIdx + 1
                end
            end
            y = y + 4
        end

        if numActive > 0 then
            greetingSectionLabels[2]:SetText("进行中任务")
            greetingSectionLabels[2]:ClearAllPoints()
            greetingSectionLabels[2]:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            greetingSectionLabels[2]:Show()
            y = y + 16

            for qi = 1, numActive do
                if btnIdx <= MAX_OPTIONS then
                    local title = GetActiveTitle and GetActiveTitle(qi) or ("Quest " .. qi)
                    local btn = greetingOptionBtns[btnIdx]
                    btn.icon:SetTexture(QUEST_ICON_ACTIVE)
                    btn.text:SetText(title)
                    btn.normalR = T.questActive[1]; btn.normalG = T.questActive[2]; btn.normalB = T.questActive[3]
                    btn.text:SetTextColor(T.questActive[1], T.questActive[2], T.questActive[3])
                    btn:ClearAllPoints()
                    btn:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
                    local idx = qi
                    btn:SetScript("OnClick", function()
                        currentPage = nil
                        MainFrame:Hide()
                        SelectActiveQuest(idx)
                    end)
                    btn:Show()
                    y = y + 22
                    btnIdx = btnIdx + 1
                end
            end
        end

        content:SetHeight(y + 10)
    end

    return page
end
--------------------------------------------------------------------------------
-- DETAIL PAGE (accept / decline quest)
--------------------------------------------------------------------------------
local detailItemSlots = {}

local function CreateDetailPage(parent)
    local page = CreateFrame("Frame", nil, parent)
    page:SetAllPoints(parent)
    page:Hide()

    local scroll = CreateScrollArea(page, "SFramesQuestDetailScroll")
    scroll:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    scroll:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
    page.scroll = scroll
    local content = scroll.content

    page.titleFS = content:CreateFontString(nil, "OVERLAY")
    page.titleFS:SetFont(GetFont(), 14, "OUTLINE")
    page.titleFS:SetWidth(CONTENT_W - 4)
    page.titleFS:SetJustifyH("LEFT")
    page.titleFS:SetTextColor(T.gold[1], T.gold[2], T.gold[3])

    page.sepLine = MakeSep(content, CONTENT_W)

    page.descFS = content:CreateFontString(nil, "OVERLAY")
    page.descFS:SetFont(GetFont(), 12)
    page.descFS:SetWidth(CONTENT_W - 4)
    page.descFS:SetJustifyH("LEFT")
    page.descFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])

    page.objLabel = content:CreateFontString(nil, "OVERLAY")
    page.objLabel:SetFont(GetFont(), 12, "OUTLINE")
    page.objLabel:SetWidth(CONTENT_W)
    page.objLabel:SetJustifyH("LEFT")
    page.objLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.objLabel:SetText("任务目标")

    page.objFS = content:CreateFontString(nil, "OVERLAY")
    page.objFS:SetFont(GetFont(), 12)
    page.objFS:SetWidth(CONTENT_W - 4)
    page.objFS:SetJustifyH("LEFT")
    page.objFS:SetTextColor(T.objectiveText[1], T.objectiveText[2], T.objectiveText[3])

    page.reqLabel = content:CreateFontString(nil, "OVERLAY")
    page.reqLabel:SetFont(GetFont(), 11, "OUTLINE")
    page.reqLabel:SetWidth(CONTENT_W)
    page.reqLabel:SetJustifyH("LEFT")
    page.reqLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.reqLabel:SetText("所需物品")

    for i = 1, MAX_ITEMS do detailItemSlots[i] = CreateItemSlot(content) end

    page.rewardLabel = content:CreateFontString(nil, "OVERLAY")
    page.rewardLabel:SetFont(GetFont(), 11, "OUTLINE")
    page.rewardLabel:SetWidth(CONTENT_W)
    page.rewardLabel:SetJustifyH("LEFT")
    page.rewardLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.rewardLabel:SetText("任务奖励")

    page.detailRewardSlots = {}
    for i = 1, MAX_ITEMS do page.detailRewardSlots[i] = CreateItemSlot(content) end

    page.detailChoiceLabel = content:CreateFontString(nil, "OVERLAY")
    page.detailChoiceLabel:SetFont(GetFont(), 11, "OUTLINE")
    page.detailChoiceLabel:SetWidth(CONTENT_W)
    page.detailChoiceLabel:SetJustifyH("LEFT")
    page.detailChoiceLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.detailChoiceLabel:SetText("可选奖励")

    page.detailChoiceSlots = {}
    for i = 1, MAX_ITEMS do page.detailChoiceSlots[i] = CreateItemSlot(content) end

    page.moneyLine = CreateMoneyLine(content)

    page.Update = function()
        local y = 6
        page.titleFS:SetText(GetTitleText and GetTitleText() or "")
        page.titleFS:ClearAllPoints()
        page.titleFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.titleFS, 16) + 8

        page.sepLine:ClearAllPoints()
        page.sepLine:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
        page.sepLine:Show()
        y = y + 6

        page.descFS:SetText(GetQuestText and GetQuestText() or "")
        page.descFS:ClearAllPoints()
        page.descFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.descFS, 14) + 12

        local obj = GetObjectiveText and GetObjectiveText() or ""
        if obj ~= "" then
            page.objLabel:ClearAllPoints()
            page.objLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.objLabel:Show(); y = y + 16
            page.objFS:SetText(obj)
            page.objFS:ClearAllPoints()
            page.objFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.objFS:Show()
            y = y + TextHeight(page.objFS, 14) + 10
        else
            page.objLabel:Hide(); page.objFS:Hide()
        end

        local numReq = GetNumQuestItems and GetNumQuestItems() or 0
        for i = 1, MAX_ITEMS do detailItemSlots[i]:Clear() end
        if numReq > 0 then
            page.reqLabel:ClearAllPoints()
            page.reqLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.reqLabel:Show(); y = y + 16
            for i = 1, math.min(numReq, MAX_ITEMS) do
                detailItemSlots[i]:ClearAllPoints()
                detailItemSlots[i]:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                detailItemSlots[i]:SetItemInfo("required", i)
                y = y + ITEM_SIZE + ITEM_GAP
            end
            y = y + 4
        else
            page.reqLabel:Hide()
        end

        local numRew = GetNumQuestRewards and GetNumQuestRewards() or 0
        local numCho = GetNumQuestChoices and GetNumQuestChoices() or 0
        local money  = GetRewardMoney and GetRewardMoney() or 0

        for i = 1, MAX_ITEMS do page.detailRewardSlots[i]:Clear(); page.detailChoiceSlots[i]:Clear() end
        page.rewardLabel:Hide(); page.detailChoiceLabel:Hide(); page.moneyLine:Hide()

        if numRew > 0 or money > 0 then
            page.rewardLabel:ClearAllPoints()
            page.rewardLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.rewardLabel:Show(); y = y + 16
            for i = 1, math.min(numRew, MAX_ITEMS) do
                page.detailRewardSlots[i]:ClearAllPoints()
                page.detailRewardSlots[i]:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                page.detailRewardSlots[i]:SetItemInfo("reward", i)
                y = y + ITEM_SIZE + ITEM_GAP
            end
            if money > 0 then
                page.moneyLine:ClearAllPoints()
                page.moneyLine:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                page.moneyLine:SetMoney(money); y = y + 20
            end
            y = y + 4
        end

        if numCho > 0 then
            page.detailChoiceLabel:ClearAllPoints()
            page.detailChoiceLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.detailChoiceLabel:Show(); y = y + 16
            for i = 1, math.min(numCho, MAX_ITEMS) do
                page.detailChoiceSlots[i]:ClearAllPoints()
                page.detailChoiceSlots[i]:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                page.detailChoiceSlots[i]:SetItemInfo("choice", i)
                y = y + ITEM_SIZE + ITEM_GAP
            end
            y = y + 4
        end
        content:SetHeight(y + 10)
    end
    return page
end
--------------------------------------------------------------------------------
-- PROGRESS PAGE (turn-in requirements)
--------------------------------------------------------------------------------
local progressItemSlots = {}

local function CreateProgressPage(parent)
    local page = CreateFrame("Frame", nil, parent)
    page:SetAllPoints(parent)
    page:Hide()

    local scroll = CreateScrollArea(page, "SFramesQuestProgressScroll")
    scroll:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    scroll:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
    page.scroll = scroll
    local content = scroll.content

    page.titleFS = content:CreateFontString(nil, "OVERLAY")
    page.titleFS:SetFont(GetFont(), 14, "OUTLINE")
    page.titleFS:SetWidth(CONTENT_W - 4)
    page.titleFS:SetJustifyH("LEFT")
    page.titleFS:SetTextColor(T.gold[1], T.gold[2], T.gold[3])

    page.sepLine = MakeSep(content, CONTENT_W)

    page.progressFS = content:CreateFontString(nil, "OVERLAY")
    page.progressFS:SetFont(GetFont(), 12)
    page.progressFS:SetWidth(CONTENT_W - 4)
    page.progressFS:SetJustifyH("LEFT")
    page.progressFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])

    page.reqLabel = content:CreateFontString(nil, "OVERLAY")
    page.reqLabel:SetFont(GetFont(), 11, "OUTLINE")
    page.reqLabel:SetWidth(CONTENT_W)
    page.reqLabel:SetJustifyH("LEFT")
    page.reqLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.reqLabel:SetText("需要的物品")

    for i = 1, MAX_ITEMS do progressItemSlots[i] = CreateItemSlot(content) end

    page.Update = function()
        local y = 6
        page.titleFS:SetText(GetTitleText and GetTitleText() or "")
        page.titleFS:ClearAllPoints()
        page.titleFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.titleFS, 16) + 8

        page.sepLine:ClearAllPoints()
        page.sepLine:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
        page.sepLine:Show(); y = y + 6

        page.progressFS:SetText(GetProgressText and GetProgressText() or "")
        page.progressFS:ClearAllPoints()
        page.progressFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.progressFS, 14) + 12

        local numReq = GetNumQuestItems and GetNumQuestItems() or 0
        for i = 1, MAX_ITEMS do progressItemSlots[i]:Clear() end
        if numReq > 0 then
            page.reqLabel:ClearAllPoints()
            page.reqLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.reqLabel:Show(); y = y + 16
            for i = 1, math.min(numReq, MAX_ITEMS) do
                progressItemSlots[i]:ClearAllPoints()
                progressItemSlots[i]:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                progressItemSlots[i]:SetItemInfo("required", i)
                y = y + ITEM_SIZE + ITEM_GAP
            end
        else
            page.reqLabel:Hide()
        end
        content:SetHeight(y + 10)
    end
    return page
end
--------------------------------------------------------------------------------
-- COMPLETE PAGE (reward selection & finish)
--------------------------------------------------------------------------------
local completeRewardSlots = {}
local completeChoiceSlots = {}

local function CreateCompletePage(parent)
    local page = CreateFrame("Frame", nil, parent)
    page:SetAllPoints(parent)
    page:Hide()

    local scroll = CreateScrollArea(page, "SFramesQuestCompleteScroll")
    scroll:SetPoint("TOPLEFT", page, "TOPLEFT", 0, 0)
    scroll:SetPoint("BOTTOMRIGHT", page, "BOTTOMRIGHT", 0, 0)
    page.scroll = scroll
    local content = scroll.content

    page.titleFS = content:CreateFontString(nil, "OVERLAY")
    page.titleFS:SetFont(GetFont(), 14, "OUTLINE")
    page.titleFS:SetWidth(CONTENT_W - 4)
    page.titleFS:SetJustifyH("LEFT")
    page.titleFS:SetTextColor(T.gold[1], T.gold[2], T.gold[3])

    page.sepLine = MakeSep(content, CONTENT_W)

    page.rewardTextFS = content:CreateFontString(nil, "OVERLAY")
    page.rewardTextFS:SetFont(GetFont(), 12)
    page.rewardTextFS:SetWidth(CONTENT_W - 4)
    page.rewardTextFS:SetJustifyH("LEFT")
    page.rewardTextFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])

    page.rewardLabel = content:CreateFontString(nil, "OVERLAY")
    page.rewardLabel:SetFont(GetFont(), 11, "OUTLINE")
    page.rewardLabel:SetWidth(CONTENT_W)
    page.rewardLabel:SetJustifyH("LEFT")
    page.rewardLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.rewardLabel:SetText("你将获得")

    for i = 1, MAX_ITEMS do completeRewardSlots[i] = CreateItemSlot(content) end

    page.moneyLine = CreateMoneyLine(content)

    page.choiceLabel = content:CreateFontString(nil, "OVERLAY")
    page.choiceLabel:SetFont(GetFont(), 11, "OUTLINE")
    page.choiceLabel:SetWidth(CONTENT_W)
    page.choiceLabel:SetJustifyH("LEFT")
    page.choiceLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
    page.choiceLabel:SetText("选择一项奖励")

    for i = 1, MAX_ITEMS do
        local slot = CreateItemSlot(content)
        local slotIdx = i
        slot:SetScript("OnClick", function()
            selectedReward = slotIdx
            for si = 1, MAX_ITEMS do completeChoiceSlots[si]:SetSelected(si == slotIdx) end
            UpdateBottomButtons()
        end)
        completeChoiceSlots[i] = slot
    end

    page.Update = function()
        selectedReward = 0
        local y = 6

        page.titleFS:SetText(GetTitleText and GetTitleText() or "")
        page.titleFS:ClearAllPoints()
        page.titleFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.titleFS, 16) + 8

        page.sepLine:ClearAllPoints()
        page.sepLine:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
        page.sepLine:Show(); y = y + 6

        page.rewardTextFS:SetText(GetRewardText and GetRewardText() or "")
        page.rewardTextFS:ClearAllPoints()
        page.rewardTextFS:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
        y = y + TextHeight(page.rewardTextFS, 14) + 12

        local numRew = GetNumQuestRewards and GetNumQuestRewards() or 0
        local numCho = GetNumQuestChoices and GetNumQuestChoices() or 0
        local money  = GetRewardMoney and GetRewardMoney() or 0

        for i = 1, MAX_ITEMS do completeRewardSlots[i]:Clear(); completeChoiceSlots[i]:Clear() end
        page.rewardLabel:Hide(); page.choiceLabel:Hide(); page.moneyLine:Hide()

        if numRew > 0 or money > 0 then
            page.rewardLabel:ClearAllPoints()
            page.rewardLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.rewardLabel:Show(); y = y + 16
            for i = 1, math.min(numRew, MAX_ITEMS) do
                completeRewardSlots[i]:ClearAllPoints()
                completeRewardSlots[i]:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                completeRewardSlots[i]:SetItemInfo("reward", i)
                y = y + ITEM_SIZE + ITEM_GAP
            end
            if money > 0 then
                page.moneyLine:ClearAllPoints()
                page.moneyLine:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                page.moneyLine:SetMoney(money); y = y + 20
            end
            y = y + 4
        end

        if numCho > 0 then
            page.choiceLabel:ClearAllPoints()
            page.choiceLabel:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -y)
            page.choiceLabel:Show(); y = y + 16
            for i = 1, math.min(numCho, MAX_ITEMS) do
                completeChoiceSlots[i]:ClearAllPoints()
                completeChoiceSlots[i]:SetPoint("TOPLEFT", content, "TOPLEFT", 4, -y)
                completeChoiceSlots[i]:SetItemInfo("choice", i)
                completeChoiceSlots[i]:SetSelected(false)
                y = y + ITEM_SIZE + ITEM_GAP
            end
            if numCho == 1 then
                selectedReward = 1
                completeChoiceSlots[1]:SetSelected(true)
            end
        end
        content:SetHeight(y + 10)
    end
    return page
end
--------------------------------------------------------------------------------
-- INITIALIZE
--------------------------------------------------------------------------------
function QUI:Initialize()
    if MainFrame then return end

    MainFrame = CreateFrame("Frame", "SFramesQuestFrame", UIParent)
    MainFrame:SetWidth(FRAME_W)
    MainFrame:SetHeight(FRAME_H)
    MainFrame:SetPoint("LEFT", UIParent, "LEFT", 64, 0)
    MainFrame:SetFrameStrata("HIGH")
    MainFrame:SetToplevel(true)
    MainFrame:EnableMouse(true)
    MainFrame:SetMovable(true)
    MainFrame:RegisterForDrag("LeftButton")
    MainFrame:SetScript("OnDragStart", function() this:StartMoving() end)
    MainFrame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)

    SetRoundBackdrop(MainFrame)
    CreateShadow(MainFrame)

    local header = CreateFrame("Frame", nil, MainFrame)
    header:SetPoint("TOPLEFT", MainFrame, "TOPLEFT", 0, 0)
    header:SetPoint("TOPRIGHT", MainFrame, "TOPRIGHT", 0, 0)
    header:SetHeight(HEADER_H)

    local titleIco = SFrames:CreateIcon(header, "quest", 16)
    titleIco:SetDrawLayer("OVERLAY")
    titleIco:SetPoint("LEFT", header, "LEFT", SIDE_PAD, 0)
    titleIco:SetVertexColor(T.gold[1], T.gold[2], T.gold[3])

    local npcNameFS = header:CreateFontString(nil, "OVERLAY")
    npcNameFS:SetFont(GetFont(), 14, "OUTLINE")
    npcNameFS:SetPoint("LEFT", titleIco, "RIGHT", 5, 0)
    npcNameFS:SetPoint("RIGHT", header, "RIGHT", -30, 0)
    npcNameFS:SetJustifyH("LEFT")
    npcNameFS:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
    MainFrame.npcNameFS = npcNameFS

    local closeBtn = CreateFrame("Button", nil, MainFrame, "UIPanelCloseButton")
    closeBtn:SetPoint("TOPRIGHT", MainFrame, "TOPRIGHT", 2, 2)
    closeBtn:SetWidth(24); closeBtn:SetHeight(24)

    local headerSep = MainFrame:CreateTexture(nil, "ARTWORK")
    headerSep:SetTexture("Interface\\Buttons\\WHITE8X8")
    headerSep:SetHeight(1)
    headerSep:SetPoint("TOPLEFT", MainFrame, "TOPLEFT", 6, -HEADER_H)
    headerSep:SetPoint("TOPRIGHT", MainFrame, "TOPRIGHT", -6, -HEADER_H)
    headerSep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])

    local contentArea = CreateFrame("Frame", nil, MainFrame)
    contentArea:SetPoint("TOPLEFT", MainFrame, "TOPLEFT", SIDE_PAD, -(HEADER_H + 4))
    contentArea:SetPoint("BOTTOMRIGHT", MainFrame, "BOTTOMRIGHT", -SIDE_PAD, BOTTOM_H + 4)
    MainFrame.contentArea = contentArea

    local bottomSep = MainFrame:CreateTexture(nil, "ARTWORK")
    bottomSep:SetTexture("Interface\\Buttons\\WHITE8X8")
    bottomSep:SetHeight(1)
    bottomSep:SetPoint("BOTTOMLEFT", MainFrame, "BOTTOMLEFT", 6, BOTTOM_H)
    bottomSep:SetPoint("BOTTOMRIGHT", MainFrame, "BOTTOMRIGHT", -6, BOTTOM_H)
    bottomSep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])

    MainFrame.btn1 = CreateActionBtn(MainFrame, "", 100)
    MainFrame.btn1:SetPoint("BOTTOMLEFT", MainFrame, "BOTTOMLEFT", SIDE_PAD, 8)
    MainFrame.btn1:Hide()

    MainFrame.btn2 = CreateActionBtn(MainFrame, "", 100)
    MainFrame.btn2:SetPoint("BOTTOMRIGHT", MainFrame, "BOTTOMRIGHT", -SIDE_PAD, 8)
    MainFrame.btn2:Hide()

    pages.gossip   = CreateGossipPage(contentArea)
    pages.greeting = CreateGreetingPage(contentArea)
    pages.detail   = CreateDetailPage(contentArea)
    pages.progress = CreateProgressPage(contentArea)
    pages.complete = CreateCompletePage(contentArea)

    MainFrame:SetScript("OnHide", function()
        if currentPage then
            if CloseGossip then pcall(CloseGossip) end
            if DeclineQuest then pcall(DeclineQuest) end
            previousPage = nil
        end
        currentPage = nil
        pendingClose = false
    end)

    MainFrame:SetScript("OnUpdate", function()
        if pendingClose then
            closeTimer = closeTimer + (arg1 or 0)
            if closeTimer > 0.15 then
                pendingClose = false
                closeTimer = 0
                previousPage = nil
                if MainFrame:IsVisible() then
                    currentPage = nil
                    MainFrame:Hide()
                end
            end
        end
    end)

    MainFrame:Hide()

    MainFrame:RegisterEvent("GOSSIP_SHOW")
    MainFrame:RegisterEvent("GOSSIP_CLOSED")
    MainFrame:RegisterEvent("QUEST_GREETING")
    MainFrame:RegisterEvent("QUEST_DETAIL")
    MainFrame:RegisterEvent("QUEST_PROGRESS")
    MainFrame:RegisterEvent("QUEST_COMPLETE")
    MainFrame:RegisterEvent("QUEST_FINISHED")
    MainFrame:SetScript("OnEvent", function()
        if event == "GOSSIP_SHOW" then
            pendingClose = false
            local gText = GetGossipText() or ""
            if GOSSIP_TRIGGER_TEXTS[strtrim and strtrim(gText) or gText] then return end
            if TryAutoGossip() then return end
            previousPage = "gossip"
            MainFrame.npcNameFS:SetText(UnitName("npc") or "NPC")
            ShowPage("gossip")
            pages.gossip.Update()
            UpdateBottomButtons()
            MainFrame:Show()

        elseif event == "GOSSIP_CLOSED" then
            if currentPage == "gossip" then
                pendingClose = true
                closeTimer = 0
            end

        elseif event == "QUEST_GREETING" then
            pendingClose = false
            previousPage = "greeting"
            MainFrame.npcNameFS:SetText(UnitName("npc") or "NPC")
            ShowPage("greeting")
            pages.greeting.Update()
            UpdateBottomButtons()
            MainFrame:Show()

        elseif event == "QUEST_DETAIL" then
            pendingClose = false
            MainFrame.npcNameFS:SetText(UnitName("npc") or "NPC")
            ShowPage("detail")
            pages.detail.Update()
            UpdateBottomButtons()
            MainFrame:Show()

        elseif event == "QUEST_PROGRESS" then
            pendingClose = false
            MainFrame.npcNameFS:SetText(UnitName("npc") or "NPC")
            ShowPage("progress")
            pages.progress.Update()
            UpdateBottomButtons()
            MainFrame:Show()

        elseif event == "QUEST_COMPLETE" then
            pendingClose = false
            selectedReward = 0
            MainFrame.npcNameFS:SetText(UnitName("npc") or "NPC")
            ShowPage("complete")
            pages.complete.Update()
            UpdateBottomButtons()
            MainFrame:Show()

        elseif event == "QUEST_FINISHED" then
            if previousPage then
                pendingClose = true
                closeTimer = 0
            else
                pendingClose = false
                currentPage = nil
                MainFrame:Hide()
            end
        end
    end)

    if GossipFrame then
        GossipFrame:UnregisterEvent("GOSSIP_SHOW")
        GossipFrame:UnregisterEvent("GOSSIP_CLOSED")
    end
    if QuestFrame then
        QuestFrame:UnregisterEvent("QUEST_GREETING")
        QuestFrame:UnregisterEvent("QUEST_DETAIL")
        QuestFrame:UnregisterEvent("QUEST_PROGRESS")
        QuestFrame:UnregisterEvent("QUEST_COMPLETE")
        QuestFrame:UnregisterEvent("QUEST_FINISHED")
    end

    tinsert(UISpecialFrames, "SFramesQuestFrame")
end

--------------------------------------------------------------------------------
-- Bootstrap
--------------------------------------------------------------------------------
local bootstrap = CreateFrame("Frame")
bootstrap:RegisterEvent("PLAYER_LOGIN")
bootstrap:SetScript("OnEvent", function()
    if event == "PLAYER_LOGIN" then
        if SFramesDB.enableQuestUI == nil then
            SFramesDB.enableQuestUI = true
        end
        if SFramesDB.enableQuestUI ~= false then
            QUI:Initialize()
        end
    end
end)