local AddOnName = "Nanami-UI"
SFrames = SFrames or {}

local TradeUI = CreateFrame("Frame", "SFramesTradeUI", UIParent)
TradeUI:RegisterEvent("TRADE_SHOW")
TradeUI:RegisterEvent("TRADE_CLOSED")
TradeUI:RegisterEvent("TRADE_UPDATE")
TradeUI:RegisterEvent("TRADE_PLAYER_ITEM_CHANGED")
TradeUI:RegisterEvent("TRADE_TARGET_ITEM_CHANGED")
TradeUI:RegisterEvent("TRADE_ACCEPT_UPDATE")
TradeUI:RegisterEvent("TRADE_MONEY_CHANGED")
TradeUI:RegisterEvent("PLAYER_TRADE_MONEY")
TradeUI:RegisterEvent("UI_INFO_MESSAGE")
TradeUI:RegisterEvent("CHAT_MSG_SYSTEM")

local L = {
    NOT_TRADED = "不会被交易",
    WHISPER_CHECK = "发送清单",
    CONFIRMED = "已确认",
    WAITING = "等待中...",
}

local TRADE_DATA = {
    active = false,
    playerItems = {},
    targetItems = {},
    playerMoney = 0,
    targetMoney = 0,
    targetName = "",
    playerAccepted = false,
    targetAccepted = false,
}

SFramesDB = SFramesDB or {}

--------------------------------------------------------------------------------
-- Theme: Pink Cat-Paw (matching GameMenu / Nanami-UI)
--------------------------------------------------------------------------------
local T = SFrames.Theme:Extend({
    labelDim        = { 0.7, 0.5, 0.6 },
    tradeBg         = { 0.10, 0.22, 0.12, 0.95 },
    tradeBorder     = { 0.30, 0.70, 0.35, 0.90 },
    tradeText       = { 0.7, 1, 0.75 },
    moneyBg         = { 0.08, 0.04, 0.06, 0.85 },
    moneyBorder     = { 0.40, 0.25, 0.35, 0.6 },
    confirmOverlay  = { 0.15, 0.85, 0.25, 0.55 },
    confirmBorder   = { 0.30, 1.0, 0.40, 0.95 },
    confirmText     = { 0.3, 1, 0.4 },
    statusConfirmBg = { 0.08, 0.35, 0.12, 0.92 },
    statusConfirmBd = { 0.25, 0.95, 0.35, 0.95 },
    statusWaitBg    = { 0.15, 0.08, 0.12, 0.8 },
    statusWaitBd    = { 0.45, 0.28, 0.38, 0.6 },
    statusWaitText  = { 0.6, 0.45, 0.55 },
})

local FRAME_W = 440
local SLOT_W = 188
local SLOT_H = 42
local SLOT_GAP = 2
local SIDE_PAD = 16
local HEADER_H = 46
local BOTTOM_H = 54
local NOT_TRADED_GAP = 38
local STATUS_BAR_H = 22
-- FRAME_H computed: HEADER_H+8 + 6*SLOT_H+5*GAP + NOT_TRADED_GAP + SLOT_H + 6 + STATUS_BAR_H + 6 + BOTTOM_H
local FRAME_H = HEADER_H + 8 + 6 * SLOT_H + 5 * SLOT_GAP + NOT_TRADED_GAP + SLOT_H + 6 + STATUS_BAR_H + 6 + BOTTOM_H

local DEFAULT_SLOT_BORDER = { 0.25, 0.25, 0.3, 0.8 }

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

--------------------------------------------------------------------------------
-- Data logic
--------------------------------------------------------------------------------
local function SaveTradeState()
    if not TradeFrame or not TradeFrame:IsVisible() then return end

    local pItems = {}
    for i = 1, 6 do
        local link = nil
        if GetTradePlayerItemLink then
            link = GetTradePlayerItemLink(i)
        end
        if not link and GetTradePlayerItemInfo then
            local name, texture, numItems = GetTradePlayerItemInfo(i)
            if name and name ~= "" then
                link = name
            end
        end
        if link then
            local _, _, numItems = GetTradePlayerItemInfo(i)
            table.insert(pItems, { link = link, count = numItems or 1 })
        end
    end
    if table.getn(pItems) > 0 then
        TRADE_DATA.playerItems = pItems
    end

    local tItems = {}
    for i = 1, 6 do
        local link = nil
        if GetTradeTargetItemLink then
            link = GetTradeTargetItemLink(i)
        end
        if not link and GetTradeTargetItemInfo then
            local name, texture, numItems = GetTradeTargetItemInfo(i)
            if name and name ~= "" then
                link = name
            end
        end
        if link then
            local _, _, numItems = GetTradeTargetItemInfo(i)
            table.insert(tItems, { link = link, count = numItems or 1 })
        end
    end
    if table.getn(tItems) > 0 then
        TRADE_DATA.targetItems = tItems
    end

    local pm = GetPlayerTradeMoney and GetPlayerTradeMoney() or 0
    local tm = GetTargetTradeMoney and GetTargetTradeMoney() or 0
    if pm >= 0 and pm > TRADE_DATA.playerMoney then
        TRADE_DATA.playerMoney = pm
    end
    if tm >= 0 and tm > TRADE_DATA.targetMoney then
        TRADE_DATA.targetMoney = tm
    end

    local name = UnitName("NPC")
    if name and name ~= "" then TRADE_DATA.targetName = name end
end

local function FormatMoneyZH(copper)
    if not copper or copper == 0 then return nil end
    local g = math.floor(copper / 10000)
    local s = math.floor((copper - (g * 10000)) / 100)
    local c = math.mod(copper, 100)
    local text = ""
    if g > 0 then text = text .. g .. "g " end
    if s > 0 then text = text .. s .. "s " end
    if c > 0 then text = text .. c .. "c " end
    return text
end

local function SendLine(msg, channel, target)
    if channel == "WHISPER" then
        if target and target ~= "" and target ~= "Unknown" then
            SendChatMessage(msg, "WHISPER", nil, target)
        end
    else
        SendChatMessage(msg, channel)
    end
end

