<template>
  <div class="main">
    <el-scrollbar class="left"> <!-- 左边栏 -->
      <div style="position: absolute;left: 20px; top: 20px;">
        <el-button style="background: linear-gradient(90deg, #60a5fa, #34d399); color: azure; "
          @click="showMyInfo = true">人设编辑</el-button>
      </div>

      <div class="center-line"> <!-- 左边栏按钮 -->
        <div v-for="(btn, index) in buttons" :key="index" style="width: 100%;">
          <div v-if="btn.show?.()" class="left-button" :style="btn.style ? btn.style() : {}"
            :title="btn.tooltip ? btn.tooltip() : ''" @click="btn.onClick" @mouseenter="hoverIndex = index"
            @mouseleave="hoverIndex = null">
            <img :src="hoverIndex === index ? btn.img.hover : btn.img.normal" alt="">
            {{ btn.label }}
          </div>

        </div>
        <div style="position: absolute;left: 20px; bottom: 20px;">
          <el-button @click="showHostDlg = true">执行主播库</el-button>
          <el-button type="info" @click="uploadLogFile">上传日志</el-button>
          <div>
            <!-- 监听 q.tenant.*（爬虫） -->
            <el-switch v-model="sseCrawlerEnabled" inline-prompt active-text="监听爬虫" inactive-text="监听爬虫"
              style="margin-left: 8px;" @change="onToggleCrawler" />
            <el-button size="small" style="margin-left: 8px;" @click="setSseLimit('crawler')">设置</el-button>
            <span style="margin-left: 4px;">剩余 {{ sseCrawlerRemaining }}/{{ sseCrawlerLimit }} 条</span>

            <!-- 监听 b.tenant.*（大哥） -->
            <el-switch v-model="sseBossEnabled" inline-prompt active-text="监听大哥" inactive-text="监听大哥"
              style="margin-left: 8px;" @change="onToggleBoss" />
            <el-button size="small" style="margin-left: 8px;" @click="setSseLimit('boss')">设置</el-button>
            <span style="margin-left: 4px;">剩余 {{ sseBossRemaining }}/{{ sseBossLimit }} 条</span>

          </div>
        </div>
      </div>
    </el-scrollbar>
    <!-- 中间手机区域 -->
    <div class="content" @click.self="selectedDevice = 999">
      <div class="video-container" v-for="(device, index) in deviceInformation" :key="device.deviceId">
        <div class="video-canvas" :class="{
          active: selectedDevice === index,
          'net-bad': netStatus[device.deviceId] === false
        }" :style="getCanvasStyle(index)" @click="selectDevice(index)">
          <img class="stream" :src="imgSrcMap[device.deviceId] || ''" :data-id="device.deviceId"
            :ref="el => (imgRefs[device.deviceId] = el)" />

          <canvas v-show="selectedDevice === index" class="overlay"
            @mousedown.stop="(e) => onCanvasDown(device.deviceId, e, index)"
            @mouseup.stop="(e) => onCanvasUp(device.deviceId, e, index)"
            @mousemove.stop="(e) => onCanvasMove(device.deviceId, e, index)" />
        </div>
        <div class="input-info" v-show="selectedDevice == index">
          <div class="app-button" @click="restartTikTok({ udid: device.deviceId })">重置tiktok</div>
          <div class="app-button" @click="getMesList(device.deviceId)">获取当前聊天记录</div>
          <div class="app-button" @click="stopOne(device.deviceId)">停止任务</div>
          <div class="app-button" @click="scheduleEnabled = true; runTask(runType, device.deviceId);">
            开启
          </div>

        </div>
      </div>


    </div>
    <div class="right center-line" @click.self="selectedDevice = 999">
      <!-- <div style="margin: 30px;"></div> -->

      <ChatDialog :visible="openShowChat" :messages="chatList" />
      <MessageDialogd :visible="openShowChat" :messages="MesNewList" :sound-src="ding" />
      <div class="expire-time">
        到期时间：{{ timestampToTime(userdata.aiExpireTime) }}
      </div>
    </div>
    <img v-if="isWifi" style="position: absolute; right: 20px; top: 10px; height: 30px;" src="@/assets/wifi.png"></img>
    <MultiLineInputDialog v-model:visible="showDialog" :initialText='initialTextStr' :title="dialogTitle"
      :index="selectedDevice" @confirm="onDialogConfirm" @cancel="stopAll(2000, 'click')" />
    <HostListManagerDialog v-model:visible="showHostDlg" @save="onHostSaved" @invitType="invitTypeFun" />

    <TranslationDialog v-model="showtransDlg" :type="transDlgType" :translateFn="doTranslate"
      storage-key-prefix="demo-translation" @confirm="onConfirm" @cancel="stopAll(2000, 'click')" />
  </div>
  <!-- <AgentGuildDialog v-model="showMyInfo" :model="formInit" @save="handleSave" /> -->
  <AgentGuildDialog v-model="showMyInfo" :model="{
    agentName: borkerConfig.agentName,
    guildName: borkerConfig.guildName,
    contactTool: borkerConfig.contactTool,
    contact: borkerConfig.contact
  }" @save="onSave" />
  <!-- 定时调度配置弹窗 -->
  <el-dialog v-model="showScheduleDlg" title="定时调度（每小时）" width="550px" :close-on-click-modal="false" draggable>
    <div style="display:grid;grid-template-columns: 100px 1fr; gap:12px; align-items:center;">
      <div>片段 A</div>
      <div style="display:flex; gap:8px; align-items:center;">
        <el-select v-model="schedAKey" style="width:140px;">
          <el-option label="一键私信" value="follow" />
          <el-option label="刷视频(养号)" value="like" />
          <el-option label="刷直播" value="brushLive" />
          <el-option label="监测消息" value="listen" />
        </el-select>
        <el-input-number v-model="schedAMin" :min="1" :max="59" />
        <span>分钟</span>
      </div>

      <div>片段 B</div>
      <div style="display:flex; gap:8px; align-items:center;">
        <el-select v-model="schedBKey" style="width:140px;">
          <!-- <el-option label="一键关注" value="follow" /> -->
          <el-option label="刷视频(养号)" value="like" />
          <el-option label="刷直播" value="brushLive" />
          <el-option label="监测消息" value="listen" />
        </el-select>
        <el-input-number v-model="schedBMin" :min="1" :max="59" />
        <span>分钟</span>
      </div>

      <div>总时长</div>
      <div><b>{{ schedAMin + schedBMin }}</b> 分钟（必须等于 60）</div>

      <div>换号</div>
      <div style="display:flex; gap:8px; align-items:center;">
        <el-switch v-model="interruptEnabled" active-text="开启换号" />
        <el-input-number v-model="interruptEveryMin" :min="1" :max="24" />
        <span>小时换一次</span>
      </div>
      <div>联盟号</div>
      <div style="display:flex; gap:8px; align-items:center;">
        <el-switch v-model="isAlliance" active-text="联盟号快速私信" />

      </div>

    </div>

    <template #footer>
      <el-button @click="showScheduleDlg = false">取消</el-button>
      <el-button type="primary" @click="saveSchedule">开启</el-button>
    </template>
  </el-dialog>

</template>

