/**
 * assistant.html 用户列表：对齐 live-web-operator user.vue + _onlineUser / _banUser / _blackIpUser
 * 数据来源：DWLive.onRoomContext（room 全量）、hdChat onlineUsers（可选）
 * 操作：踢出 DWLive.kickout；禁言/解禁、IP 禁言优先走 hdChat.banUser 等（与 UserHandles 一致）
 */
;(function ($) {
  var ICON = {
    kick: 'https://static.csslcloud.net/img/icon-kickout.png',
    whisper: 'https://static.csslcloud.net/img/icon-whisper.png',
    gag: 'https://static.csslcloud.net/img/icon-gag.png',
    ungag: 'https://static.csslcloud.net/img/icon-ungag.png',
    ipgag: 'https://static.csslcloud.net/img/icon-ipgag.png',
    unipgag: 'https://static.csslcloud.net/img/icon-unipgag.png'
  }

  var ADMIN_ROLE = { publisher: '讲师', teacher: '助教', host: '主持', assistant: '助教' }

  function tagToRole(tag) {
    var num = tag && tag.length ? Number(tag[0]) : 4
    if (num === 1) return 'publisher'
    if (num === 2 || num === 14) return 'teacher'
    if (num === 3) return 'host'
    if (num === 10) return 'publisher'
    return 'student'
  }

  function blackListToMap(bl) {
    if (!bl) return {}
    if (Array.isArray(bl)) {
      var m = {}
      for (var i = 0; i < bl.length; i++) {
        m[String(bl[i])] = true
      }
      return m
    }
    if (typeof bl === 'object') return Object.assign({}, bl)
    return {}
  }

  function normalizeUser(u, blackMap, blackIpArr) {
    var id = String(u.id != null ? u.id : u.userId != null ? u.userId : '')
    var role = u.role || tagToRole(u.tag)
    if (role === 'assistant') role = 'teacher'
    var ip = u.ip || ''
    var isBan = !!(blackMap && blackMap[id])
    var isBlackIP = false
    for (var i = 0; i < (blackIpArr || []).length; i++) {
      if (String(blackIpArr[i]) === String(ip)) {
        isBlackIP = true
        break
      }
    }
    return {
      id: id,
      userId: id,
      userName: u.userName || u.name || '',
      name: u.name || u.userName || '',
      ip: ip,
      role: role,
      saleName: u.saleName,
      isBan: isBan,
      isBlackIP: isBlackIP
    }
  }

  function sortUserList(list) {
    var hosts = []
    var publishers = []
    var teachers = []
    var students = []
    for (var i = 0; i < list.length; i++) {
      var item = list[i]
      var r = item.role
      if (r === 'host') hosts.push(item)
      else if (r === 'publisher') publishers.push(item)
      else if (r === 'teacher' || r === 'assistant') {
        teachers.push(Object.assign({}, item, { role: 'teacher' }))
      } else students.push(item)
    }
    return hosts.concat(publishers).concat(teachers).concat(students)
  }

  function avatarEl(item) {
    var ch = item.isIpOnlyRow
      ? String(item.ip || '?').charAt(0)
      : (item.name || item.userName || '?').charAt(0)
    return (
      '<div class="user-avator-fallback role-' +
      escapeHtml(item.role) +
      '">' +
      escapeHtml(ch) +
      '</div>'
    )
  }

  function escapeHtml(s) {
    if (s == null) return ''
    return String(s)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
  }

  function mergeSearchWithOnline(records, onlineById) {
    var out = []
    var map = {}
    for (var i = 0; i < records.length; i++) {
      var it = records[i]
      var uid = String(it.userId != null ? it.userId : it.id || '')
      var base = onlineById[uid] || {}
      var row = Object.assign({}, base, {
        id: uid,
        userId: uid,
        userName: it.userName || it.name || base.userName,
        name: it.name || it.userName || base.name,
        role: base.role || 'student',
        ip: it.ip || base.ip,
        isBan: it.isBan != null ? it.isBan : base.isBan,
        isBlackIP: it.isBlackIP != null ? it.isBlackIP : base.isBlackIP
      })
      if (!row.isBan) row.isBan = false
      if (!row.isBlackIP) row.isBlackIP = false
      if (!row.saleName && it.belongSaleName) row.saleName = it.belongSaleName
      if (!map[uid]) {
        map[uid] = true
        out.push(row)
      }
    }
    return out
  }

  function mergeBanTabRows(onlineRows, apiList) {
    var map = {}
    var out = []
    function add(row) {
      if (!row || row.id == null) return
      var id = String(row.id)
      if (map[id]) return
      map[id] = true
      out.push(row)
    }
    for (var i = 0; i < onlineRows.length; i++) {
      var u = onlineRows[i]
      if (u.role === 'student' && (u.isBan || u.isBlackIP)) add(u)
    }
    if (apiList && apiList.length) {
      for (var j = 0; j < apiList.length; j++) {
        var b = apiList[j]
        var id = String(b.id != null ? b.id : b.userId || '')
        if (!id) continue
        add({
          id: id,
          userId: id,
          name: b.name || b.userName || '',
          userName: b.userName || b.name || '',
          role: 'student',
          isBan: true,
          isBlackIP: false,
          ip: b.ip || ''
        })
      }
    }
    return out
  }

  function getIpTabRows() {
    var rows = []
    var seen = {}
    var ips = (state.blackIpList || []).slice()
    for (var k = 0; k < state.onlineRows.length; k++) {
      var u = state.onlineRows[k]
      if (u.role === 'student' && u.isBlackIP && u.ip && ips.indexOf(u.ip) === -1) {
        ips.push(u.ip)
      }
    }
    for (var i = 0; i < ips.length; i++) {
      var ip = ips[i]
      if (!ip || seen[ip]) continue
      seen[ip] = true
      var repId = ''
      for (var j = 0; j < state.onlineRows.length; j++) {
        if (state.onlineRows[j].ip === ip && state.onlineRows[j].role === 'student') {
          repId = state.onlineRows[j].id
          break
        }
      }
      rows.push({
        id: 'ip_' + ip,
        ip: ip,
        role: 'student',
        name: '',
        userName: '',
        repUserId: repId,
        isIpOnlyRow: true,
        isBan: false,
        isBlackIP: true
      })
    }
    return rows
  }

  var state = {
    onlineRows: [],
    offlineList: [],
    offlineSaleType: '1',
    onlineSaleType: '0',
    offlineTimer: null,
    onlineTimer: null,
    blackListMap: {},
    blackList: [],
    blackIpList: [],
    banApiList: [],
    roomSettings: null,
    subTab: 'online',
    isSearchMode: false,
    searchDisplayList: []
  }

  var hdRef = null

  function selfId() {
    return DWLive && DWLive.viewerid != null ? String(DWLive.viewerid) : ''
  }

  function interactiveChatToken() {
    if (!hdRef) return ''
    return hdRef.token || (hdRef.$data && hdRef.$data.token) || ''
  }

  function assistantSaleOn() {
    var rs = state.roomSettings
    return !!(rs && (rs.assistantSaleSwitch === true || rs.assistantSaleSwitch === 1 || rs.assistantSaleSwitch === '1'))
  }

  /** 对齐运营端 user-salename：assistantSaleSwitch 且学员才展示跟进人 */
  function saleFollowHtml(item, offline) {
    if (!assistantSaleOn() || item.role !== 'student') return ''
    var sale = offline
      ? item.belongSaleName || '暂未分配'
      : item.saleName || item.belongSaleName || '暂未分配'
    return '<span class="user-offline-sale">跟进人：' + escapeHtml(sale) + '</span>'
  }

  function offlineRecordsFromRes(res) {
    if (!res) return []
    if (Array.isArray(res.records)) return res.records
    if (res.data && Array.isArray(res.data.records)) return res.data.records
    return []
  }

  function stopOfflinePoll() {
    if (state.offlineTimer) {
      clearTimeout(state.offlineTimer)
      state.offlineTimer = null
    }
  }

  function stopOnlinePoll() {
    if (state.onlineTimer) {
      clearTimeout(state.onlineTimer)
      state.onlineTimer = null
    }
  }

  function saleIdFromUrl() {
    try {
      return new URLSearchParams(window.location.search).get('saleId') || ''
    } catch (e) {
      return ''
    }
  }

  function buildOnlineSaleData() {
    var saleType = state.onlineSaleType != null ? state.onlineSaleType : '0'
    if (saleType === '0' || saleType === 0) return false
    var data = { saleType: Number(saleType) }
    var sid = saleIdFromUrl()
    if (sid) data.saleId = sid
    return data
  }

  function syncUserToolbars() {
    var saleOn = assistantSaleOn()
    var onBar = document.getElementById('user-online-toolbar')
    var offBar = document.getElementById('user-offline-toolbar')
    if (onBar) onBar.hidden = !saleOn || state.subTab !== 'online'
    if (offBar) offBar.hidden = !saleOn || state.subTab !== 'offline'
  }

  function downloadUserBlob(blob, namePrefix) {
    var a = document.createElement('a')
    var url = URL.createObjectURL(blob)
    a.href = url
    a.download = namePrefix + new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-') + '.xlsx'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
  }

  function buildOnlineExportParams() {
    var kw = $.trim($('#user-search-input').val() || '')
    var params = {
      interactiveToken: interactiveChatToken(),
      kw: kw
    }
    if (kw) params.type = 2
    var saleType = state.onlineSaleType != null ? state.onlineSaleType : '0'
    if (saleType !== '0' && saleType !== 0) {
      params.saleType = Number(saleType)
      var sid = saleIdFromUrl()
      if (sid) params.saleId = sid
    }
    return params
  }

  function exportOnlineUsers() {
    var params = buildOnlineExportParams()
    if (!params.interactiveToken || typeof DWLive.hdExportOnlineUsers !== 'function') {
      window.alert('请先登录并等待 hdChat 互动 token 就绪')
      return
    }
    DWLive.hdExportOnlineUsers(params, function (res) {
      if (!res || !res.success || !res.blob) {
        window.alert((res && res.msg) || '导出失败')
        return
      }
      downloadUserBlob(res.blob, '在线用户列表')
    })
  }

  function buildOfflineParams() {
    var kw = $.trim($('#user-search-input').val() || '')
    var params = {
      interactiveToken: interactiveChatToken(),
      kw: kw,
      saleType: Number(state.offlineSaleType || 1)
    }
    if (kw) params.type = 2
    var sid = saleIdFromUrl()
    if (sid) params.saleId = sid
    return params
  }

  function fetchOfflineUsers() {
    var params = buildOfflineParams()
    DWLive.hdSearchOfflineUsers(params, function (res) {
      state.offlineList = offlineRecordsFromRes(res)
      renderList()
    })
  }

  function scheduleOfflinePoll() {
    stopOfflinePoll()
    if (state.subTab !== 'offline') return
    state.offlineTimer = setTimeout(function () {
      fetchOfflineUsers()
      scheduleOfflinePoll()
    }, 15000)
  }

  function updateOfflineUsers() {
    fetchOfflineUsers()
    scheduleOfflinePoll()
  }

  function offlineUserTypeChange(val) {
    state.offlineSaleType = val
    var kw = $.trim($('#user-search-input').val() || '')
    if (kw) {
      stopOfflinePoll()
      fetchOfflineUsers()
    } else {
      updateOfflineUsers()
    }
  }

  function searchOnlineUser() {
    var kw = $.trim($('#user-search-input').val() || '')
    var saleData = buildOnlineSaleData()
    if (hdRef && typeof hdRef.searchUserlist === 'function') {
      hdRef
        .searchUserlist(kw, saleData)
        .then(function (res) {
          var records = (res && res.data && res.data.records) || []
          state.isSearchMode = true
          state.searchDisplayList = mergeSearchWithOnline(records, onlineByIdMap())
          renderList()
        })
        .catch(function () {
          state.isSearchMode = !!(saleData || kw)
          state.searchDisplayList = kw ? localFilter(kw) : []
          renderList()
        })
      return
    }
    if (kw) {
      state.isSearchMode = true
      state.searchDisplayList = localFilter(kw)
    } else {
      state.isSearchMode = false
      state.searchDisplayList = []
    }
    renderList()
  }

  function scheduleOnlinePoll() {
    stopOnlinePoll()
    if (state.subTab !== 'online') return
    state.onlineTimer = setTimeout(function () {
      searchOnlineUser()
      scheduleOnlinePoll()
    }, 15000)
  }

  function updateOnlineUsers() {
    searchOnlineUser()
    scheduleOnlinePoll()
  }

  function onlineUserTypeChange(val) {
    state.onlineSaleType = val
    var kw = $.trim($('#user-search-input').val() || '')
    if (!kw && String(val) === '0') {
      stopOnlinePoll()
      state.isSearchMode = false
      state.searchDisplayList = []
      renderList()
      return
    }
    if (kw) {
      stopOnlinePoll()
      searchOnlineUser()
    } else {
      updateOnlineUsers()
    }
  }

  function exportOfflineUsers() {
    var params = buildOfflineParams()
    if (!params.interactiveToken || !params.liveId || typeof DWLive.hdExportOfflineUsers !== 'function') {
      window.alert('请先登录并等待 hdChat 互动 token 就绪')
      return
    }
    DWLive.hdExportOfflineUsers(params, function (res) {
      if (!res || !res.success || !res.blob) {
        window.alert((res && res.msg) || '导出失败')
        return
      }
      downloadUserBlob(res.blob, '离线用户列表')
    })
  }

  function renderOfflineList() {
    var ul = document.getElementById('user-list-ul')
    if (!ul) return
    var rows = state.offlineList || []
    if (!rows.length) {
      ul.innerHTML = '<li class="user-empty">暂无离线用户</li>'
      return
    }
    var html = ''
    for (var i = 0; i < rows.length; i++) {
      var item = rows[i]
      var uname = item.userName || item.name || '—'
      var role = item.role || 'student'
      item.role = role
      html +=
        '<li class="user-pre-list user-pre-list--offline">' +
        '<div class="user-avator-wrap">' +
        avatarEl({ role: role, name: uname, isIpOnlyRow: false }) +
        '</div>' +
        '<div class="user-line-name">' +
        '<span class="user-name-text">' +
        escapeHtml(uname) +
        '</span>' +
        saleFollowHtml(item, true) +
        '</div></li>'
    }
    ul.innerHTML = html
  }

  function onlineByIdMap() {
    var m = {}
    for (var i = 0; i < state.onlineRows.length; i++) {
      m[String(state.onlineRows[i].id)] = state.onlineRows[i]
    }
    return m
  }

  function getDisplayOnline() {
    if (state.isSearchMode) return sortUserList(state.searchDisplayList)
    return sortUserList(state.onlineRows)
  }

  function toolBtn(type, title, extra) {
    extra = extra || {}
    var iconUrl = ICON[type] || ''
    var d =
      ' data-uact="' +
      type +
      '" data-uid="' +
      escapeHtml(extra.uid || '') +
      '" data-uname="' +
      escapeHtml(extra.uname || '') +
      '" data-uip="' +
      escapeHtml(extra.uip || '') +
      '"'
    var style = iconUrl ? 'background-image:url(' + iconUrl + ')' : ''
    return (
      '<button type="button" class="user-tool-btn user-tool-' +
      type +
      '" title="' +
      escapeHtml(title) +
      '"' +
      d +
      ' style="' +
      style +
      '"></button>'
    )
  }

  function renderToolsHtml(item) {
    var sid = selfId()
    var isSelf = sid && String(item.id) === sid
    var html = ''
    var st = state.subTab

    if (item.isIpOnlyRow && st === 'ip') {
      html += toolBtn('unipgag', '解除 IP 禁言', {
        uid: item.repUserId || '',
        uname: '',
        uip: item.ip
      })
      return '<div class="user-tools">' + html + '</div>'
    }

    if (item.role === 'publisher') {
      return ''
    }

    if (item.role === 'teacher' || item.role === 'host') {
      if (!isSelf) {
        html += toolBtn('kick', '踢出', { uid: item.id, uname: item.name || item.userName })
      }
      return html ? '<div class="user-tools">' + html + '</div>' : ''
    }

    if (item.role === 'student') {
      if (!isSelf) {
        html += toolBtn('kick', '踢出', { uid: item.id, uname: item.name || item.userName })
        html += toolBtn('whisper', '私聊', { uid: item.id, uname: item.name || item.userName })
        if (item.isBan) {
          html += toolBtn('ungag', '解除禁言', { uid: item.id, uname: item.name || item.userName })
        } else {
          html += toolBtn('gag', '禁言', { uid: item.id, uname: item.name || item.userName })
        }
        if (item.isBlackIP) {
          html += toolBtn('unipgag', '解除 IP 禁言', { uid: item.id, uip: item.ip })
        } else if (item.ip) {
          html += toolBtn('ipgag', 'IP 禁言', { uid: item.id, uip: item.ip })
        }
      }
      var status =
        item.isBan || item.isBlackIP
          ? '<span class="user-ban-txt">已禁言</span>'
          : ''
      var cls = 'user-tools' + (item.isBan || item.isBlackIP ? ' user-tools--shift' : '')
      return (html ? '<div class="' + cls + '">' + html + '</div>' : '') + status
    }

    return ''
  }

  function renderList() {
    var ul = document.getElementById('user-list-ul')
    if (!ul) return

    if (state.subTab === 'offline') {
      renderOfflineList()
      return
    }

    var rows = []
    if (state.subTab === 'online') {
      rows = getDisplayOnline()
    } else if (state.subTab === 'ban') {
      rows = mergeBanTabRows(state.onlineRows, state.banApiList)
    } else {
      rows = getIpTabRows()
    }

    if (!rows.length) {
      var msg
      if (state.subTab === 'online' && state.isSearchMode) msg = '未找到匹配用户'
      else if (state.subTab === 'online') {
        msg =
          '暂无在线用户。请等待 DWLive.onRoomContext 推送，或在 demo-config 中配置 hdChat 管理者角色。'
      } else if (state.subTab === 'ban') {
        msg = '暂无禁言用户'
      } else {
        msg = '暂无禁言 IP'
      }
      ul.innerHTML = '<li class="user-empty">' + msg + '</li>'
      return
    }

    var html = ''
    for (var i = 0; i < rows.length; i++) {
      var item = rows[i]
      var badge = ''
      if (!item.isIpOnlyRow) {
        if (item.role === 'publisher') {
          badge =
            '<span class="user-role-badge publisher">' +
            escapeHtml(ADMIN_ROLE.publisher) +
            '</span>'
        } else if (item.role === 'teacher') {
          badge =
            '<span class="user-role-badge teacher">' +
            escapeHtml(ADMIN_ROLE.teacher) +
            '</span>'
        } else if (item.role === 'host') {
          badge =
            '<span class="user-role-badge host">' +
            escapeHtml(ADMIN_ROLE.host) +
            '</span>'
        }
      }
      var prefix = ''
      if (!item.isIpOnlyRow && item.role !== 'student') {
        prefix =
          '<span class="user-role-prefix">[' +
          escapeHtml(ADMIN_ROLE[item.role] || item.role) +
          ']</span> '
      }
      var mainName = item.isIpOnlyRow
        ? escapeHtml(item.ip || '')
        : escapeHtml(item.name || item.userName || '')
      var tools = renderToolsHtml(item)
      var rowCls = 'user-pre-list'
      if (item.role === 'student' && (item.isBan || item.isBlackIP)) rowCls += ' user-pre-list--flagged'
      if (tools) rowCls += ' user-pre-list--with-tools'

      html +=
        '<li class="' +
        rowCls +
        '" data-user-id="' +
        escapeHtml(String(item.id)) +
        '">' +
        '<div class="user-avator-wrap">' +
        avatarEl(item) +
        badge +
        '</div>' +
        '<div class="user-line-name">' +
        prefix +
        '<span class="user-name-text">' +
        mainName +
        '</span>' +
        saleFollowHtml(item, false) +
        '</div>' +
        tools +
        '</li>'
    }
    ul.innerHTML = html
  }

  function setSubTab(name) {
    stopOfflinePoll()
    stopOnlinePoll()
    if (name !== 'online' && name !== 'offline') {
      state.isSearchMode = false
      state.searchDisplayList = []
    }
    if (name === 'online') {
      try {
        var kw = $.trim($('#user-search-input').val() || '')
        if (!kw) {
          state.isSearchMode = false
          state.searchDisplayList = []
        }
      } catch (e) {}
    }
    if (name !== 'offline') {
      state.offlineList = []
    }
    state.subTab = name
    var tabs = document.querySelectorAll('.user-subtab[data-sub]')
    for (var i = 0; i < tabs.length; i++) {
      var t = tabs[i]
      var on = t.getAttribute('data-sub') === name
      if (on) t.classList.add('active')
      else t.classList.remove('active')
    }
    syncUserToolbars()
    if (name === 'online') {
      var okw = $.trim($('#user-search-input').val() || '')
      var otype = state.onlineSaleType != null ? state.onlineSaleType : '0'
      if (!okw && String(otype) === '0') {
        renderList()
      } else if (okw) {
        searchOnlineUser()
      } else {
        updateOnlineUsers()
      }
    } else if (name === 'offline') {
      updateOfflineUsers()
    } else if (name === 'ban' && hdRef && typeof hdRef.banUserList === 'function') {
      hdRef
        .banUserList()
        .then(function (res) {
          var data = res && res.data
          state.banApiList = (data && data.userList) || []
          renderList()
        })
        .catch(function () {
          state.banApiList = []
          renderList()
        })
    } else {
      renderList()
    }
    var foot = document.getElementById('user-footer-search')
    if (foot) foot.style.display = name === 'online' || name === 'offline' ? '' : 'none'
  }

  function applyPayload(list, blackListRaw, blackIpRaw) {
    state.blackListMap = blackListToMap(blackListRaw)
    state.blackList = Object.keys(state.blackListMap)
    state.blackIpList = Array.isArray(blackIpRaw) ? blackIpRaw.slice() : []
    state.onlineRows = []
    for (var i = 0; i < (list || []).length; i++) {
      state.onlineRows.push(normalizeUser(list[i], state.blackListMap, state.blackIpList))
    }
    if (!state.isSearchMode) renderList()
  }

  window.onRoomSettings = function (data) {
    state.roomSettings = data || null
    syncUserToolbars()
  }

  window.DemoUserPanel_applyRoomContext = function (res) {
    if (!res) return
    stopOnlinePoll()
    state.isSearchMode = false
    state.searchDisplayList = []
    try {
      $('#user-search-input').val('')
    } catch (e) {}
    applyPayload(res.onlineUsers, res.blackList, res.blackIpList)
  }

  function onOnlineUsersPayload(payload) {
    var list = (payload && payload.onlineUsers) || []
    var bl = (payload && payload.blackList) || []
    var bil = (payload && payload.blackIpList) || []
    applyPayload(list, bl, bil)
  }

  function runSearch() {
    var kw = $.trim($('#user-search-input').val() || '')
    if (state.subTab === 'offline') {
      if (kw) {
        stopOfflinePoll()
        fetchOfflineUsers()
      } else {
        updateOfflineUsers()
      }
      return
    }
    if (state.subTab !== 'online') {
      state.subTab = 'online'
      setSubTab('online')
      return
    }
    var saleType = state.onlineSaleType != null ? state.onlineSaleType : '0'
    if (!kw && String(saleType) === '0') {
      stopOnlinePoll()
      state.isSearchMode = false
      state.searchDisplayList = []
      renderList()
      return
    }
    if (kw) {
      stopOnlinePoll()
      searchOnlineUser()
    } else {
      updateOnlineUsers()
    }
  }

  function localFilter(kw) {
    var lower = kw.toLowerCase()
    return state.onlineRows.filter(function (u) {
      var n = (u.name || u.userName || '').toLowerCase()
      return n.indexOf(lower) !== -1
    })
  }

  function switchAssistantTabToChat() {
    var chatPanel = document.getElementById('panel-chat')
    var qaPanel = document.getElementById('panel-qa')
    var userPanel = document.getElementById('panel-user')
    var tabBtns = document.querySelectorAll('.assistant-tab[data-tab]')
    for (var i = 0; i < tabBtns.length; i++) {
      var b = tabBtns[i]
      if (b.getAttribute('data-tab') === 'chat') b.classList.add('active')
      else b.classList.remove('active')
    }
    if (chatPanel) chatPanel.classList.add('is-active')
    if (qaPanel) qaPanel.classList.remove('is-active')
    if (userPanel) userPanel.classList.remove('is-active')
  }

  function wrapToJqDeferred(x) {
    var d = $.Deferred()
    if (!x) {
      d.reject()
      return d.promise()
    }
    if (typeof x.then === 'function') {
      x.then(
        function () {
          d.resolve()
        },
        function (e) {
          d.reject(e)
        }
      )
      return d.promise()
    }
    if (typeof x.done === 'function') {
      x.done(function () {
        d.resolve()
      }).fail(function (e) {
        d.reject(e)
      })
      return d.promise()
    }
    d.resolve()
    return d.promise()
  }

  function banUserApi(userId, mode) {
    var m = mode != null ? mode : 0
    if (hdRef && typeof hdRef.banUser === 'function') {
      return wrapToJqDeferred(hdRef.banUser({ userId: userId, mode: m }))
    }
    return $.Deferred(function (def) {
      if (typeof DWLive.chatBan === 'function') {
        DWLive.chatBan({ viewerid: userId, type: m }, function () {
          def.resolve()
        })
      } else def.reject()
    }).promise()
  }

  function unbanUserApi(userId) {
    if (hdRef && typeof hdRef.unbanUser === 'function') {
      return wrapToJqDeferred(hdRef.unbanUser({ userId: userId }))
    }
    return $.Deferred(function (def) {
      if (typeof DWLive.chatUnBan === 'function') {
        DWLive.chatUnBan({ viewerid: userId }, function () {
          def.resolve()
        })
      } else def.reject()
    }).promise()
  }

  var banPending = { id: '', name: '' }

  function openBanModal(id, name) {
    banPending.id = id
    banPending.name = name || ''
    var q = document.getElementById('user-ban-modal-q')
    if (q) q.textContent = '确定要禁止 ' + (name || id) + ' 的发言吗？'
    var del = document.getElementById('user-ban-delete-chat')
    var keep = document.getElementById('user-ban-keep-option')
    if (del) del.checked = false
    if (keep) keep.checked = false
    var mask = document.getElementById('user-ban-modal-mask')
    if (mask) {
      mask.classList.add('is-open')
      mask.setAttribute('aria-hidden', 'false')
    }
  }

  function closeBanModal() {
    var mask = document.getElementById('user-ban-modal-mask')
    if (mask) {
      mask.classList.remove('is-open')
      mask.setAttribute('aria-hidden', 'true')
    }
    banPending.id = ''
    banPending.name = ''
  }

  function banChatNoPopup(id, name, chatStatus) {
    var mode = parseInt(chatStatus, 10) || 0
    return banUserApi(id, mode).then(
      function () {
        patchLocalBan(id, true)
        renderList()
      },
      function () {
        alert('禁言失败')
      }
    )
  }

  function confirmBanModal() {
    var del = document.getElementById('user-ban-delete-chat')
    var keep = document.getElementById('user-ban-keep-option')
    var chatStatus = del && del.checked ? '1' : '0'
    var opStatus = keep && keep.checked ? '1' : '0'
    window.localStorage.setItem('chatStatus', chatStatus)
    window.localStorage.setItem('opStatus', opStatus)
    var id = banPending.id
    var name = banPending.name
    closeBanModal()
    banChatNoPopup(id, name, chatStatus)
  }

  function handleBanChat(id, name) {
    var chatStatus = window.localStorage.getItem('chatStatus') || '0'
    var opStatus = window.localStorage.getItem('opStatus') || '0'
    if (parseInt(opStatus, 10)) {
      banChatNoPopup(id, name, chatStatus)
    } else {
      openBanModal(id, name)
    }
  }

  function bindBanModal() {
    var mask = document.getElementById('user-ban-modal-mask')
    if (!mask || mask._banBound) return
    mask._banBound = true
    var closeBtn = document.getElementById('user-ban-modal-close')
    var cancelBtn = document.getElementById('user-ban-modal-cancel')
    var okBtn = document.getElementById('user-ban-modal-ok')
    if (closeBtn) closeBtn.addEventListener('click', closeBanModal)
    if (cancelBtn) cancelBtn.addEventListener('click', closeBanModal)
    if (okBtn) okBtn.addEventListener('click', confirmBanModal)
    mask.addEventListener('click', function (e) {
      if (e.target === mask) closeBanModal()
    })
  }

  function banIpApi(userId, ip) {
    if (hdRef && typeof hdRef.banUserIP === 'function') {
      return wrapToJqDeferred(hdRef.banUserIP({ userId: userId, ip: ip }))
    }
    return $.Deferred(function (def) {
      if (typeof DWLive.ipBan === 'function') {
        DWLive.ipBan({ viewerid: userId }, function () {
          def.resolve()
        })
      } else def.reject()
    }).promise()
  }

  function unbanIpApi(userId, ip) {
    if (hdRef && typeof hdRef.unbanUserIP === 'function') {
      return wrapToJqDeferred(hdRef.unbanUserIP({ userId: userId, ip: ip }))
    }
    return $.Deferred(function (def) {
      if (typeof DWLive.ipUnBan === 'function') {
        DWLive.ipUnBan({ viewerid: userId }, function () {
          def.resolve()
        })
      } else def.reject()
    }).promise()
  }

  function patchLocalBan(userId, isBan) {
    if (isBan) state.blackListMap[String(userId)] = true
    else delete state.blackListMap[String(userId)]
    state.blackList = Object.keys(state.blackListMap)
    for (var i = 0; i < state.onlineRows.length; i++) {
      if (String(state.onlineRows[i].id) === String(userId)) {
        state.onlineRows[i].isBan = !!isBan
      }
    }
  }

  function patchLocalIp(userId, ip, isBlack) {
    if (isBlack) {
      if (state.blackIpList.indexOf(ip) === -1) state.blackIpList.push(ip)
    } else {
      state.blackIpList = state.blackIpList.filter(function (x) {
        return String(x) !== String(ip)
      })
    }
    for (var i = 0; i < state.onlineRows.length; i++) {
      if (state.onlineRows[i].ip === ip) {
        state.onlineRows[i].isBlackIP = !!isBlack
      }
    }
  }

  function bindUserListActions() {
    var ul = document.getElementById('user-list-ul')
    if (!ul || ul._uactBound) return
    ul._uactBound = true
    ul.addEventListener('click', function (e) {
      var btn = e.target.closest('[data-uact]')
      if (!btn) return
      var act = btn.getAttribute('data-uact')
      var uid = btn.getAttribute('data-uid')
      var uname = btn.getAttribute('data-uname') || ''
      var uip = btn.getAttribute('data-uip') || ''

      if (act === 'kick') {
        if (!confirm('确认踢出「' + uname + '」?')) return
        if (typeof DWLive.kickout === 'function') {
          DWLive.kickout({ viewerid: uid }, function () {
            state.onlineRows = state.onlineRows.filter(function (r) {
              return String(r.id) !== String(uid)
            })
            renderList()
          })
        }
        return
      }
      if (act === 'whisper') {
        switchAssistantTabToChat()
        if (hdRef && typeof hdRef.assistantPrivateStudent === 'function') {
          hdRef.assistantPrivateStudent({ userId: uid, userName: uname })
        } else {
          console.info('[用户] 私聊:', uname, uid, '（可接 hdChat.assistantPrivateStudent 或公聊区私聊）')
        }
        return
      }
      if (act === 'gag') {
        handleBanChat(uid, uname)
        return
      }
      if (act === 'ungag') {
        if (!confirm('解除「' + uname + '」禁言？')) return
        unbanUserApi(uid).then(
          function () {
            patchLocalBan(uid, false)
            renderList()
          },
          function () {
            alert('解除禁言失败')
          }
        )
        return
      }
      if (act === 'ipgag') {
        if (!uip || !uid) {
          alert('缺少 IP 或用户标识')
          return
        }
        if (!confirm('禁言 IP「' + uip + '」？')) return
        banIpApi(uid, uip).then(
          function () {
            patchLocalIp(uid, uip, true)
            renderList()
          },
          function () {
            alert('IP 禁言失败')
          }
        )
        return
      }
      if (act === 'unipgag') {
        if (!uip) return
        var rid = uid
        if (!rid) {
          for (var i = 0; i < state.onlineRows.length; i++) {
            if (state.onlineRows[i].ip === uip && state.onlineRows[i].role === 'student') {
              rid = state.onlineRows[i].id
              break
            }
          }
        }
        if (!rid) {
          alert('未找到该 IP 下的在线学员，无法解除 IP 禁言')
          return
        }
        if (!confirm('解除 IP「' + uip + '」禁言？')) return
        unbanIpApi(rid, uip).then(
          function () {
            patchLocalIp(rid, uip, false)
            renderList()
          },
          function () {
            alert('解除 IP 禁言失败')
          }
        )
      }
    })
  }

  function bindDom() {
    bindUserListActions()
    bindBanModal()
    var subtabs = document.querySelectorAll('.user-subtab[data-sub]')
    for (var i = 0; i < subtabs.length; i++) {
      ;(function (btn) {
        btn.addEventListener('click', function () {
          setSubTab(btn.getAttribute('data-sub'))
        })
      })(subtabs[i])
    }

    $('#user-search-btn').on('click', runSearch)
    $('#user-search-input').on('keydown', function (ev) {
      if (ev.key === 'Enter' || ev.keyCode === 13) {
        ev.preventDefault()
        runSearch()
      }
    })
    var onlineSaleSel = document.getElementById('user-online-sale-type')
    if (onlineSaleSel) {
      onlineSaleSel.addEventListener('change', function () {
        onlineUserTypeChange(onlineSaleSel.value)
      })
    }
    var onlineExportBtn = document.getElementById('user-online-export')
    if (onlineExportBtn) onlineExportBtn.addEventListener('click', exportOnlineUsers)
    var saleSel = document.getElementById('user-offline-sale-type')
    if (saleSel) {
      saleSel.addEventListener('change', function () {
        offlineUserTypeChange(saleSel.value)
      })
    }
    var exportBtn = document.getElementById('user-offline-export')
    if (exportBtn) exportBtn.addEventListener('click', exportOfflineUsers)
  }

  window.DemoUserPanel_attachHdchat = function (hd) {
    if (!hd || hd._demoUserPanelBound) return
    hd._demoUserPanelBound = true
    hdRef = hd
    hd.on('onlineUsers', function (payload) {
      onOnlineUsersPayload(payload)
      renderList()
    })
    try {
      var d = hd.$data
      if (d && d.onlineUsers && d.onlineUsers.length) {
        onOnlineUsersPayload({
          onlineUsers: d.onlineUsers,
          blackList: d.banUserList || [],
          blackIpList: d.banIpList || []
        })
      }
    } catch (e) {}
    renderList()
  }

  $(function () {
    bindDom()
    syncUserToolbars()
    setSubTab('online')
    renderList()
  })
})(window.jQuery)