local function SendTradeWhisper()
    if not SFramesDB.TradeWhisperEnable then return end
    local target = TRADE_DATA.targetName
    local channel = SFramesDB.TradeWhisperChannel or "WHISPER"
    local outLines = {}

    local playerMoneyStr = FormatMoneyZH(TRADE_DATA.playerMoney)
    local targetMoneyStr = FormatMoneyZH(TRADE_DATA.targetMoney)

    local giveItems = ""
    for _, item in ipairs(TRADE_DATA.playerItems) do
        giveItems = giveItems .. item.link .. (item.count > 1 and ("x" .. item.count) or "") .. " "
    end
    local getItems = ""
    for _, item in ipairs(TRADE_DATA.targetItems) do
        getItems = getItems .. item.link .. (item.count > 1 and ("x" .. item.count) or "") .. " "
    end

    if not playerMoneyStr and giveItems == "" and not targetMoneyStr and getItems == "" then
        return
    end

    local useCN = (SFramesDB.TradeWhisperLang == "ZH")
    local header    = useCN and "=== 交易完成清单 ===" or "=== Trade Summary ==="
    local lblGiveG  = useCN and "我方金币: "           or "I gave gold: "
    local lblGiveI  = useCN and "我方物品: "           or "I gave items: "
    local lblGotG   = useCN and "对方金币: "           or "I got gold: "
    local lblGotI   = useCN and "对方物品: "           or "I got items: "

    table.insert(outLines, header)
    if playerMoneyStr then table.insert(outLines, lblGiveG .. playerMoneyStr) end
    if giveItems ~= "" then table.insert(outLines, lblGiveI .. giveItems) end
    if targetMoneyStr then table.insert(outLines, lblGotG .. targetMoneyStr) end
    if getItems ~= "" then table.insert(outLines, lblGotI .. getItems) end

    for _, line in ipairs(outLines) do
        SendLine(line, channel, target)
    end
end

local function ClearTradeData()
    TRADE_DATA.playerItems = {}
    TRADE_DATA.targetItems = {}
    TRADE_DATA.playerMoney = 0
    TRADE_DATA.targetMoney = 0
end

local tradeWhisperSent = false

local function IsTradeCompleteMsg(msg)
    if not msg then return false end
    if string.find(msg, "Trade successful") then return true end
    if string.find(msg, "Trade complete") then return true end
    if string.find(msg, "交易完成") then return true end
    if string.find(msg, "交易成功") then return true end
    return false
end

local sfTradeRefreshing = false
local function ForceRefreshTradeVisuals()
    if not TradeFrame or not TradeFrame:IsVisible() then return end
    if sfTradeRefreshing then return end
    if TradeFrame_Update then
        sfTradeRefreshing = true
        pcall(function() TradeFrame_Update() end)
        sfTradeRefreshing = false
    end
end

TradeUI:SetScript("OnEvent", function()
    if event == "TRADE_SHOW" then
        tradeWhisperSent = false
        TRADE_DATA.active = true
        TRADE_DATA.targetName = UnitName("NPC") or ""
        TRADE_DATA.playerItems = {}
        TRADE_DATA.targetItems = {}
        TRADE_DATA.playerMoney = 0
        TRADE_DATA.targetMoney = 0
        TRADE_DATA.playerAccepted = false
        TRADE_DATA.targetAccepted = false
        SaveTradeState()
        ForceRefreshTradeVisuals()
    elseif event == "TRADE_PLAYER_ITEM_CHANGED" or event == "TRADE_TARGET_ITEM_CHANGED" then
        TRADE_DATA.playerAccepted = false
        TRADE_DATA.targetAccepted = false
        SaveTradeState()
        ForceRefreshTradeVisuals()
    elseif event == "TRADE_UPDATE" then
        SaveTradeState()
        ForceRefreshTradeVisuals()
    elseif event == "TRADE_ACCEPT_UPDATE" then
        TRADE_DATA.playerAccepted = (arg1 and arg1 == 1)
        TRADE_DATA.targetAccepted = (arg2 and arg2 == 1)
        SaveTradeState()
        ForceRefreshTradeVisuals()
    elseif event == "TRADE_MONEY_CHANGED" or event == "PLAYER_TRADE_MONEY" then
        SaveTradeState()
        ForceRefreshTradeVisuals()
    elseif event == "TRADE_CLOSED" then
        if TRADE_DATA.playerAccepted and TRADE_DATA.targetAccepted and not tradeWhisperSent then
            tradeWhisperSent = true
            SendTradeWhisper()
            ClearTradeData()
        end
        TRADE_DATA.active = false
        TRADE_DATA.playerAccepted = false
        TRADE_DATA.targetAccepted = false
    elseif event == "UI_INFO_MESSAGE" then
        if IsTradeCompleteMsg(arg1) and not tradeWhisperSent then
            tradeWhisperSent = true
            SendTradeWhisper()
            ClearTradeData()
        end
    elseif event == "CHAT_MSG_SYSTEM" then
        if IsTradeCompleteMsg(arg1) and not tradeWhisperSent then
            tradeWhisperSent = true
            SendTradeWhisper()
            ClearTradeData()
        end
    end
end)

--------------------------------------------------------------------------------
-- UI Helpers (matching GameMenu / Nanami-UI style)
--------------------------------------------------------------------------------
local function SetRoundBackdrop(frame, bgColor, borderColor)
    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 },
    })
    local bg = bgColor or T.panelBg
    local bd = borderColor or T.panelBorder
    frame:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
    frame:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1)
end

local function CreateShadow(parent, size)
    local s = CreateFrame("Frame", nil, parent)
    local sz = size or 4
    s:SetPoint("TOPLEFT", parent, "TOPLEFT", -sz, sz)
    s:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", sz, -sz)
    s:SetFrameLevel(math.max(parent:GetFrameLevel() - 1, 0))
    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.55)
    s:SetBackdropBorderColor(0, 0, 0, 0.4)
    return s
end

--------------------------------------------------------------------------------
-- Tooltip scanner for item level
--------------------------------------------------------------------------------
local tooltipScanner = CreateFrame("GameTooltip", "SFramesTradeTooltipScan", nil, "GameTooltipTemplate")
tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")

local function ScanItemLevelFromTooltip()
    for i = 2, tooltipScanner:NumLines() do
        local line = _G["SFramesTradeTooltipScanTextLeft" .. i]
        if line then
            local text = line:GetText()
            if text then
                local _, _, ilvl = string.find(text, "(%d+)")
                if string.find(text, "Item Level") or string.find(text, "iLvl") or string.find(text, "ilvl") or string.find(text, "物品等级") then
                    if ilvl then return tonumber(ilvl) end
                end
            end
        end
    end
    return nil
end

local function GetTradePlayerItemLevel(slot)
    tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
    tooltipScanner:ClearLines()
    local ok = pcall(function() tooltipScanner:SetTradePlayerItem(slot) end)
    if ok then return ScanItemLevelFromTooltip() end
    local link = GetTradePlayerItemLink and GetTradePlayerItemLink(slot)
    if link then
        tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
        tooltipScanner:ClearLines()
        ok = pcall(function() tooltipScanner:SetHyperlink(link) end)
        if ok then return ScanItemLevelFromTooltip() end
    end
    return nil
end

local function GetTradeTargetItemLevel(slot)
    tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
    tooltipScanner:ClearLines()
    local ok = pcall(function() tooltipScanner:SetTradeTargetItem(slot) end)
    if ok then return ScanItemLevelFromTooltip() end
    local link = GetTradeTargetItemLink and GetTradeTargetItemLink(slot)
    if link then
        tooltipScanner:SetOwner(UIParent, "ANCHOR_NONE")
        tooltipScanner:ClearLines()
        ok = pcall(function() tooltipScanner:SetHyperlink(link) end)
        if ok then return ScanItemLevelFromTooltip() end
    end
    return nil