<script setup>
import { ref, reactive, onMounted, onUnmounted, watch, computed } from "vue";
import { useRouter } from 'vue-router';
import {
  setphoneXYinfo, getphoneXYinfo, getUser,
  getHostList, setHostList, getContentpriList,
  setContentpriList, getContentList, setContentList,
  getContentListMultiline, getContentpriListMultiline, getHostfilters
} from '@/stores/storage'
import { connectSSE } from '@/utils/sseUtils'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import { chat, translationToChinese, translation, customTranslation } from "@/api/chat";
import HostListManagerDialog from '@/components/HostListManagerDialog.vue'
import AgentGuildDialog from '@/components/AgentGuildDialog.vue'

import MultiLineInputDialog from '@/components/MultiLineInputDialog.vue';
import TranslationDialog from '@/components/translationDialog.vue';

import ChatDialog from '@/components/ChatDialog.vue'
import MessageDialogd from '@/components/MessageDialogd.vue'
import { logout, updates, health } from '@/api/account';
import {
  toHome,
  growAccount,
  stopScript,
  watchLiveForGrowth,
  monitorMessages,
  passAnchorData,
  followAndGreetUnion,
  addTempAnchorData,
  getChatTextInfo,
  setLoginInfo,
  aiConfig,
  selectLast,
  changeAccount,
  stopAllTask,
  anchorList,
  restartTikTok,
  getDeviceNetStatus
} from '@/api/ios';
import ding from '@/assets/mes.wav'
//引入两个分包方法
import { useDevices } from '@/composables/useDevices'
import { useScreenStreams } from '@/composables/useScreenStreams'

const router = useRouter();
const openShowChat = ref(true)
//主播库
const showHostDlg = ref(false)
//ai人设弹框
const showMyInfo = ref(false)
//翻译弹框
const showtransDlg = ref(false)
let transDlgType = ref('')

// 假设这是你已有的数据
const borkerConfig = reactive({
  agentName: '',
  guildName: '',
  contactTool: '',
  contact: ''
})

//评论 自动化 
let common = ref(true);
// 自动化 
let auto = ref(true);
let isTranslate = ref(false)
let initialTextStr = ref('') // 初始文本字符串
// 批次缓冲（仅用于当前“波”）
let batch = [];                 // [{ country, text }]
let flushTimer = null;

let hostList = []       // 主播列表
let comonList = []      //评论列表
//查询 列表 新消息轮询
let getNetworkListtimer = null;
let userdata = getUser();
let chatList = ref([])

let MesNewList = ref([])

//start弹窗
let isMsgPop = ref(false)

let runType = ref('')
let isMonitorOn = ref(false)
const hoverIndex = ref(null)  //选中
let showDialog = ref(false);//弹窗是否显示
let dialogTitle = ref('');//当前弹窗类型
// 你可以用这种方式声明按钮们
//停止中
let stopLoading = null

// 是否未开通 AI 自动回复（= 监测消息不可用）
const isListenLockedByPlan = computed(() => String(userdata?.aiReplay ?? '0') === '0')


// === 1. 设备相关 ===
const {
  deviceInformation,
  getDeviceListFun,
  getListTimer,
  openTk,
  stopOne,
} = useDevices()

// === 2. 屏幕流相关 ===
const {
  selectedDevice,
  imgRefs,
  imgSrcMap,
  refreshAllImgs,
  refreshOneImg,
  refreshAllStopImgs,
  getCanvasStyle,
  imgWH,
  selectDevice,
  onCanvasDown,
  onCanvasMove,
  onCanvasUp,
} = useScreenStreams(deviceInformation)

// 每台设备的网络状态：true=正常，false=异常
const netStatus = reactive({}) // { [deviceId]: boolean }

// —— 网络波动联动（每设备暂停/恢复）——
const offlineFlags = reactive({})   // { [deviceId]: true/false } 是否因断网而暂停过
const lastNet = reactive({})        // { [deviceId]: true/false } 上一次看到的网络状态
const resumeTimers = new Map()      // deviceId -> setTimeout id
const NET_RESUME_STABLE_MS = 6000   // 断网恢复后，等待网络稳定的毫秒数

function clearResumeTimer(id) {
  const t = resumeTimers.get(id)
  if (t) {
    clearTimeout(t)
    resumeTimers.delete(id)
  }
}

// —— 在线判定/过滤 ——
// 红框=netStatus[id] === false 视为离线；其它（true/undefined）都当作可用
const isOnline = (id) => netStatus[id] !== false
const onlineOnly = (ids) => ids.filter(isOnline)

// 当前是否被其它模式占用（四个互斥按钮专用）
const isLocked = (type) => !!runType.value && runType.value !== type

// —— SSE 开关（分别控制爬虫队列 & 大哥队列）——
const sseCrawlerEnabled = ref(JSON.parse(localStorage.getItem('SSE_CRAWLER_ENABLED') ?? 'true')) // q.tenant.*
const sseBossEnabled = ref(JSON.parse(localStorage.getItem('SSE_BOSS_ENABLED') ?? 'false'))       // b.tenant.*
const sseCrawlerLimit = ref(Number(localStorage.getItem('SSE_CRAWLER_LIMIT') ?? '500'))
const sseBossLimit = ref(Number(localStorage.getItem('SSE_BOSS_LIMIT') ?? '500'))
const sseCrawlerRemaining = ref(Number(localStorage.getItem('SSE_CRAWLER_REMAIN') ?? sseCrawlerLimit.value))
const sseBossRemaining = ref(Number(localStorage.getItem('SSE_BOSS_REMAIN') ?? sseBossLimit.value))

const normalizeSseLimit = (val, fallback) => {
  const n = Number(val)
  if (!Number.isFinite(n) || n <= 0) return fallback
  return Math.floor(n)
}

watch(sseCrawlerEnabled, v => {
  localStorage.setItem('SSE_CRAWLER_ENABLED', JSON.stringify(v))
})


watch(sseBossEnabled, v => {
  localStorage.setItem('SSE_BOSS_ENABLED', JSON.stringify(v))
 
})

watch(sseCrawlerLimit, v => {
  const next = normalizeSseLimit(v, 500)
  if (next !== v) {
    sseCrawlerLimit.value = next
    return
  }
  localStorage.setItem('SSE_CRAWLER_LIMIT', String(next))
})

watch(sseBossLimit, v => {
  const next = normalizeSseLimit(v, 500)
  if (next !== v) {
    sseBossLimit.value = next
    return
  }
  localStorage.setItem('SSE_BOSS_LIMIT', String(next))
})

watch(sseCrawlerRemaining, v => {
  const next = normalizeSseLimit(v, 0)
  if (next !== v) {
    sseCrawlerRemaining.value = next
    return
  }
  localStorage.setItem('SSE_CRAWLER_REMAIN', String(next))
})

watch(sseBossRemaining, v => {
  const next = normalizeSseLimit(v, 0)
  if (next !== v) {
    sseBossRemaining.value = next
    return
  }
  localStorage.setItem('SSE_BOSS_REMAIN', String(next))
})

// 至少有一个开着才算启用 SSE
const sseAnyEnabled = computed(() => sseCrawlerEnabled.value || sseBossEnabled.value)