end

--------------------------------------------------------------------------------
-- Quality color from item link
--------------------------------------------------------------------------------
local function GetQualityColorFromLink(link)
    if not link then return nil, nil, nil end
    local _, _, hex = string.find(link, "|c(%x+)|H")
    if hex and string.len(hex) == 8 then
        local r = tonumber(string.sub(hex, 3, 4), 16) / 255
        local g = tonumber(string.sub(hex, 5, 6), 16) / 255
        local b = tonumber(string.sub(hex, 7, 8), 16) / 255
        return r, g, b
    end
    return nil, nil, nil
end

local function GetQualityColorFromRarity(rarity)
    if type(rarity) ~= "number" then return nil, nil, nil end
    if not GetItemQualityColor then return nil, nil, nil end
    local ok, r, g, b = pcall(function() return GetItemQualityColor(rarity) end)
    if ok and r and g and b then
        return r, g, b
    end
    return nil, nil, nil
end
local function ResolveTradeItemQuality(link, quality, name)
    if type(quality) == "number" then
        return quality
    end
    if GetItemInfo then
        if link then
            local _, _, q = GetItemInfo(link)
            if type(q) == "number" then return q end
        end
        if name then
            local _, _, q = GetItemInfo(name)
            if type(q) == "number" then return q end
        end
    end
    return nil
end

local function IsCommonOrPoor(link)
    if not link then return true end
    local _, _, hex = string.find(link, "|c(%x+)|H")
    if hex then
        local hexLower = string.lower(hex)
        return hexLower == "ffffffff" or hexLower == "ff9d9d9d"
    end
    return true
end

--------------------------------------------------------------------------------
-- Right-click to remove player trade items
--------------------------------------------------------------------------------
local function HookSingleTradeSlot(slotIndex)
    local itemBtn = _G["TradePlayerItem" .. slotIndex .. "ItemButton"]
    if not itemBtn or itemBtn.sfRightClickHooked then return end
    itemBtn.sfRightClickHooked = true

    itemBtn:RegisterForClicks("LeftButtonUp", "RightButtonUp")

    local origScript = itemBtn:GetScript("OnClick")
    itemBtn:SetScript("OnClick", function()
        if arg1 == "RightButton" then
            local hasItem = false
            if GetTradePlayerItemLink then
                local link = GetTradePlayerItemLink(slotIndex)
                if link then hasItem = true end
            end
            if not hasItem then
                local name = GetTradePlayerItemInfo(slotIndex)
                if name and name ~= "" then hasItem = true end
            end
            if hasItem then
                ClearCursor()
                ClickTradeButton(slotIndex)
                return
            end
        end
        if origScript then origScript() end
    end)
end

local function HookTradeItemRightClick()
    for i = 1, 6 do
        HookSingleTradeSlot(i)
    end
end

--------------------------------------------------------------------------------
-- Re-hide Blizzard visuals (called every TradeFrame_Update)
--------------------------------------------------------------------------------
local function ReHideBlizzardSlot(itemPrefix, index)
    local btnName = itemPrefix .. index .. "ItemButton"
    local icon = _G[btnName .. "IconTexture"]
    if icon then icon:SetAlpha(0); icon:Hide() end
    local nt = _G[btnName .. "NormalTexture"]
    if nt then nt:SetAlpha(0); nt:Hide() end
    local slot = _G[btnName .. "SlotTexture"]
    if slot then slot:SetAlpha(0); slot:Hide() end
    local cnt = _G[btnName .. "Count"]
    if cnt then cnt:SetAlpha(0) end
    local bgSlot = _G[itemPrefix .. index .. "ItemButtonSlotTexture"]
    if bgSlot then bgSlot:SetAlpha(0); bgSlot:Hide() end
    local bgSlot2 = _G[itemPrefix .. index .. "SlotTexture"]
    if bgSlot2 then bgSlot2:SetAlpha(0); bgSlot2:Hide() end
    local bgSlot3 = _G[itemPrefix .. index .. "ItemButtonBackground"]
    if bgSlot3 then bgSlot3:SetAlpha(0); bgSlot3:Hide() end
    local nf = _G[itemPrefix .. index .. "NameFrame"]
    if nf then nf:SetAlpha(0); nf:Hide() end
end