const bumpSseCount = (type) => {
  const isCrawler = type === 'crawler'
  const remainingRef = isCrawler ? sseCrawlerRemaining : sseBossRemaining
  if (remainingRef.value <= 0) {
    if (isCrawler) {
      if (sseCrawlerEnabled.value) {
        sseCrawlerEnabled.value = false
        window.electronAPI.toggleMq('crawler', false)
      }
    } else {
      if (sseBossEnabled.value) {
        sseBossEnabled.value = false
        window.electronAPI.toggleMq('boss', false)
      }
    }
    return false
  }

  remainingRef.value -= 1
  if (remainingRef.value <= 0) {
    if (isCrawler) {
      sseCrawlerEnabled.value = false
      window.electronAPI.toggleMq('crawler', false)
    } else {
      sseBossEnabled.value = false
      window.electronAPI.toggleMq('boss', false)
    }
    ElMessage.info(`监听${isCrawler ? '爬虫' : '大哥'}条数已用完，已自动关闭`)
  }
  return true
}

const setSseLimit = async (type) => {
  const isCrawler = type === 'crawler'
  const label = isCrawler ? '爬虫' : '大哥'
  const limitRef = isCrawler ? sseCrawlerLimit : sseBossLimit
  const remainingRef = isCrawler ? sseCrawlerRemaining : sseBossRemaining

  try {
    const { value } = await ElMessageBox.prompt(
      `请输入监听${label}条数`,
      '设置监听条数',
      {
        inputValue: String(limitRef.value || 0),
        inputPattern: /^[1-9]\d*$/,
        inputErrorMessage: '请输入正整数',
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      }
    )
    const next = normalizeSseLimit(value, limitRef.value || 500)
    limitRef.value = next
    remainingRef.value = next
  } catch (e) {
    // 用户取消
  }
}

// 互斥按钮的样式：激活=红，锁定=半透明且禁点
const ctrlStyle = (type) => {
  const lockedByMode = isLocked(type)
  const disableListen = (type === 'listen') && isListenLockedByPlan.value

  const style = {
    backgroundColor: runType.value === type ? 'red' : '',
    opacity: (lockedByMode || disableListen) ? 0.5 : 1,
    // 允许事件，以便显示浏览器原生 title 提示 & 点击弹出 ElMessage
    pointerEvents: lockedByMode ? 'none' : 'auto',
    cursor: (lockedByMode || disableListen) ? 'not-allowed' : 'pointer',
    filter: disableListen ? 'grayscale(1)' : ''
  }
  return style
}


const buttons = [
  {
    label: '刷新',
    onClick: () => refreshAllImgs(),
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn1.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn1-1.png', import.meta.url).href
    }
  },
  {
    label: '打开tiktok',
    onClick: () => openTk(),
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn2.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn2-2.png', import.meta.url).href
    }
  },
  {
    label: '返回主页',
    onClick: () => {
      deviceInformation.value.forEach((item) => {
        toHome({ udid: item.deviceId })
      })
    },
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn3.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn3-3.png', import.meta.url).href
    }
  },
  {
    label: '刷直播',
    onClick: () => {
      if (runType.value == 'brushLive') {
        deviceInformation.value.forEach((item) => {
          stopScript({ udid: item.deviceId })
        })
        runType.value = ''
        return
      };

      // 若被其它模式占用：直接返回（已在样式层禁点，这里双保险）
      if (isLocked('brushLive')) return

      runType.value = 'brushLive'
      deviceInformation.value.forEach((item) => watchLiveForGrowth({ udid: item.deviceId }))
    },
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn4.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn4-4.png', import.meta.url).href
    },
    style: () => ctrlStyle('brushLive')

  },
  {
    label: '刷视频',
    onClick: () => {
      if (runType.value == 'like') {
        deviceInformation.value.forEach((item) => {
          stopScript({ udid: item.deviceId })
        })
        runType.value = ''
        return
      };

      if (isLocked('like')) return
      // runType.value = 'like'
      // deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId })) 
      dialogTitle.value = '视频评论';
      initialTextStr.value = getContentListMultiline();
      setTimeout(() => {
        console.log("内容", initialTextStr.value, getContentListMultiline())
        showDialog.value = true;


      }, 500)

    },
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn5.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn5-5.png', import.meta.url).href
    },
    style: () => ctrlStyle('like')
  },

  {
    label: '启动调度任务',
    onClick: () => openScheduleDialog(),
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn6.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn6-6.png', import.meta.url).href
    },
    style: () => ctrlStyle('follow')

  },
  {
    label: '监测消息',
    // 点击前先判断是否未开通，未开通只提示不执行
    onClick: () => {
      if (isListenLockedByPlan.value) {
        ElMessage.warning('未开通 AI 自动回复功能，请联系管理员开通');
        return
      }
      if (runType.value == 'listen') {
        deviceInformation.value.forEach((item) => {
          stopScript({ udid: item.deviceId })
        })
        runType.value = ''
        return
      }
      if (isLocked('listen')) return
      runType.value = 'listen'
      deviceInformation.value.forEach((item) => monitorMessages({ udid: item.deviceId }))
    },
    // 悬停提示文案
    tooltip: () => (isListenLockedByPlan.value ? '未开通：AI 自动回复' : ''),
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn1.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn1-1.png', import.meta.url).href
    },
    style: () => ctrlStyle('listen')
  },
  // {
  //   label: '定时调度',
  //   onClick: () => openScheduleDialog(),
  //   show: () => true,
  //   img: {
  //     normal: new URL('@/assets/video/leftBtn1.png', import.meta.url).href,
  //     hover: new URL('@/assets/video/leftBtn1-1.png', import.meta.url).href
  //   }
  // },
  {
    label: '全部停止',
    onClick: () => stopAll(2000, 'click'),
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn8.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn8-8.png', import.meta.url).href
    },

  },
  {
    label: '登出',
    onClick: () => doLogout(),
    show: () => true,
    img: {
      normal: new URL('@/assets/video/leftBtn9.png', import.meta.url).href,
      hover: new URL('@/assets/video/leftBtn9-9.png', import.meta.url).href
    }
  }
]
//联盟号
const isAlliance = ref(false)


// —— 打断器配置 ——
// 是否开启
const interruptEnabled = ref(false)
// 每隔多少分钟打断一次（可做弹窗配置）
const interruptEveryMin = ref(2)
// 打断器最大重试次数 & 每次超时（按需调）
const interruptMaxRetries = 1
const interruptCallTimeoutMs = 120_000

// 运行态
let interrupting = false
let lastInterruptTs = Number(localStorage.getItem('INT_LAST_TS') || '0')

// 暂存“被打断时”的片段状态，用于恢复
let pauseSnapshot = null
// 结构：{ index, elapsedBeforePause }



const schedulePlan = [
  { key: 'follow', duration: 40 * 60 * 1000 },
  { key: 'like', duration: 20 * 60 * 1000 },
]

// 调度状态（持久化一下，避免刷新丢失）
let scheduleState = (() => {
  try {
    const saved = JSON.parse(localStorage.getItem('SCHEDULE_STATE') || '{}')
    if (saved && typeof saved.index === 'number' && typeof saved.startTime === 'number') {
      return saved
    }
  } catch { }
  return { index: 0, startTime: Date.now() }
})()

let scheduleTimer = null      // 轮询定时器句柄
const scheduleTickMs = 30_000 // 每 30s 检查一次是否到切换点
let scheduleEnabled = ref(false)    // 需要时可手动关闭调度（例如“全部停止”）
// 弹窗
const showScheduleDlg = ref(false)