--------------------------------------------------------------------------------
-- Skin Trade Frame
--------------------------------------------------------------------------------
local function SkinTradeFrame()
    if TradeFrame.sfSkinned then return end
    TradeFrame.sfSkinned = true

    TradeFrame:SetMovable(true)
    TradeFrame:EnableMouse(true)
    TradeFrame:RegisterForDrag("LeftButton")
    TradeFrame:SetScript("OnDragStart", function() this:StartMoving() end)
    TradeFrame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)

    -- Hide ALL default textures
    local regions = { TradeFrame:GetRegions() }
    local bottomTexts = {}
    for _, r in ipairs(regions) do
        if r:IsObjectType("Texture") then
            r:SetTexture(nil)
            r:SetAlpha(0)
        elseif r:IsObjectType("FontString") then
            local text = r:GetText()
            if text and (string.find(text, "不会被交易") or string.find(text, "Will not be traded") or text == L.NOT_TRADED) then
                table.insert(bottomTexts, r)
            end
        end
    end
    TradeFrame.sfBottomTexts = bottomTexts

    TradeFrame:SetWidth(FRAME_W)
    TradeFrame:SetHeight(FRAME_H)

    -- Main backdrop - Nanami-UI rounded style (matching GameMenu)
    SetRoundBackdrop(TradeFrame, T.panelBg, T.panelBorder)
    CreateShadow(TradeFrame, 5)

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

    -- Center vertical divider (pink tinted)
    local divLine = TradeFrame:CreateTexture(nil, "ARTWORK")
    divLine:SetTexture("Interface\\Buttons\\WHITE8X8")
    divLine:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])
    divLine:SetWidth(1)
    divLine:SetPoint("TOP", TradeFrame, "TOP", 0, -(HEADER_H + 2))
    divLine:SetPoint("BOTTOM", TradeFrame, "BOTTOM", 0, BOTTOM_H + 2)

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

    -- Player name (pink/gold tint)
    TradeFramePlayerNameText:ClearAllPoints()
    TradeFramePlayerNameText:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", SIDE_PAD, -8)
    TradeFramePlayerNameText:SetFont(GetFont(), 12, "OUTLINE")
    TradeFramePlayerNameText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])

    -- Target name
    TradeFrameRecipientNameText:ClearAllPoints()
    TradeFrameRecipientNameText:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -SIDE_PAD, -8)
    TradeFrameRecipientNameText:SetFont(GetFont(), 12, "OUTLINE")
    TradeFrameRecipientNameText:SetTextColor(T.gold[1], T.gold[2], T.gold[3])

    if TradeFrameRecipientPortrait then TradeFrameRecipientPortrait:Hide() end
    if TradeFramePlayerPortrait then TradeFramePlayerPortrait:Hide() end

    -- Confirmation status bars (created here, positioned after item layout)
    local function CreateStatusBar(parent, anchorSide)
        local bar = CreateFrame("Frame", nil, parent)
        bar:SetWidth(SLOT_W)
        bar:SetHeight(STATUS_BAR_H)
        bar:SetFrameLevel(parent:GetFrameLevel() + 5)
        SetRoundBackdrop(bar, T.statusWaitBg, T.statusWaitBd)

        local icon = bar:CreateFontString(nil, "OVERLAY")
        icon:SetFont(GetFont(), 13, "OUTLINE")
        icon:SetPoint("LEFT", bar, "LEFT", 8, 0)
        bar.sfIcon = icon

        local label = bar:CreateFontString(nil, "OVERLAY")
        label:SetFont(GetFont(), 12, "OUTLINE")
        label:SetPoint("LEFT", icon, "RIGHT", 4, 0)
        label:SetPoint("RIGHT", bar, "RIGHT", -6, 0)
        label:SetJustifyH(anchorSide)
        bar.sfLabel = label

        local glow = bar:CreateTexture(nil, "BACKGROUND")
        glow:SetTexture("Interface\\Buttons\\WHITE8X8")
        glow:SetAllPoints(bar)
        glow:SetAlpha(0)
        bar.sfGlow = glow

        bar.sfElapsed = 0
        bar:SetScript("OnUpdate", function()
            if not this.sfConfirmed then return end
            this.sfElapsed = (this.sfElapsed or 0) + arg1
            local a = 0.35 + 0.2 * math.sin(this.sfElapsed * 3.5)
            if this.sfGlow then this.sfGlow:SetAlpha(a) end
        end)

        bar:Hide()
        return bar
    end

    local playerStatusBar = CreateStatusBar(TradeFrame, "LEFT")
    TradeFrame.sfPlayerStatusBar = playerStatusBar

    local targetStatusBar = CreateStatusBar(TradeFrame, "LEFT")
    TradeFrame.sfTargetStatusBar = targetStatusBar

    -- Money frames
    TradePlayerInputMoneyFrame:ClearAllPoints()
    TradePlayerInputMoneyFrame:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", SIDE_PAD, -24)

    local pMoneyBg = CreateFrame("Frame", nil, TradeFrame)
    pMoneyBg:SetPoint("TOPLEFT", TradePlayerInputMoneyFrame, "TOPLEFT", -3, 3)
    pMoneyBg:SetPoint("BOTTOMRIGHT", TradePlayerInputMoneyFrame, "BOTTOMRIGHT", 3, -3)
    pMoneyBg:SetFrameLevel(math.max(TradePlayerInputMoneyFrame:GetFrameLevel() - 1, 0))
    SetRoundBackdrop(pMoneyBg, T.moneyBg, T.moneyBorder)

    TradeRecipientMoneyFrame:ClearAllPoints()
    TradeRecipientMoneyFrame:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -SIDE_PAD, -24)

    local rMoneyBg = CreateFrame("Frame", nil, TradeFrame)
    rMoneyBg:SetPoint("TOPLEFT", TradeRecipientMoneyFrame, "TOPLEFT", -3, 3)
    rMoneyBg:SetPoint("BOTTOMRIGHT", TradeRecipientMoneyFrame, "BOTTOMRIGHT", 3, -3)
    rMoneyBg:SetFrameLevel(math.max(TradeRecipientMoneyFrame:GetFrameLevel() - 1, 0))
    SetRoundBackdrop(rMoneyBg, T.moneyBg, T.moneyBorder)

    -- Style money input text
    local moneyEditNames = { "Gold", "Silver", "Copper" }
    for _, suffix in ipairs(moneyEditNames) do
        local eb = _G["TradePlayerInputMoneyFrame" .. suffix]
        if eb and eb.SetFont then
            eb:SetFont(GetFont(), 12, "OUTLINE")
            eb:SetTextColor(1, 1, 1, 1)
        end
        local btn = _G["TradePlayerInputMoneyFrame" .. suffix .. "Button"]
        if btn then
            local icon = btn:GetNormalTexture()
            if icon then icon:SetVertexColor(1, 1, 1, 1) end
        end
        local rBtn = _G["TradeRecipientMoneyFrame" .. suffix .. "Button"]
        if rBtn then
            local rText = _G["TradeRecipientMoneyFrame" .. suffix .. "ButtonText"]
            if rText and rText.SetFont then
                rText:SetFont(GetFont(), 11, "OUTLINE")
                rText:SetTextColor(1, 1, 1, 1)
            end
        end
    end

    ----------------------------------------------------------------------------
    -- Item slots - fully custom, Blizzard button is invisible click receiver
    ----------------------------------------------------------------------------
    local function HideBlizzardButton(itemBtn)
        local btnName = itemBtn:GetName()

        -- Kill every known named child texture by explicit name
        local suffixes = { "IconTexture", "NormalTexture", "SlotTexture", "Count" }
        for _, suf in ipairs(suffixes) do
            local obj = _G[btnName .. suf]
            if obj then
                if obj.SetTexture then obj:SetTexture(nil) end
                if obj.SetAlpha then obj:SetAlpha(0) end
                if obj.SetTextColor then obj:SetTextColor(0,0,0,0) end
                if obj.Hide then obj:Hide() end
            end
        end

        -- Kill template textures via API
        if itemBtn.GetNormalTexture then
            local nt = itemBtn:GetNormalTexture()
            if nt then nt:SetTexture(nil); nt:SetAlpha(0) end
        end
        if itemBtn.GetPushedTexture then
            local pt = itemBtn:GetPushedTexture()
            if pt then pt:SetTexture(nil); pt:SetAlpha(0) end
        end
        if itemBtn.GetHighlightTexture then
            local ht = itemBtn:GetHighlightTexture()
            if ht then ht:SetTexture(nil); ht:SetAlpha(0) end
        end

        -- Kill all remaining child regions
        local regions = { itemBtn:GetRegions() }
        for _, r in ipairs(regions) do
            if r then
                if r.SetTexture then r:SetTexture(nil) end
                if r.SetAlpha then r:SetAlpha(0) end
                if r.SetTextColor then r:SetTextColor(0,0,0,0) end
                if r.Hide then r:Hide() end
            end
        end

        itemBtn:SetBackdrop(nil)
    end

    local function CreateSfSlot(parent)
        local slot = CreateFrame("Frame", nil, parent)
        slot:SetWidth(SLOT_H)
        slot:SetHeight(SLOT_H)
        slot:SetBackdrop({
            bgFile   = "Interface\\Tooltips\\UI-Tooltip-Background",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = true, tileSize = 16, edgeSize = 12,
            insets = { left = 2, right = 2, top = 2, bottom = 2 }
        })
        slot:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
        slot:SetBackdropBorderColor(DEFAULT_SLOT_BORDER[1], DEFAULT_SLOT_BORDER[2], DEFAULT_SLOT_BORDER[3], DEFAULT_SLOT_BORDER[4])

        local icon = slot:CreateTexture(nil, "ARTWORK")
        icon:SetWidth(SLOT_H - 4)
        icon:SetHeight(SLOT_H - 4)
        icon:SetPoint("CENTER", slot, "CENTER", 0, 0)
        icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        icon:Hide()
        slot.icon = icon

        local highlight = slot:CreateTexture(nil, "OVERLAY")
        highlight:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
        highlight:SetBlendMode("ADD")
        highlight:SetWidth(SLOT_H - 4)
        highlight:SetHeight(SLOT_H - 4)
        highlight:SetPoint("CENTER", slot, "CENTER", 0, 0)
        highlight:SetAlpha(0)
        slot.highlight = highlight

        local ilvl = slot:CreateFontString(nil, "OVERLAY")
        ilvl:SetFont(GetFont(), 9, "OUTLINE")
        ilvl:SetPoint("BOTTOMRIGHT", slot, "BOTTOMRIGHT", -2, 2)
        ilvl:SetTextColor(1, 0.82, 0)
        ilvl:SetJustifyH("RIGHT")
        ilvl:Hide()
        slot.ilvl = ilvl

        local count = slot:CreateFontString(nil, "OVERLAY")
        count:SetFont(GetFont(), 11, "OUTLINE")
        count:SetPoint("BOTTOMRIGHT", slot, "BOTTOMRIGHT", -2, 2)
        count:SetTextColor(1, 1, 1)
        count:SetJustifyH("RIGHT")
        count:Hide()
        slot.count = count

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

        return slot
    end

    local function StyleTradeItem(itemPrefix, index)
        local itemFrame = _G[itemPrefix .. index]
        local itemBtn = _G[itemPrefix .. index .. "ItemButton"]
        local itemName = _G[itemPrefix .. index .. "Name"]
        local bgSlot = _G[itemPrefix .. index .. "ItemButtonSlotTexture"]
        local bgSlot2 = _G[itemPrefix .. index .. "SlotTexture"]
        local bgSlot3 = _G[itemPrefix .. index .. "ItemButtonBackground"]
        local nameFrame = _G[itemPrefix .. index .. "NameFrame"]

        if bgSlot then
            bgSlot:SetTexture(nil); bgSlot:Hide()
            bgSlot.Show = function() end
        end
        if bgSlot2 then
            bgSlot2:SetTexture(nil); bgSlot2:Hide()
            bgSlot2.Show = function() end
        end
        if bgSlot3 then
            bgSlot3:SetTexture(nil); bgSlot3:Hide()
            bgSlot3.Show = function() end
        end
        if nameFrame then
            nameFrame:SetTexture(nil); nameFrame:Hide()
            nameFrame.Show = function() end
        end

        itemFrame:SetWidth(SLOT_W)
        itemFrame:SetHeight(SLOT_H)

        -- Row background (create once)
        if not itemFrame.sfRowBg then
            local slotBg = itemFrame:CreateTexture(nil, "BACKGROUND")
            slotBg:SetTexture("Interface\\Tooltips\\UI-Tooltip-Background")
            slotBg:SetVertexColor(0.08, 0.04, 0.06, 0.4)
            slotBg:SetAllPoints(itemFrame)
            itemFrame.sfRowBg = slotBg
        end

        -- Create our pure custom slot (once)
        if not itemFrame.sfSlot then
            itemFrame.sfSlot = CreateSfSlot(itemFrame)
        end
        local sfSlot = itemFrame.sfSlot
        sfSlot:ClearAllPoints()
        sfSlot:SetPoint("LEFT", itemFrame, "LEFT", 1, 0)
        sfSlot:SetFrameLevel(itemFrame:GetFrameLevel() + 1)
        sfSlot:Show()

        -- Make Blizzard button completely invisible
        HideBlizzardButton(itemBtn)

        -- Position invisible Blizzard button exactly over sfSlot for click/drag
        itemBtn:ClearAllPoints()
        itemBtn:SetWidth(SLOT_H)
        itemBtn:SetHeight(SLOT_H)
        itemBtn:SetPoint("CENTER", sfSlot, "CENTER", 0, 0)
        itemBtn:SetFrameLevel(sfSlot:GetFrameLevel() + 2)

        -- Hover glow: show/hide highlight on sfSlot when mouse enters Blizzard button
        if not itemBtn.sfHoverHooked then
            itemBtn.sfHoverHooked = true
            local origEnter = itemBtn:GetScript("OnEnter")
            local origLeave = itemBtn:GetScript("OnLeave")
            itemBtn:SetScript("OnEnter", function()
                if origEnter then origEnter() end
                local sf = this:GetParent() and this:GetParent().sfSlot
                if sf and sf.highlight then sf.highlight:SetAlpha(0.35) end
            end)
            itemBtn:SetScript("OnLeave", function()
                if origLeave then origLeave() end
                local sf = this:GetParent() and this:GetParent().sfSlot
                if sf and sf.highlight then sf.highlight:SetAlpha(0) end
            end)
        end

        -- Name text anchored to sfSlot
        itemName:ClearAllPoints()
        itemName:SetPoint("LEFT", sfSlot, "RIGHT", 6, 0)
        itemName:SetPoint("RIGHT", itemFrame, "RIGHT", -4, 0)
        itemName:SetJustifyH("LEFT")
        itemName:SetFont(GetFont(), 11, "OUTLINE")
    end

    -- Layout items in two columns
    for i = 1, 7 do
        StyleTradeItem("TradePlayerItem", i)
        StyleTradeItem("TradeRecipientItem", i)

        local pf = _G["TradePlayerItem" .. i]
        local rf = _G["TradeRecipientItem" .. i]
        pf:ClearAllPoints()
        rf:ClearAllPoints()

        if i == 1 then
            pf:SetPoint("TOPLEFT", TradeFrame, "TOPLEFT", SIDE_PAD, -(HEADER_H + 8))
            rf:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -SIDE_PAD, -(HEADER_H + 8))
        elseif i == 7 then
            pf:SetPoint("TOPLEFT", _G["TradePlayerItem6"], "BOTTOMLEFT", 0, -NOT_TRADED_GAP)
            rf:SetPoint("TOPLEFT", _G["TradeRecipientItem6"], "BOTTOMLEFT", 0, -NOT_TRADED_GAP)

            if TradeFrame.sfBottomTexts then
                for idx, lbl in ipairs(TradeFrame.sfBottomTexts) do
                    lbl:ClearAllPoints()
                    lbl:SetFont(GetFont(), 10, "OUTLINE")
                    lbl:SetTextColor(T.labelDim[1], T.labelDim[2], T.labelDim[3])
                    if idx == 1 then
                        lbl:SetPoint("BOTTOMLEFT", pf, "TOPLEFT", 0, 5)
                    else
                        lbl:SetPoint("BOTTOMLEFT", rf, "TOPLEFT", 0, 5)
                    end
                end
            end
        else
            pf:SetPoint("TOPLEFT", _G["TradePlayerItem" .. (i - 1)], "BOTTOMLEFT", 0, -SLOT_GAP)
            rf:SetPoint("TOPLEFT", _G["TradeRecipientItem" .. (i - 1)], "BOTTOMLEFT", 0, -SLOT_GAP)
        end
    end

    -- Position status bars below item 7
    if TradeFrame.sfPlayerStatusBar then
        TradeFrame.sfPlayerStatusBar:ClearAllPoints()
        TradeFrame.sfPlayerStatusBar:SetPoint("TOPLEFT", _G["TradePlayerItem7"], "BOTTOMLEFT", 0, -4)
    end
    if TradeFrame.sfTargetStatusBar then
        TradeFrame.sfTargetStatusBar:ClearAllPoints()
        TradeFrame.sfTargetStatusBar:SetPoint("TOPLEFT", _G["TradeRecipientItem7"], "BOTTOMLEFT", 0, -4)
    end

    -- Thin separator in the gap between slot 6 and not-traded label
    local function MakeThinSep(anchor, w)
        local sep = TradeFrame:CreateTexture(nil, "ARTWORK")
        sep:SetTexture("Interface\\Buttons\\WHITE8X8")
        sep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4])
        sep:SetHeight(1)
        sep:SetWidth(w)
        sep:SetPoint("BOTTOMLEFT", anchor, "BOTTOMLEFT", 0, -(NOT_TRADED_GAP / 2.5))
    end
    MakeThinSep(_G["TradePlayerItem6"], SLOT_W)
    MakeThinSep(_G["TradeRecipientItem6"], SLOT_W)

    ----------------------------------------------------------------------------
    -- Hide Blizzard confirm highlights (we handle this ourselves)
    ----------------------------------------------------------------------------
    local function KillBlizzardHighlight(name)
        local obj = _G[name]
        if not obj then return end
        if obj:IsObjectType("Texture") then
            obj:SetTexture(nil); obj:SetAlpha(0); obj:Hide()
            obj.Show = function() end
        elseif obj:IsObjectType("Frame") then
            local regs = { obj:GetRegions() }
            for _, r in ipairs(regs) do
                if r:IsObjectType("Texture") then r:SetTexture(nil); r:Hide() end
            end
            obj:SetAlpha(0); obj:Hide()
            obj.Show = function() end
        end
    end
    KillBlizzardHighlight("TradeHighlightPlayer")
    KillBlizzardHighlight("TradeHighlightRecipient")
    KillBlizzardHighlight("TradeHighlightPlayerEnchant")
    KillBlizzardHighlight("TradeHighlightRecipientEnchant")

    ----------------------------------------------------------------------------
    -- Custom confirm overlay (our own, with proper FrameLevel + pulse)
    ----------------------------------------------------------------------------
    local function CreateConfirmOverlay(parent, item1, item2)
        local overlay = CreateFrame("Frame", nil, parent)
        overlay:SetFrameLevel(parent:GetFrameLevel() + 8)
        overlay:SetPoint("TOPLEFT", item1, "TOPLEFT", -4, 4)
        overlay:SetPoint("BOTTOMRIGHT", item2, "BOTTOMRIGHT", 4, -4)

        SetRoundBackdrop(overlay, { 0, 0, 0, 0 }, T.confirmBorder)

        overlay.sfElapsed = 0
        overlay:SetScript("OnUpdate", function()
            this.sfElapsed = (this.sfElapsed or 0) + arg1
            local pulse = 0.6 + 0.4 * math.sin(this.sfElapsed * 3)
            this:SetBackdropBorderColor(
                T.confirmBorder[1], T.confirmBorder[2], T.confirmBorder[3], pulse)
        end)

        overlay:EnableMouse(false)
        overlay:Hide()
        return overlay
    end

    TradeFrame.sfPlayerOverlay = CreateConfirmOverlay(TradeFrame,
        _G["TradePlayerItem1"], _G["TradePlayerItem6"])
    TradeFrame.sfPlayerOverlayEnchant = CreateConfirmOverlay(TradeFrame,
        _G["TradePlayerItem7"], _G["TradePlayerItem7"])
    TradeFrame.sfTargetOverlay = CreateConfirmOverlay(TradeFrame,
        _G["TradeRecipientItem1"], _G["TradeRecipientItem6"])
    TradeFrame.sfTargetOverlayEnchant = CreateConfirmOverlay(TradeFrame,
        _G["TradeRecipientItem7"], _G["TradeRecipientItem7"])

    ----------------------------------------------------------------------------
    -- Buttons - Nanami-UI GameMenu style
    ----------------------------------------------------------------------------
    local function SkinBtn(btn, bgCol, borderCol, textCol, label)
        if not btn then return end
        btn:SetWidth(72)
        btn:SetHeight(26)

        local nt = btn:GetNormalTexture()
        if nt then nt:SetTexture(nil) end
        local pt = btn:GetPushedTexture()
        if pt then pt:SetTexture(nil) end
        local ht = btn:GetHighlightTexture()
        if ht then ht:SetTexture(nil) end
        local dt = btn:GetDisabledTexture()
        if dt then dt:SetTexture(nil) end

        SetRoundBackdrop(btn, bgCol, borderCol)

        local origEnter = btn:GetScript("OnEnter")
        local origLeave = btn:GetScript("OnLeave")

        if not btn.sfHoverHooked then
            btn.sfHoverHooked = true
            btn.sfBgCol = bgCol
            btn.sfBorderCol = borderCol

            btn:SetScript("OnEnter", function()
                if origEnter then origEnter() end
                this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
                this:SetBackdropBorderColor(T.btnHoverBorder[1], T.btnHoverBorder[2], T.btnHoverBorder[3], T.btnHoverBorder[4])
                local fs = this:GetFontString()
                if fs then fs:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3]) end
            end)

            btn:SetScript("OnLeave", function()
                if origLeave then origLeave() end
                local bg = this.sfBgCol or T.btnBg
                local bd = this.sfBorderCol or T.btnBorder
                this:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
                this:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1)
                local fs = this:GetFontString()
                if fs and this.sfTextCol then fs:SetTextColor(unpack(this.sfTextCol)) end
            end)
        end

        local fs = btn:GetFontString()
        if fs then
            fs:SetFont(GetFont(), 11, "OUTLINE")
            fs:SetTextColor(unpack(textCol or T.btnText))
            if label then fs:SetText(label) end
        end
        btn.sfTextCol = textCol or T.btnText
    end

    SkinBtn(TradeFrameTradeButton, T.tradeBg, T.tradeBorder, T.tradeText, "交易")
    SkinBtn(TradeFrameCancelButton, T.btnBg, T.btnBorder, T.btnText, "取消")

    TradeFrameTradeButton:ClearAllPoints()
    TradeFrameCancelButton:ClearAllPoints()
    TradeFrameCancelButton:SetPoint("BOTTOMRIGHT", TradeFrame, "BOTTOMRIGHT", -SIDE_PAD, 14)
    TradeFrameTradeButton:SetPoint("RIGHT", TradeFrameCancelButton, "LEFT", -6, 0)

    ----------------------------------------------------------------------------
    -- Whisper checkbox + channel dropdown
    ----------------------------------------------------------------------------
    local cbObj = _G["SFramesTradeWhisperObj"]
    if not cbObj then
        cbObj = CreateFrame("CheckButton", "SFramesTradeWhisperObj", TradeFrame, "UICheckButtonTemplate")
        cbObj:SetWidth(20)
        cbObj:SetHeight(20)
        cbObj:SetPoint("BOTTOMLEFT", TradeFrame, "BOTTOMLEFT", SIDE_PAD, 14)

        local cbText = _G[cbObj:GetName() .. "Text"]
        if cbText then
            cbText:SetFont(GetFont(), 11, "OUTLINE")
            cbText:SetText(L.WHISPER_CHECK)
            cbText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
        end

        if SFramesDB.TradeWhisperEnable == nil then SFramesDB.TradeWhisperEnable = false end
        cbObj:SetChecked(SFramesDB.TradeWhisperEnable and 1 or 0)
        cbObj:SetScript("OnClick", function()
            SFramesDB = SFramesDB or {}
            SFramesDB.TradeWhisperEnable = (this:GetChecked() == 1)
        end)

        local drop = CreateFrame("Frame", "SFramesTradeChannelObj", TradeFrame, "UIDropDownMenuTemplate")
        drop:SetPoint("LEFT", cbText or cbObj, "RIGHT", -8, -1)
        UIDropDownMenu_SetWidth(60, drop)

        local dropText = _G[drop:GetName() .. "Text"]
        if dropText then
            dropText:SetFont(GetFont(), 10, "OUTLINE")
            dropText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
        end

        local channels = {
            { text = "密语", value = "WHISPER" },
            { text = "小队", value = "PARTY" },
            { text = "当前", value = "SAY" },
        }

        local function TradeDropDownInit()
            SFramesDB = SFramesDB or {}
            local selected = SFramesDB.TradeWhisperChannel or "WHISPER"
            for _, info in ipairs(channels) do
                local capturedText = info.text
                local capturedValue = info.value
                local d = {}
                d.text = capturedText
                d.value = capturedValue
                d.func = function()
                    SFramesDB = SFramesDB or {}
                    SFramesDB.TradeWhisperChannel = capturedValue
                    UIDropDownMenu_SetSelectedValue(drop, capturedValue)
                    local txt = _G[drop:GetName() .. "Text"]
                    if txt then txt:SetText(capturedText) end
                end
                d.checked = (capturedValue == selected)
                UIDropDownMenu_AddButton(d)
            end
        end

        UIDropDownMenu_Initialize(drop, TradeDropDownInit)
        SFramesDB = SFramesDB or {}
        UIDropDownMenu_SetSelectedValue(drop, SFramesDB.TradeWhisperChannel or "WHISPER")
        if dropText then
            for _, info in ipairs(channels) do
                if info.value == (SFramesDB.TradeWhisperChannel or "WHISPER") then
                    dropText:SetText(info.text)
                    break
                end
            end
        end

        -- Language dropdown (EN / ZH)
        local langDrop = CreateFrame("Frame", "SFramesTradeLangObj", TradeFrame, "UIDropDownMenuTemplate")
        langDrop:SetPoint("LEFT", drop, "RIGHT", -16, 0)
        UIDropDownMenu_SetWidth(50, langDrop)

        local langDropText = _G[langDrop:GetName() .. "Text"]
        if langDropText then
            langDropText:SetFont(GetFont(), 10, "OUTLINE")
            langDropText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
        end

        local langs = {
            { text = "EN", value = "EN" },
            { text = "中文", value = "ZH" },
        }

        local function TradeLangDropInit()
            SFramesDB = SFramesDB or {}
            local selected = SFramesDB.TradeWhisperLang or "EN"
            for _, info in ipairs(langs) do
                local capText = info.text
                local capValue = info.value
                local d = {}
                d.text = capText
                d.value = capValue
                d.func = function()
                    SFramesDB = SFramesDB or {}
                    SFramesDB.TradeWhisperLang = capValue
                    UIDropDownMenu_SetSelectedValue(langDrop, capValue)
                    local txt = _G[langDrop:GetName() .. "Text"]
                    if txt then txt:SetText(capText) end
                end
                d.checked = (capValue == selected)
                UIDropDownMenu_AddButton(d)
            end
        end

        UIDropDownMenu_Initialize(langDrop, TradeLangDropInit)
        SFramesDB = SFramesDB or {}
        UIDropDownMenu_SetSelectedValue(langDrop, SFramesDB.TradeWhisperLang or "EN")
        if langDropText then
            for _, info in ipairs(langs) do
                if info.value == (SFramesDB.TradeWhisperLang or "EN") then
                    langDropText:SetText(info.text)
                    break
                end
            end
        end
    end

    -- Close button
    local closeBtn = _G["TradeFrameCloseButton"]
    if closeBtn then
        closeBtn:ClearAllPoints()
        closeBtn:SetPoint("TOPRIGHT", TradeFrame, "TOPRIGHT", -2, -2)
    end

    HookTradeItemRightClick()
end

--------------------------------------------------------------------------------
-- Hooks
--------------------------------------------------------------------------------
local Hook_TradeFrame_OnShow = TradeFrame_OnShow
function TradeFrame_OnShow()
    if Hook_TradeFrame_OnShow then Hook_TradeFrame_OnShow() end
    SkinTradeFrame()
    ForceRefreshTradeVisuals()
end

local function UpdateSfSlot(sfSlot, texture, numItems, link, ilvl, quality)
    if not sfSlot then return end

    -- Icon texture
    if texture then
        sfSlot.icon:SetTexture(texture)
        sfSlot.icon:Show()
    else
        sfSlot.icon:SetTexture(nil)
        sfSlot.icon:Hide()
    end

    -- Stack count
    if numItems and numItems > 1 then
        sfSlot.count:SetText(numItems)
        sfSlot.count:Show()
        sfSlot.ilvl:Hide()
    else
        sfSlot.count:Hide()
        -- Item level (only show if no stack count)
        if ilvl and ilvl > 0 and texture then
            sfSlot.ilvl:SetText(ilvl)
            sfSlot.ilvl:Show()
        else
            sfSlot.ilvl:Hide()
        end
    end

    -- Quality border
    local r, g, b = nil, nil, nil
    if link and not IsCommonOrPoor(link) then
        r, g, b = GetQualityColorFromLink(link)
    end
    if (not r) and type(quality) == "number" and quality ~= 1 then
        r, g, b = GetQualityColorFromRarity(quality)
    end
    if r then
        if sfSlot.qualGlow then
            sfSlot.qualGlow:SetVertexColor(r, g, b)
            sfSlot.qualGlow:Show()
        end
    else
        if sfSlot.qualGlow then
            sfSlot.qualGlow:Hide()
        end
    end