// 两个时间片（默认 A=follow 40min，B=like 20min）
const schedAKey = ref('follow')
const schedAMin = ref(40)
const schedBKey = ref('like')
const schedBMin = ref(20)
// 打开弹窗：把当前 schedulePlan 映射到 UI
function openScheduleDialog() {
  // 把当前计划读出来（只支持两个片段的简易版）
  if (Array.isArray(schedulePlan) && schedulePlan.length >= 2) {
    const a = schedulePlan[0], b = schedulePlan[1]
    schedAKey.value = a?.key || 'follow'
    schedAMin.value = Math.max(1, Math.round((a?.duration || 40 * 60_000) / 60_000))
    schedBKey.value = b?.key || 'like'
    schedBMin.value = Math.max(1, Math.round((b?.duration || 20 * 60_000) / 60_000))
  }
  showScheduleDlg.value = true
}

// 保存：校验=60 分钟 → 更新 schedulePlan → 持久化 → 重启轮询
function saveSchedule() {

  const total = schedAMin.value + schedBMin.value
  if (total !== 60) {
    ElMessage.error('两个片段相加必须等于 60 分钟')
    return
  }
  schedulePlan.splice(0, schedulePlan.length,
    { key: schedAKey.value, duration: schedAMin.value * 60_000 },
    { key: schedBKey.value, duration: schedBMin.value * 60_000 },
  )

  // 存 localStorage
  localStorage.setItem('SCHEDULE_PLAN', JSON.stringify(schedulePlan))
  localStorage.setItem('SCHEDULE_ENABLED', JSON.stringify(!!scheduleEnabled.value))

  // 重置时间片起点并立即生效
  scheduleState.index = 0
  scheduleState.startTime = Date.now()
  localStorage.setItem('SCHEDULE_STATE', JSON.stringify(scheduleState))

  // 若启用则重启轮询
  startScheduleLoop()

  showScheduleDlg.value = false
  ElMessage.success('已保存定时调度')
}

function getMesList(deviceId) {
  getChatTextInfo({ udid: deviceId }).then((res) => {
    if (res) {
      chatList.value = res
      console.log(chatList.value)
      getTranslation(chatList.value)
    }
  })
}

async function stopAll(time, type) {
  stopLoading = ElLoading.service({
    lock: true,
    text: '停止中',
    background: 'rgba(0, 0, 0, 0.7)',
  });

  // 🔒 先强制关闭所有提示弹窗
  ElMessageBox.close();
  if (type == 'click') {
    //  关闭调度定时器
    clearInterval(scheduleTimer)
    scheduleTimer = null
  }


  scheduleEnabled.value = false;
  runType.value = '';
  isMsgPop.value = false;
  dropCurrentWave();

  // ========== ⏰ 新增超时逻辑 ==========
  const STOP_TASK_TIMEOUT_MS = 120000; // 120 秒超时，可调整

  // 封装一个带超时保护的 Promise
  const withTimeout = (p, ms) => {
    return Promise.race([
      p,
      new Promise((resolve) =>
        setTimeout(() => {
          console.warn(`[stopAllTask] 超时 ${ms}ms`);
          resolve('timeout');
        }, ms)
      ),
    ]);
  };

  try {
    // 1️⃣ 带超时保护地调用接口
    const res = await withTimeout(
      stopAllTask(deviceInformation.value.map(item => item.deviceId)),
      STOP_TASK_TIMEOUT_MS
    );

    if (res === 'timeout') {
      console.warn('stopAllTask 请求超时，自动继续后续逻辑');
    }

    // 2️⃣ 等待指定时间（原逻辑保留）
    await new Promise(r => setTimeout(r, time));

    stopLoading.close();
    console.log('全部停止成功', printCurrentTime());
    ElMessage.success('全部停止成功');

    // 3️⃣ 无论接口成功或超时都返回 true
    return true;
  } catch (e) {
    console.error('停止失败', printCurrentTime(), e);
    ElMessage.error('脚本已停止');
    stopLoading.close();
    return false;
  }
}


//确认多行文本框内容
function onDialogConfirm(result, type, index, data) {
  console.log(type, result, data);
  if (type == '主播ID') {
    hostList = (result || []).map(id => ({ id, country: '' }))

    //无需评论，注释，如果需要注释下面代码，放开后面代码
    // dialogTitle.value = '私信';
    // setTimeout(() => {
    //   showDialog.value = true;

    //   initialTextStr.value = getContentpriListMultiline();
    // }, 500)
    dialogTitle.value = '评论';
    setTimeout(() => {
      showDialog.value = true;

      initialTextStr.value = getContentListMultiline();
    }, 500)
  } else if (type == '评论') {
    comonList = result
    setContentList(result)
    common.value = data.common

    transDlgType.value = '私信'
    showtransDlg.value = true
  } else if (type == '视频评论') {
    runType.value = 'like'
    deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId, comment: result, isComment: data.common }))
  } else if (type == '评论(无消息将刷视频)') {
    runType.value = 'listen'
    deviceInformation.value.forEach((item) => monitorMessages({ udid: item.deviceId, comment: result }))
  }

}