end

local function UpdateSlotNameColor(nameObj, link, quality)
    if not nameObj then return end
    local r, g, b = nil, nil, nil
    if link then
        r, g, b = GetQualityColorFromLink(link)
    end
    if (not r) and type(quality) == "number" and quality ~= 1 then
        r, g, b = GetQualityColorFromRarity(quality)
    end
    if r then
        nameObj:SetTextColor(r, g, b)
        return
    end
    nameObj:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
end

local function SetStatusBarState(bar, confirmed)
    if not bar then return end
    if confirmed then
        bar.sfConfirmed = true
        bar.sfElapsed = 0
        SetRoundBackdrop(bar, T.statusConfirmBg, T.statusConfirmBd)
        if bar.sfGlow then
            bar.sfGlow:SetVertexColor(T.confirmOverlay[1], T.confirmOverlay[2], T.confirmOverlay[3], 1)
            bar.sfGlow:SetAlpha(0.35)
        end
        if bar.sfIcon then
            bar.sfIcon:SetText("|cff33ff55>>|r")
            bar.sfIcon:SetTextColor(0.3, 1, 0.4)
        end
        if bar.sfLabel then
            bar.sfLabel:SetText(L.CONFIRMED)
            bar.sfLabel:SetTextColor(T.confirmText[1], T.confirmText[2], T.confirmText[3])
        end
    else
        bar.sfConfirmed = false
        SetRoundBackdrop(bar, T.statusWaitBg, T.statusWaitBd)
        if bar.sfGlow then bar.sfGlow:SetAlpha(0) end
        if bar.sfIcon then
            bar.sfIcon:SetText("...")
            bar.sfIcon:SetTextColor(T.statusWaitText[1], T.statusWaitText[2], T.statusWaitText[3])
        end
        if bar.sfLabel then
            bar.sfLabel:SetText(L.WAITING)
            bar.sfLabel:SetTextColor(T.statusWaitText[1], T.statusWaitText[2], T.statusWaitText[3])
        end
    end
    bar:Show()
end

local function UpdateConfirmStatus()
    if not TradeFrame then return end

    local playerConfirmed = TRADE_DATA.playerAccepted
    local targetConfirmed = TRADE_DATA.targetAccepted

    -- Status bars
    SetStatusBarState(TradeFrame.sfPlayerStatusBar, playerConfirmed)
    SetStatusBarState(TradeFrame.sfTargetStatusBar, targetConfirmed)

    -- Green overlays on item columns
    if TradeFrame.sfPlayerOverlay then
        if playerConfirmed then TradeFrame.sfPlayerOverlay:Show()
        else TradeFrame.sfPlayerOverlay:Hide() end
    end
    if TradeFrame.sfPlayerOverlayEnchant then
        if playerConfirmed then TradeFrame.sfPlayerOverlayEnchant:Show()
        else TradeFrame.sfPlayerOverlayEnchant:Hide() end
    end
    if TradeFrame.sfTargetOverlay then
        if targetConfirmed then TradeFrame.sfTargetOverlay:Show()
        else TradeFrame.sfTargetOverlay:Hide() end
    end
    if TradeFrame.sfTargetOverlayEnchant then
        if targetConfirmed then TradeFrame.sfTargetOverlayEnchant:Show()
        else TradeFrame.sfTargetOverlayEnchant:Hide() end
    end