onMounted(async () => {
  const loading = ElLoading.service({
    lock: true,
    text: '检测设备中...',
    background: 'rgba(0, 0, 0, 0.7)',
  });
  getDeviceListFun()


  const res = await window.electronAPI.isiproxy({
    intervalMs: 2000,
    exeName: 'iproxy.exe',
    // maxWaitMs: 3000, // 可选：5分钟超时
    maxWaitMs: 300000, // 可选：5分钟超时
  });
  if (res.running) {
    // 检测到了，你再决定是否跳转
    loading.close();
    console.log('检测到了')
  } else {
    // 超时兜底提示
    loading.close();
    console.log('未检测到设备')
    ElMessage.error(`未检测到设备`)

  }

  console.log("sse过滤的数据", getHostfilters())
  //MQ链接
  window.electronAPI.startMq(userdata.tenantId, userdata.id).then(() => {
    // 初始化时同步状态
    window.electronAPI.toggleMq('crawler', sseCrawlerEnabled.value)
    window.electronAPI.toggleMq('boss', sseBossEnabled.value)
  })


  // 初始化时获取设备列表
  //每3秒获取一次设备列表 消息列表 和 查询主播列表是否还有主播
  getListTimer.value = setInterval(async () => {
    getDeviceListFun()      //获取设备列表
    selectLastFun()         //获取新消息
    const hostsList = await getStoredHostList()   //获取主播列表
    // console.log(hostsList.length)
    //当私信主播时，主播列表没有数据了，提示列表空了 并且关闭私信
    if (runType.value == 'follow') {
      if (hostsList.length <= 0) {
        await stopAll(2000)
        runType.value = 'like'
        deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId, comment: getContentList(), isComment: common.value }))

        ElMessageBox.alert('私信全部完成！(刷视频中)', '提示', {
          confirmButtonText: 'OK',
          callback: (action) => {

          },
        })

      }
    }

  }, 3000)

  getNetworkListtimer = setInterval(async () => {
    await checkVPN()
    await refreshNetStatus()
    health().then((res) => {

    }).catch((err) => {
      if (err.code === 40400) {
        //关闭获取设备列表的方法
        clearInterval(getListTimer.value)
        getListTimer.value = null
        //关闭获取网络状态的方法
        clearInterval(getNetworkListtimer)
        getNetworkListtimer = null
      }


    })
  }, 1000 * 60)


  if (!await isAiConfig()) {
    showMyInfo.value = true
  }

  function scheduleFlush(handler, delay = 400) {
    if (flushTimer) clearTimeout(flushTimer);
    flushTimer = setTimeout(() => {
      if (batch.length) {
        const items = batch.slice(); // 拷贝一份
        batch.length = 0;            // 清空批次
        try {
          handler(items);
        } catch (e) {
          console.error('[SSE flush error]', e);
          // 出错不回灌，避免重复提交；必要时可根据需要 batch.push(...items)
        }
      }
    }, delay);
  }
  let FWnum = 0
  // —— SSE 接收 —— 
  const es = connectSSE('http://localhost:3312/events', (data) => {
    // 所有 SSE 关掉的话，直接不处理
    if (!sseAnyEnabled.value) return

    if (data === 'start') {
      // 新一波开始：清空上一波的缓冲，重置防抖
      dropCurrentWave()
      return
    }

    // 1️⃣ 判断来源：_mqMeta = 1(q.tenant.*) / 2(b.tenant.*)
    const metaCode = data && data._mqMeta
    const fromCrawler = metaCode === 1 || metaCode === '1'   // 爬虫队列 q.tenant.*
    const fromBoss = metaCode === 2 || metaCode === '2'      // 大哥队列 b.tenant.*

    // 没有标记的老数据，一律按爬虫处理（可选）
    const isUnknown = !fromCrawler && !fromBoss

    // 2️⃣ 按开关过滤
    if (fromCrawler && !sseCrawlerEnabled.value) return
    if (fromBoss && !sseBossEnabled.value) return
    if (fromCrawler) {
      if (!bumpSseCount('crawler')) return
    } else if (fromBoss) {
      if (!bumpSseCount('boss')) return
    }
    if (isUnknown && !sseCrawlerEnabled.value) return  // 老数据当爬虫看

    // 3️⃣ 按来源取不同字段
    let country = ''
    let text = ''
    let invitationType = ''
    let id = ''

    // ⭐ 用于筛选的字段
    let fans = null
    let coins = null
    let onlineFans = null
    let level = ''       // 主播等级

    if (fromBoss) {
      // 大哥队列 b.tenant.* 进来的那条 JSON 结构：
      // {"id":5681,"displayId":"80",...,"region":"西南",...,"_mqMeta":2}
      country = data.region || ''
      // 主播ID 优先 displayId → hostDisplayId → userIdStr → userId
      text = data.displayId
        || data.hostDisplayId
        || data.userIdStr
        || (data.userId != null ? String(data.userId) : '')
      invitationType = data.invitationType != null ? data.invitationType : 2  // 金票默认 2
      id = data.id != null ? data.id : ''

      // ⭐ 这里根据真实字段来改，如果你后端是别的字段名，改这一段就行
      fans = data.fllowernum ?? null
      coins = data.hostsCoins ?? null
      onlineFans = data.onlineFans ?? null
      level = data.hostsLevel || ''
    } else {
      // 爬虫队列 q.tenant.*（以前的老逻辑）
      country = data && data.country != null ? data.country : ''
      text = data && (data.hostsId != null ? data.hostsId : data.text)
      invitationType = data && (data.invitationType != null ? data.invitationType : '')
      id = data && data.id != null ? data.id : ''

      // ⭐ 同样根据真实字段改
      fans = data.fllowernum ?? null
      coins = data.hostsCoins ?? null
      onlineFans = data.onlineFans ?? null
      level = data.hostsLevel || ''
    }

    // 4️⃣ 先根据筛选条件过滤，不符合的直接丢掉
    const pass = matchByHostFilters({
      onlineFans,
      level,
      invitationType,   // 把票种也传进去
    })

    if (!pass) {
      FWnum++
      console.log(
        '丢弃' + FWnum + '条不符合筛选条件的主播：',
        JSON.stringify({ text, invitationType, onlineFans, level })
      )
      return
    }


    if (!text) return

    batch.push({ country, text, invitationType, id })

    //400ms内如果没有数据了  进行入库，如果400ms内进入了数据  重置计时器  一波一波入库
    scheduleFlush((items) => {
      // 批量入库
      const list = items.map(h => ({
        anchorId: h.text,
        country: h.country || '',
        invitationType: h.invitationType,
        state: stateByInvType(h.invitationType),
      }))
      updates(items.map(h => ({
        id: h.id,
        aiOperation: 1,
      })))
      addTempAnchorData(list)
    }, 400)
  })




})

onUnmounted(() => {
  //关闭获取设备列表的方法
  clearInterval(getListTimer.value)
  getListTimer.value = null
  //关闭获取网络状态的方法
  clearInterval(getNetworkListtimer)
  getNetworkListtimer = null

  //关闭调度定时器
  if (scheduleTimer) {
    clearInterval(scheduleTimer)
    scheduleTimer = null
  }
})


//获取新消息
const selectLastFun = () => {
  selectLast().then((res) => {
    let mesInfoData = res
    mesInfoData.forEach(element => {
      deviceInformation.value.forEach((item, index) => {
        if (item.deviceId == element.device) {
          element.device = index + 1 + '号设备'
        }
      })
    });

    MesNewList.value = [...mesInfoData];
  })
}

async function uploadLogFile() {
  let loading = null
  try {
    // 先弹出确认框
    await ElMessageBox.confirm(
      '确定要上传日志文件吗？',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }
    )
    // 如果点了确定，就会走到这里
    loading = ElLoading.service({
      lock: true,
      text: '上传中...',
      background: 'rgba(0, 0, 0, 0.7)',
    })
    const res = await setLoginInfo({
      "tenantId": userdata.tenantId,
      "userId": userdata.id,
      "token": userdata.tokenValue
    })
    loading.close()
    console.log("上传文件返回", res)
    if (res) {
      console.log("✅ 上传成功:", res)
      ElMessage.success('✅ 上传成功')
    } else {
      console.error("❌ 上传失败:", res.msg)
      ElMessage.error('❌ 上传失败: ' + (res.msg || '未知错误'))
    }
  } catch (err) {
    if (loading) {
      loading.close()
    }
    // 如果用户点了取消，会进入这里
    if (err === 'cancel' || err === 'close') {
      ElMessage.info('已取消上传')
    } else {
      console.error("❌ 上传异常:", err)
      ElMessage.error('❌ 上传异常: ' + err)
    }

  }
}
function runTask(key, deviceId, type) {

  // —— 若指定单设备，且该设备当前离线，则直接跳过 —— 
  if (deviceId && !isOnline(deviceId)) {
    markPendingForOffline([deviceId]);     // ⭐ 新增：让它后续来网能自动跟上
    ElMessage.warning('该设备当前网络不可用，已跳过');
    return;
  }
  console.log('[schedule] 切换到任务:', key, printCurrentTime())

  forceActivate(key, async () => {
    if (key === 'follow') {
      console.log("进入follow", scheduleEnabled.value)
      if (scheduleEnabled.value) {
        //如果有id 就只执行一个  并且return出去
        if (deviceId) {
          if (isAlliance.value) {
            followAndGreetUnion(
              {
                deviceList: [deviceId],
                anchorList: [],
                prologueList: getContentpriList(),
                needReply: auto.value,
                needTranslate: isTranslate.value,
              }
            ).then((res) => {
              hostList = []
              return
            })
          } else {
            passAnchorData(
              {
                deviceList: [deviceId],
                anchorList: [],
                prologueList: getContentpriList(),
                comment: comonList,
                needReply: auto.value,
                needTranslate: isTranslate.value,
                isComment: common.value    //是否评论

              }
            ).then((res) => {
              hostList = []
              return
            })
          }

          return

        } else {
          //没有id 直接停止所有设备
          await stopAll(2000)

          //第一个小时结束后,第二轮开始的时候,直接进入follow
          setTimeout(() => {
            scheduleEnabled.value = true
            runType.value = 'follow'
            //过滤无网络设备
            const allIds = deviceInformation.value.map(item => item.deviceId)
            // 在线要启动的
            const deviceIds = onlineOnly(allIds)
            if (!deviceIds.length) {
              ElMessage.warning('没有在线设备可执行任务');
              // 也别忘了把离线的标成待恢复
              markPendingForOffline(allIds.filter(id => netStatus[id] === false))
              return
            }
            // ⭐ 给这次被跳过的离线设备打标记（后续来网自动补上）
            markPendingForOffline(allIds.filter(id => !deviceIds.includes(id)))

            if (isAlliance.value) {

              followAndGreetUnion(
                {
                  deviceList: deviceIds,
                  anchorList: [],
                  prologueList: getContentpriList(),
                  needReply: auto.value,
                  needTranslate: isTranslate.value,
                }
              ).then((res) => {
                hostList = []
                return
              })
            } else {
              passAnchorData(
                {
                  deviceList: deviceIds,
                  anchorList: [],
                  prologueList: getContentpriList(),
                  comment: comonList,
                  needReply: auto.value,
                  needTranslate: isTranslate.value,
                  isComment: common.value    //是否评论

                }
              ).then((res) => {
                hostList = []
                return
              })
            }

          }, 1000)

          return
        }

      } else {
        // 如果是暂停状态，则打开弹窗进行开启第一轮任务
        scheduleEnabled.value = true
        initialTextStr.value = '';
        dialogTitle.value = '主播ID';
        showDialog.value = true;
      }



    } else if (key === 'like') {

      if (!deviceId) {
        if (scheduleEnabled.value) {
          await stopAll(2000)
          setTimeout(() => {
            scheduleEnabled.value = true
            runType.value = 'like'
            deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId, comment: getContentList(), isComment: common.value }))
          }, 1000)
        } else {
          dialogTitle.value = '视频评论';
          setTimeout(() => {
            showDialog.value = true;

            initialTextStr.value = getContentListMultiline();
          }, 500)
        }

      } else {
        growAccount({ udid: deviceId, comment: getContentList(), isComment: common.value })
        return
      }




    } else if (key === 'brushLive') {
      if (!deviceId) {
        await stopAll(2000)
      } else {
        watchLiveForGrowth({ udid: deviceId })
        return
      }

      setTimeout(() => {
        scheduleEnabled.value = true
        runType.value = 'brushLive'
        deviceInformation.value.forEach((item) => watchLiveForGrowth({ udid: item.deviceId }))
      }, 1000)
      runType.value = 'brushLive'
    } else if (key === 'listen') {
      if (!deviceId) {
        await stopAll(2000)
      } else {
        monitorMessages({ udid: deviceId })
        return
      }

      setTimeout(() => {
        runType.value = 'listen'
        scheduleEnabled.value = true
        isMonitorOn.value = true
        deviceInformation.value.forEach((item) => monitorMessages({ udid: item.deviceId }))
      }, 1000)

    }
  })
}


/** 恢复：回到 scheduleState.index 对应片段，并让 startTime 回到“暂停前进度” */
function resumeAfterInterrupt() {
  if (!pauseSnapshot) return
  const { index, elapsedBeforePause } = pauseSnapshot
  scheduleState.index = index
  scheduleEnabled.value = true
  // 让当前片段已用时 = 暂停前的 elapsedBeforePause
  scheduleState.startTime = Date.now() - elapsedBeforePause
  localStorage.setItem('SCHEDULE_STATE', JSON.stringify(scheduleState))

  // 确保任务是该片段
  scheduleEnabled.value = true
  runTask(schedulePlan[scheduleState.index].key)
  pauseSnapshot = null
}

/**
 * 执行一次中断器函数
 * 该函数会尝试使用设备列表中的每个设备更改账户，并检查是否所有操作都成功
 * @returns {Promise<boolean>} 返回一个Promise，解析为布尔值，表示操作是否全部成功
 */
async function runInterrupterOnce() {
  // 定义一个异步函数，用于执行设备账户更改操作
  const promiseFn = async () => {
    // 从deviceInformation中提取设备ID，并过滤掉无效值
    const devices = (deviceInformation.value || [])
      .map(d => d?.deviceId)
      .filter(Boolean);

    // 如果没有有效设备，直接返回false
    if (!devices.length) return false;
    console.log(devices.length + '台设备换账号')
    // 为每个设备创建一个检查函数，用于尝试更改账户
    const checks = devices.map(async (udid) => {
      try {
        // 尝试更改账户并检查返回结果
        const res = await changeAccount({ udid: udid });
        // 检查返回值是否表示成功
        return (
          res.code === 200
        );
      } catch {
        // 如果出错，返回false
        return false;
      }
    });

    // 等待所有检查完成，并获取结果
    const settled = await Promise.allSettled(checks);
    // 检查是否所有操作都成功完成
    const allOk = settled.every(s => s.status === 'fulfilled' && s.value === true);
    return allOk;
  };

  // 使用超时机制执行promiseFn，捕获超时或其他错误并返回false
  return await withTimeout(promiseFn(), interruptCallTimeoutMs).catch(() => false);
}

function withTimeout(p, ms) {
  return new Promise((resolve, reject) => {
    const t = setTimeout(() => reject(new Error('timeout')), ms)
    p.then(v => { clearTimeout(t); resolve(v) })
      .catch(e => { clearTimeout(t); reject(e) })
  })
}

function startScheduleLoop() {
  lastInterruptTs = Date.now()
  localStorage.setItem('INT_LAST_TS', String(lastInterruptTs))
  // 先按当前 index 跑一次，保持“即刻对齐”
  runTask(schedulePlan[scheduleState.index].key, null, 1)
  scheduleEnabled.value = true;  // ✅ 关键：启动调度就必须开启

  if (scheduleTimer) clearInterval(scheduleTimer)

  scheduleTimer = setInterval(async () => {
    // 关总调度就什么都不做
    if (!scheduleEnabled.value) return

    // —— 先处理“打断器” —— 
    if (interruptEnabled.value && !interrupting) {
      const now = Date.now()
      if (!lastInterruptTs) lastInterruptTs = now   // 首次初始化

      const due = now - lastInterruptTs >= interruptEveryMin.value * 60_000 * 60
      // const due = now - lastInterruptTs >= interruptEveryMin.value * 60_000

      console.log(
        'due=', due,
        'elapsed=', now - lastInterruptTs,
        'threshold=', interruptEveryMin.value * 60_000 * 60
      )
      if (due) {
        interrupting = true
        try {
          // 记录暂停前的进度（用于恢复）
          const cur = schedulePlan[scheduleState.index]
          const elapsedBeforePause = now - scheduleState.startTime
          pauseSnapshot = { index: scheduleState.index, elapsedBeforePause }

          // 停掉当前片段
          await stopAll(2000)


          // 执行中断任务（带重试）
          let ok = false
          for (let i = 0; i < interruptMaxRetries; i++) {
            ok = await runInterrupterOnce()
            if (ok) break
          }

          // 无论成功与否都更新节拍，避免立刻再次触发
          lastInterruptTs = Date.now()
          localStorage.setItem('INT_LAST_TS', String(lastInterruptTs))

          // 成功：恢复暂停前的片段进度；失败：也恢复（或改成重启当前片段都可）
          resumeAfterInterrupt()
        } catch (e) {
          console.error('[Interrupter] 失败：', e)
          // 失败兜底：恢复任务
          resumeAfterInterrupt()
        } finally {
          interrupting = false
        }

        // ⚠️ 本轮只做打断，不做片段切换判断
        return
      }
    }

    // —— 正常片段轮换（你原有的逻辑） ——
    const now = Date.now()
    const cur = JSON.parse(localStorage.getItem('SCHEDULE_PLAN'))[scheduleState.index]
    const elapsed = now - scheduleState.startTime
    console.log('已调度', elapsed / 1000, '秒', cur.duration / 1000 + '秒后调度', printCurrentTime())

    if (elapsed >= cur.duration) {
      scheduleState.index = (scheduleState.index + 1) % schedulePlan.length
      scheduleState.startTime = now
      localStorage.setItem('SCHEDULE_STATE', JSON.stringify(scheduleState))
      scheduleEnabled.value = true
      runTask(schedulePlan[scheduleState.index].key)
    }
  }, scheduleTickMs)
}