end

local Hook_TradeFrame_Update = TradeFrame_Update
function TradeFrame_Update()
    if Hook_TradeFrame_Update then Hook_TradeFrame_Update() end

    for i = 1, 7 do
        -- Re-hide Blizzard visuals (Blizzard code re-shows them every update)
        ReHideBlizzardSlot("TradePlayerItem", i)
        ReHideBlizzardSlot("TradeRecipientItem", i)

        -- Read from Blizzard API
        local pName, pTex, pNum, pQuality = GetTradePlayerItemInfo(i)
        local rName, rTex, rNum, rQuality = GetTradeTargetItemInfo(i)
        local pLink = GetTradePlayerItemLink(i)
        local rLink = GetTradeTargetItemLink and GetTradeTargetItemLink(i)

        if (not pTex or pTex == "") then
            local pIconObj = _G["TradePlayerItem" .. i .. "ItemButtonIconTexture"]
            if pIconObj and pIconObj.GetTexture then
                pTex = pIconObj:GetTexture()
            end
        end
        if (not rTex or rTex == "") then
            local rIconObj = _G["TradeRecipientItem" .. i .. "ItemButtonIconTexture"]
            if rIconObj and rIconObj.GetTexture then
                rTex = rIconObj:GetTexture()
            end
        end
        if (not pTex or pTex == "") and pLink and GetItemIcon then
            pTex = GetItemIcon(pLink)
        end
        if (not rTex or rTex == "") and rLink and GetItemIcon then
            rTex = GetItemIcon(rLink)
        end
        if (not pTex or pTex == "") and pName and GetItemInfo then
            local _, _, _, _, _, _, _, _, pInfoTex = GetItemInfo(pName)
            pTex = pInfoTex or pTex
        end
        if (not rTex or rTex == "") and rName and GetItemInfo then
            local _, _, _, _, _, _, _, _, rInfoTex = GetItemInfo(rName)
            rTex = rInfoTex or rTex
        end

        pQuality = ResolveTradeItemQuality(pLink, pQuality, pName)
        rQuality = ResolveTradeItemQuality(rLink, rQuality, rName)

        local pIlvl = (pTex or pName or pLink) and GetTradePlayerItemLevel(i) or nil
        local rIlvl = (rTex or rName or rLink) and GetTradeTargetItemLevel(i) or nil

        -- Write to custom sfSlot
        local pFrame = _G["TradePlayerItem" .. i]
        local rFrame = _G["TradeRecipientItem" .. i]
        if pFrame then UpdateSfSlot(pFrame.sfSlot, pTex, pNum, pLink, pIlvl, pQuality) end
        if rFrame then UpdateSfSlot(rFrame.sfSlot, rTex, rNum, rLink, rIlvl, rQuality) end

        UpdateSlotNameColor(_G["TradePlayerItem" .. i .. "Name"], pLink, pQuality)
        UpdateSlotNameColor(_G["TradeRecipientItem" .. i .. "Name"], rLink, rQuality)
    end

    UpdateConfirmStatus()
end

local Hook_MoneyFrame_Update = MoneyFrame_Update
function MoneyFrame_Update(frameName, money)
    if Hook_MoneyFrame_Update then Hook_MoneyFrame_Update(frameName, money) end
end