function forceActivate(key, runner) {
  // 跳过互斥逻辑，直接切换
  // runType.value = key;
  if (typeof runner === 'function') runner();
}

function getTranslation(list) {
  list.forEach((item, index) => {
    translationToChinese({ msg: item.text }).then(res => {
      console.log(res);
      chatList.value[index].text = res
    })
  })
}

function onHostSaved(list) {
  console.log('保存后的主播id:', list)
}


//当前时间获取
function printCurrentTime() {
  const now = new Date();
  return now.toLocaleString()
}


function onSave(payload) {
  console.log(payload)
  aiConfig(payload).then((res) => {

  })

}


async function isAiConfig() {
  const res = await window.electronAPI.fileExists();

  console.log(res);
  return res.exists;
}

//金票
const gold = ref(true)      // ON=执行
//普票
const ordinary = ref(true)  // ON=执行


function invitTypeFun(invitType, nextEnabled) {
  // 子组件已传来“切换后的布尔值”
  if (invitType === 'gold') {
    gold.value = nextEnabled
  } else if (invitType === 'ordinary') {
    ordinary.value = nextEnabled
  }
}


/** 由邀请类型 + 当前全局开关，得到 state(0/1) */
function stateByInvType(invitationType) {
  // 2=金票，其他=普票（按你的写法）
  const enabled = invitationType == 2 ? gold.value : ordinary.value
  return enabled ? 1 : 0
}




//清空当前批次的小工具
function dropCurrentWave() {
  // 丢弃当前这“波”已缓冲的数据 & 取消待 flush
  batch.length = 0
  if (flushTimer) { clearTimeout(flushTimer); flushTimer = null }
  // 复位本波的弹窗与模式标记
  isMsgPop.value = false
}


async function doLogout() {
  try {
    dropCurrentWave()
    // es?.close?.()              // 如果 connectSSE 返回 EventSource，调用 close
    refreshAllStopImgs()       // 你已有：把所有 <img> src 清空
    clearInterval(getListTimer.value)
    getListTimer.value = null
    await logout({ userId: userdata.id, tenantId: userdata.tenantId })
  } finally {
    router.push('/')
  }
}


//查看主播库主播信息
async function getStoredHostList() {
  const v = await anchorList()
  return v ? v : []
}


let isWifi = ref(false);
const checkVPN = async () => {
  try {
    // 设置超时 5 秒钟
    const timeout = new Promise((_, reject) =>
      setTimeout(() => reject(new Error('请求超时')), 10000)  // 10秒超时
    );

    // 使用 Promise.race 来进行超时控制
    const response = await Promise.race([
      fetch('https://www.google.com', { method: 'HEAD', mode: 'no-cors' }),
      timeout
    ]);

    // 判断 fetch 请求是否成功
    if (response && response.type === 'opaque') {
      // ElMessage.success('VPN连接正常！');
      isWifi.value = false;
    } else {
      ElMessage.error('VPN连接失败，无法访问网络。');
      isWifi.value = true;
    }
  } catch (error) {
    // 捕获超时错误或其他错误
    ElMessage.error('VPN连接失败，无法访问网络。');
    isWifi.value = true;
  }
};

// 模拟翻译函数：你可以接入自己后端或第三方接口     sentences文本      targetLang语言
async function doTranslate(sentences, targetLang) {
  const str = arrayToString(sentences)
  try {
    const response = await customTranslation({
      "msg": str,
      "language": targetLang
    })
    console.log(response)


    // 1️⃣ 去掉首尾的大括号
    const raw = response.replace(/^{|}$/g, '')

    // 2️⃣ 按换行符切割为数组
    const arr = raw.split('\n').map(s => s.trim()).filter(Boolean)

    console.log(arr)
    // 简单演示逻辑（真实情况应调用 API）
    return arr
  } catch (error) {
    console.error('Translation error:', error)
    // 发生错误时返回空数组
    return []
  }
}

// 接收“确定”事件返回结果
function onConfirm({ type, strings, autoBlo, needTranslate }) {
  console.log('✅ 确认返回：', type, strings, autoBlo, needTranslate)
  auto.value = autoBlo
  isTranslate.value = needTranslate
  showtransDlg.value = false
  runType.value = 'follow'
  setContentpriList(strings)

  //过滤无网络设备
  const allIds = deviceInformation.value.map(item => item.deviceId)
  // 在线要启动的
  const deviceIds = onlineOnly(allIds)
  if (!deviceIds.length) {
    ElMessage.warning('没有在线设备可执行任务');
    // 也别忘了把离线的标成待恢复
    markPendingForOffline(allIds.filter(id => netStatus[id] === false))
    return
  }
  // ⭐ 给这次被跳过的离线设备打标记（后续来网自动补上）
  markPendingForOffline(allIds.filter(id => !deviceIds.includes(id)))

  if (isAlliance.value) {
    followAndGreetUnion(
      {
        deviceList: deviceIds,
        anchorList: hostList.map(item => ({
          anchorId: item.id,
          country: item.country,
          invitationType: item.invitationType,
          state: stateByInvType(item.invitationType),
        })),
        prologueList: strings,
        needReply: autoBlo,
        needTranslate: needTranslate,
      }
    ).then((res) => {
      ElMessage({ type: 'success', message: '任务开启成功' });

      hostList = []
    })
  } else {
    passAnchorData(
      {
        deviceList: deviceIds,
        anchorList: hostList.map(item => ({
          anchorId: item.id,
          country: item.country,
          invitationType: item.invitationType,
          state: stateByInvType(item.invitationType),
        })),
        prologueList: strings,     //私信对象
        comment: comonList,       //评论列表
        needReply: autoBlo,          //自动回复
        needTranslate: needTranslate, //翻译
        isComment: common.value    //是否评论
      }
    ).then((res) => {
      ElMessage({ type: 'success', message: '任务开启成功' });
      console.log("启动成功")
      hostList = []
    })

  }
}

function arrayToString(arr) {
  // 过滤空项并用 \n 连接
  return arr.filter(Boolean).join('  \n')
}


// 每台设备请求间隔（毫秒）
const REQ_GAP_MS = 100

async function refreshNetStatus() {
  const list = [...(deviceInformation.value || [])]
  if (!list.length) return

  const firstPass = {}

  // ✅ 串行请求：每台设备间隔 5 秒
  for (let i = 0; i < list.length; i++) {
    const d = list[i]
    try {
      const res = await getDeviceNetStatus({ udid: d.deviceId })
      firstPass[d.deviceId] = (res === true)
    } catch (err) {
      firstPass[d.deviceId] = true
    }

    // 不是最后一台才等待 5s
    if (i < list.length - 1) {
      await new Promise(r => setTimeout(r, REQ_GAP_MS))
    }
  }

  // 二次复核
  const falseIds = Object.keys(firstPass).filter(id => firstPass[id] === false)
  const confirmResults = await Promise.all(
    falseIds.map(async id => {
      const stableOffline = await confirmStableOffline(id)
      return { id, stableOffline }
    })
  )

  const effective = { ...firstPass }
  confirmResults.forEach(({ id, stableOffline }) => {
    effective[id] = !stableOffline
  })

  // 写回 UI
  Object.keys(effective).forEach(id => { netStatus[id] = effective[id] })
  console.log('设备在线状态（有效值）', effective)

  // 清理已下线设备
  const aliveIds = new Set(list.map(d => d.deviceId))
  Object.keys(netStatus).forEach(id => {
    if (!aliveIds.has(id)) {
      delete netStatus[id]
      delete lastNet[id]
      delete offlineFlags[id]
      clearResumeTimer(id)
    }
  })

  // 任务联动（保持原逻辑）
  if (runType.value) {
    list.forEach(({ deviceId: id }) => {
      const prev = lastNet[id]
      const curr = netStatus[id]
      if (typeof curr === 'undefined') return

      if (prev !== curr) {
        if (curr === false) {
          clearResumeTimer(id)
          if (!offlineFlags[id]) {
            console.log(`[NET] ${id} 稳定离线，停止该设备任务`)
            offlineFlags[id] = true
            stopOne(id)
          }
        }

        if (curr === true && offlineFlags[id]) {
          clearResumeTimer(id)
          const t = setTimeout(() => {
            if (runType.value && netStatus[id] === true) {
              console.log(`[NET] ${id} 恢复在线，重新启动当前模式: ${runType.value}`)
              scheduleEnabled.value = true
              runTask(runType.value, id)
              offlineFlags[id] = false
            }
            resumeTimers.delete(id)
          }, NET_RESUME_STABLE_MS)
          resumeTimers.set(id, t)
        }
      }

      lastNet[id] = curr
    })
  } else {
    Object.keys(offlineFlags).forEach(id => { offlineFlags[id] = false })
    Array.from(resumeTimers.keys()).forEach(id => clearResumeTimer(id))
  }
}




// ===== 新增：二次复核断网所需的小工具 =====
const verifyingOffline = {}   // { [deviceId]: Promise<boolean> } 去重并发

const RETRY_COUNT = 2         // 额外再查几次
const RETRY_GAP_MS = 1000      // 每次间隔（可调）

const sleep = (ms) => new Promise(r => setTimeout(r, ms))

async function safeGetStatus(udid) {
  try {
    return await getDeviceNetStatus({ udid }) === true
  } catch {
    return true
  }
}

/** 返回 true=稳定离线；false=误报/抖动（有一次为 true 就当没离线） */
async function confirmStableOffline(udid) {
  if (verifyingOffline[udid]) return verifyingOffline[udid]

  verifyingOffline[udid] = (async () => {
    for (let i = 0; i < RETRY_COUNT; i++) {
      await sleep(RETRY_GAP_MS)
      const ok = await safeGetStatus(udid) // true=在线
      if (ok) return false                 // 有一次在线 => 抖动
    }
    return true                             // 全部都是 false => 稳定离线
  })().finally(() => { delete verifyingOffline[udid] })

  return verifyingOffline[udid]
}

//小工具：标记离线待恢复
const markPendingForOffline = (ids) => {
  ids.forEach(id => {
    if (netStatus[id] === false) {
      offlineFlags[id] = true;   // 标记成“暂停/待恢复”
      // 可选：初始化上次状态，便于日志判断
      if (typeof lastNet[id] === 'undefined') lastNet[id] = false;
    }
  });
};

const onToggleCrawler = (val) => {
  // 🔒 想打开，但剩余为 0，直接拦截
  if (val === true && sseCrawlerRemaining.value <= 0) {
    ElMessage.warning('请先设置监听爬虫条数')
    // 立即回滚（但这是“主动控制”，不是 watch 回滚）
    sseCrawlerEnabled.value = false
    return
  }

  // 互斥逻辑
  if (val) {
    sseBossEnabled.value = false
     window.electronAPI.toggleMq('boss', false)
  }

  // 真正同步给后端 / MQ
  window.electronAPI.toggleMq('crawler', val)
}


const onToggleBoss = (val) => {
  if (val === true && sseBossRemaining.value <= 0) {
    ElMessage.warning('请先设置监听大哥条数')
    sseBossEnabled.value = false
    return
  }

  if (val) {
    sseCrawlerEnabled.value = false
    window.electronAPI.toggleMq('crawler', false)
  }

  window.electronAPI.toggleMq('boss', val)
}



function timestampToTime(timestamp_ms) {
  const date = new Date(timestamp_ms);
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, '0');
  const day = date.getDate().toString().padStart(2, '0');
  const hours = date.getHours().toString().padStart(2, '0');
  const minutes = date.getMinutes().toString().padStart(2, '0');
  const seconds = date.getSeconds().toString().padStart(2, '0');
  const milliseconds = date.getMilliseconds().toString().padStart(3, '0');

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}


// ========= 主播过滤工具：使用 getHostfilters() 的参数 =========

// 区间匹配：如果范围没配就不拦
function matchRange(val, min, max) {
  if (val == null || val === '') return true   // 没值直接放过（按需改）
  const n = Number(val)
  if (!Number.isFinite(n)) return true         // 非数字也直接放过
  if (min != null && n < min) return false
  if (max != null && n > max) return false
  return true
}

// 等级匹配：没选等级就不拦，有选择时必须包含
function matchLevel(level, selected) {
  if (!selected || !selected.length) return true
  if (!level) return false
  return selected.includes(level)
}

// 金票 / 普票过滤：true = 不过滤, false = 过滤掉
function matchTicket(invitationType, goldFlag, ordinaryFlag) {
  // invitationType 可能是字符串，统一转成数字
  const t = Number(invitationType)

  // 没配置时不限制
  const gold = goldFlag
  const ordinary = ordinaryFlag

  // t == 2 金票
  if (t === 2) {
    if (gold === false) return false   // 关闭金票 → 过滤掉
  }

  // t == 1 普票（或者其它都按普票看也行）
  if (t === 1) {
    if (ordinary === false) return false   // 关闭普票 → 过滤掉
  }

  return true
}

// 根据 filters 判断当前主播数据是否命中
function matchByHostFilters(payload) {
  const filters = getHostfilters() || {}

  const {
    min_onlineFans,
    max_onlineFans,
    hostslevel,
    gold,        // 新增
    ordinary,    // 新增
  } = filters

  const {
    onlineFans,
    level,
    invitationType,
  } = payload

  // 在线人数
  if (!matchRange(onlineFans, min_onlineFans, max_onlineFans)) return false
  // 等级
  if (!matchLevel(level, hostslevel)) return false
  // 金票 / 普票
  if (!matchTicket(invitationType, gold, ordinary)) return false

  return true
}

</script>

<style scoped lang="less">
@import '../static/css/video.less';
</style>
