<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>
          <!-- 新增：SSE 弹窗总开关 -->
          <el-switch v-model="sseEnabled" inline-prompt active-text="监听爬虫" inactive-text="监听爬虫"
            style="margin-left: 8px;" />
        </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>
    <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, onBeforeUnmount, watch, inject, computed, nextTick } from "vue";
import { useRouter } from 'vue-router';
import {
  setphoneXYinfo, getphoneXYinfo, getUser,
  getHostList, setHostList, getContentpriList,
  setContentpriList, getContentList, setContentList,
  setsessionId, getsessionId, getContentListMultiline, getContentpriListMultiline
} 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 { pickTikTokBundleId } from '@/utils/arrUtils'
import { logout, updates, health } from '@/api/account';
import {
  getDeviceList,
  toHome,
  swipeAction,
  tapAction,
  growAccount,
  stopScript,
  watchLiveForGrowth,
  monitorMessages,
  passAnchorData,
  followAndGreetUnion,
  addTempAnchorData,
  deviceAppList,
  launchApp,
  getChatTextInfo,
  setLoginInfo,
  aiConfig,
  selectLast,
  updatelast,
  changeAccount,
  stopAllTask,
  anchorList,
  restartTikTok,
  getDeviceNetStatus
} from '@/api/ios';
import ding from '@/assets/mes.wav'
import { set } from "lodash";
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 getListtimer = null;
let getNetworkListtimer = null;
let userdata = getUser();
let chatList = ref([])

let MesNewList = ref([])

// 刷新方法
const reloadImg = () => {
  refreshAllImgs()
}
//start弹窗
let isMsgPop = ref(false)

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

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


// 每台设备的网络状态：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 sseEnabled = ref(JSON.parse(localStorage.getItem('SSE_ENABLED') ?? 'true'))
watch(sseEnabled, v => {
  localStorage.setItem('SSE_ENABLED', JSON.stringify(v))
  if (!v) dropCurrentWave()   // 关掉总开关时，立刻丢弃本波缓冲并取消待flush
})

// 互斥按钮的样式：激活=红，锁定=半透明且禁点
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: () => reloadImg(),
    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 = '视频评论';
      setTimeout(() => {
        showDialog.value = true;

        initialTextStr.value = getContentListMultiline();
      }, 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('已保存定时调度')
}

const selectedDevice = ref(null)


// 每台设备的 <img> 引用
const imgRefs = ref({})              // { [id]: HTMLImageElement }

// 新增：每台设备当前展示的 URL
const imgSrcMap = reactive({}) // { [deviceId]: string }

// 新增：每台设备循环状态（timer/abort 等）
const loops = new Map(); // deviceId -> { timer, stopped, lastUrl }

/** 生成一次地址 */
const makeUrl = (port) => `http://localhost:${port}/?t=${Date.now()}`;
// const makeUrl = (port) => `http://192.168.1.209:${port}/?t=${Date.now()}`;

/** 真正断开某台设备当前连接并清理 */
function hardCloseImg(deviceId) {
  const el = imgRefs.value[deviceId];
  if (el) {
    el.src = '';
    el.removeAttribute('src');
  }
  // 如果你用的是 blob/objectURL，这里应该 revokeObjectURL；我们当前直接 URL，不需要。
  imgSrcMap[deviceId] = '';
}

/** 只启动某台设备的 3 秒循环（第 idx 台错峰 idx*200ms） */
function startLoop(dev, idx = 0) {
  stopLoop(dev.deviceId);            // 防止重复开
  const state = { timer: null, stopped: false, lastUrl: '' };
  loops.set(dev.deviceId, state);

  const tick = () => {
    if (state.stopped) return;

    // 1) 先把老连接硬断开
    // hardCloseImg(dev.deviceId);

    // 2) 立刻换新地址
    const url = makeUrl(dev.screenPort);
    imgSrcMap[dev.deviceId] = url;

    // 3) 3 秒后再来一轮（串行，不并发）
    state.timer = window.setTimeout(tick, 3000);
  };

  // 错峰启动，避免 N 台同时一口气重连
  state.timer = window.setTimeout(tick, idx * 200);
}

/** 停止某台设备的循环并断开连接 */
function stopLoop(deviceId) {
  const s = loops.get(deviceId);
  if (!s) {
    hardCloseImg(deviceId); // 也确保断一次
    return;
  }
  s.stopped = true;
  if (s.timer) clearTimeout(s.timer);
  loops.delete(deviceId);
  hardCloseImg(deviceId);
}


// —— 设备列表变化时，同步循环 ——
// 注意：你的 deviceInformation 每 3 秒会被重新赋值。
// 这里不每次都重启，而是只做“增删对齐”，避免不必要中断。
watch(deviceInformation, (list) => {
  reconcileLoopsByDevices(list || []);
}, { deep: true });


/** 批量控制：根据 deviceInformation 启停循环（新增启动，移除停止） */
function reconcileLoopsByDevices(list) {
  const keep = new Set(list.map(d => d.deviceId));

  // 停掉已不存在的设备
  for (const id of Array.from(loops.keys())) {
    if (!keep.has(id)) stopLoop(id);
  }
  // 为新增设备启动循环（带错峰）
  list.forEach((d, i) => {
    if (!loops.has(d.deviceId)) startLoop(d, i);
  });
}// 强制重建 <img>

// 如果你还有“手动刷新”按钮，改成只刷新当前/所有设备的一轮
function refreshOneImg(deviceId) {
  // 立即强制进入下一轮：先停后启
  const dev = deviceInformation.value.find(d => d.deviceId === deviceId);
  if (dev) {
    stopLoop(deviceId);
    startLoop(dev, 0);
  }
}
function refreshAllImgs() {
  deviceInformation.value.forEach((d, i) => {
    stopLoop(d.deviceId);
    startLoop(d, i);
  });
}

// —— 关键：登出/离开时“批量硬中断”所有图片流 —— 
async function hardStopAllImgStreams(id, port) {
  const el = imgRefs.value[id]
  console.log("终止", id)

  if (el) {
    // 硬中断旧请求
    el.src = ''
    el.removeAttribute('src')
  }

}




function refreshAllStopImgs() {
  Object.keys(imgRefs.value).forEach(id => hardStopAllImgStreams(id))
}


// —— 显示尺寸固定为 320x720；未选中缩略为 THUMB_SCALE 倍 ——
// 尺寸与排布
const BASE_W = 320
const BASE_H = 720
const THUMB_SCALE = 0.6
const PER_ROW = 3

// 底行上移的位移量：720*(1-0.6)=288
const BOTTOM_SHIFT = Math.round(BASE_H * (1 - THUMB_SCALE)) // 288

// 是否至少有两行
const hasTwoRows = computed(() => deviceInformation.value.length > PER_ROW)

// 真正的“底行”判定：必须有两行以上才成立
const isBottomRow = (index) => {
  if (!hasTwoRows.value) return false
  const lastRow = Math.floor((deviceInformation.value.length - 1) / PER_ROW)
  return Math.floor(index / PER_ROW) === lastRow
}

// 统一给 .video-canvas 返回 transform（缩略/放大/底行上移）
function getCanvasStyle(index) {
  const isSelected = selectedDevice.value === index
  if (!isSelected) {
    return { transform: `scale(${THUMB_SCALE})` }
  }
  // 选中：默认正常放大；若在底行且至少两行 -> 先上移再放大
  return isBottomRow(index)
    ? { transform: `translateY(-${BOTTOM_SHIFT}px) scale(1)` }
    : { transform: 'scale(1)' }
}
// 当前选中的卡片：选中=1倍，未选中=缩略比例
const imgWH = (index) => {
  const scale = (selectedDevice.value === index) ? 1 : THUMB_SCALE
  return {
    width: `${BASE_W * scale}px`,
    height: `${BASE_H * scale}px`,
    transition: 'all 0.3s ease',
  }
}

// 计算某索引当前展示宽高（用于坐标换算）
const displaySize = (index) => {
  const scale = (selectedDevice.value === index) ? 1 : THUMB_SCALE
  return { w: BASE_W * scale, h: BASE_H * scale }
}

// 从 Canvas offset 坐标 → 真实手机分辨率坐标
const mapToDeviceXY = (index, offsetX, offsetY) => {
  const dev = deviceInformation.value[index] || {}
  const realW = Number(dev.width) || BASE_W   // 后端返回的真实分辨率
  const realH = Number(dev.height) || BASE_H
  const rotation = Number(dev.rotation || 0)   // 若后端有提供旋转角，可用 0/90/180/270

  const { w: dispW, h: dispH } = displaySize(index)

  // 归一化到 0~1
  let nx = Math.min(Math.max(offsetX / dispW, 0), 1)
  let ny = Math.min(Math.max(offsetY / dispH, 0), 1)

  // 处理旋转（如果你的服务器坐标基于设备原生朝向）
  // 0: 直接映射；90: 顺时针；180、270 同理
  let x, y
  switch (rotation % 360) {
    case 90:
    case -270:
      x = Math.round(ny * realW)
      y = Math.round((1 - nx) * realH)
      break
    case 180:
    case -180:
      x = Math.round((1 - nx) * realW)
      y = Math.round((1 - ny) * realH)
      break
    case 270:
    case -90:
      x = Math.round((1 - ny) * realW)
      y = Math.round(nx * realH)
      break
    default: // 0°
      x = Math.round(nx * realW)
      y = Math.round(ny * realH)
  }
  return { x, y }
}

// 选中：恢复到 320x720 并显示盖层
const selectDevice = (index) => {
  selectedDevice.value = index
}

// ——— 鼠标交互：按下/移动/抬起 ———
const dragState = ref({}) // 以 index 作为 key 保存 {ox, oy, t}

const onCanvasDown = (udid, e, index) => {
  // 记录起点（Canvas 内 offset）和时间
  const startDev = mapToDeviceXY(index, e.offsetX, e.offsetY) // 也记录“设备坐标”起点，便于直接打印
  dragState.value[index] = {
    ox: e.offsetX,
    oy: e.offsetY,
    t: Date.now(),
    udid,
    startDevXY: startDev,
    startOffsetXY: { x: e.offsetX, y: e.offsetY }
  }
}

const onCanvasMove = (udid, e, index) => {
  // 若要实时观察滑动轨迹（可选）
  const st = dragState.value[index]
  if (!st) return
  const curDev = mapToDeviceXY(index, e.offsetX, e.offsetY)
  // 建议：调试阶段打开，稳定后可注释或做节流
  // console.log('[MOVE]', {
  //   udid,
  //   offsetXY: { x: e.offsetX, y: e.offsetY },
  //   deviceXY: curDev,
  //   elapsedMs: Date.now() - st.t
  // })
}

const onCanvasUp = async (udid, e, index) => {
  const st = dragState.value[index]
  if (!st) return

  const { ox, oy, t, startDevXY, startOffsetXY } = st
  const dx = e.offsetX - ox
  const dy = e.offsetY - oy
  const elapsed = Date.now() - t
  delete dragState.value[index]

  // 终点（设备坐标 & 画布 offset）
  const endDevXY = mapToDeviceXY(index, e.offsetX, e.offsetY)
  const endOffsetXY = { x: e.offsetX, y: e.offsetY }

  // ✅ 这里打印：起点/终点（两套坐标）+ 耗时
  console.log('[鼠标滑动，起点/终点）+ 耗时]', {
    udid,
    start: {
      offsetXY: startOffsetXY,      // 画布内起点
      deviceXY: startDevXY          // 设备坐标起点
    },
    end: {
      offsetXY: endOffsetXY,        // 画布内终点
      deviceXY: endDevXY            // 设备坐标终点
    },
    deltaOffset: { dx, dy },
    durationMs: elapsed,
  })

  // === 你原有逻辑：tap or swipe ===
  const MOVE_THR = 5
  const isTap = Math.hypot(dx, dy) < MOVE_THR && elapsed < 500

  try {
    if (isTap) {
      await tapAction({ udid, x: endDevXY.x, y: endDevXY.y })
    } else {
      //通过自定义滑动坐标和时间传参
      await swipeAction({ udid, sx: startDevXY.x, sy: startDevXY.y, ex: endDevXY.x, ey: endDevXY.y, duration: elapsed / 1000 })
    }
  } catch (err) {
    console.error(err)
  }
}

/** 方向码：1=上, 2=左, 3=下, 4=右（不返回0，始终给出一个方向） */
function getSwipeCode(dx, dy) {
  // 哪个轴位移更大就取哪个轴；边界≈45°
  if (Math.abs(dx) >= Math.abs(dy)) {
    return dx < 0 ? 2 : 4  // 左/右
  } else {
    return dy < 0 ? 1 : 3  // 上/下（DOM坐标里向上是负）
  }
}

/** 带设备旋转(0/90/180/270)：先把画布向量(dx,dy)旋回设备坐标系再判方向 */
function getSwipeCodeWithRotation(dx, dy, rotation = 0) {
  let dxD = dx, dyD = dy
  switch ((rotation % 360 + 360) % 360) {
    case 90: dxD = dy; dyD = -dx; break
    case 180: dxD = -dx; dyD = -dy; break
    case 270: dxD = -dy; dyD = dx; break
    default: break
  }
  return getSwipeCode(dxD, dyD)
}



async function openTk() {
  if (!deviceInformation.value?.length) {
    ElMessage.warning('暂无在线设备')
    return
  }

  const loading = ElLoading.service({ text: '正在打开 TikTok …', background: 'rgba(0,0,0,.35)' })
  const results = []

  try {
    // 为了稳妥，逐台串行（如果你希望更快，可改 Promise.all 并注意并发数）
    for (const dev of deviceInformation.value) {
      const udid = dev.deviceId
      try {
        const apps = await deviceAppList({ udid }) // 期望返回示例中的数组
        const bundleId = pickTikTokBundleId(apps)

        if (!bundleId) {
          results.push({ udid, ok: false, msg: '未找到 TikTok' })
          continue
        }

        await launchApp({ udid, bundleId })
        results.push({ udid, ok: true, msg: `已启动 TikTok (${bundleId})` })
      } catch (e) {
        console.error('openTk error', udid, e)
        results.push({ udid, ok: false, msg: '请求失败' })
      }
    }
  } finally {
    loading.close()
  }

  // 汇总提示（成功/失败各一条）
  const okCount = results.filter(r => r.ok).length
  const fail = results.filter(r => !r.ok)
  if (okCount) ElMessage.success(`已在 ${okCount} 台设备启动 TikTok`)
  if (fail.length) {
    const udids = fail.map(f => f.udid).join(', ')
    ElMessage.error(`以下设备未能启动：${udids}`)
  }
}

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(`未检测到设备`)

  }
  //MQ链接
  window.electronAPI.startMq(userdata.tenantId, userdata.id)


  // 初始化时获取设备列表
  //每3秒获取一次设备列表 消息列表 和 查询主播列表是否还有主播
  getListtimer = 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)
        getListtimer = null
        //关闭获取网络状态的方法
        clearInterval(getNetworkListtimer)
        getNetworkListtimer = null
      }


    })
  }, 1000 * 60 * 3)


  if (!await isAiConfig()) {
    showMyInfo.value = true
  }
  reconcileLoopsByDevices(deviceInformation.value || []);

  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);
  }

  // —— SSE 接收 —— 
  const es = connectSSE('http://localhost:3312/events', (data) => {
    // console.log('来自服务端:', data);
    // console.log(1)
    //总开关
    if (!sseEnabled.value) return

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

    // 非 start：正常入缓冲 → 防抖批量 addTempAnchorData
    const country = data && data.country != null ? data.country : data.region
    const text = data && (data.hostsId != null ? data.hostsId : data.displayId)
    const invitationType = data && (data.invitationType != null ? data.invitationType : '')
    const id = data && data.id != null ? data.id : ''
    if (!text) return
    // console.log(text)
    batch.push({ country, text, invitationType, id })

    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)
  getListtimer = null
  //关闭获取网络状态的方法
  clearInterval(getNetworkListtimer)
  getNetworkListtimer = null

})

let isStartLac = false
const getDeviceListFun = () => {
  getDeviceList().then((res) => {
    if (res && res.length > 0 && deviceInformation.value.length !== res.length) {
      console.log("设备变更")
      deviceInformation.value = res
    }

    if (res.length == 0) {
      deviceInformation.value = []
    }
  }).catch((err) => {
    if (isStartLac) {
      ElMessage.error(`IOSAI服务错误`)
      // isStartLac = true
    } else {

    }

  })
}

//获取新消息
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(() => {
            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) {
        await stopAll(2000)
      } else {
        growAccount({ udid: deviceId, comment: getContentList(), isComment: common.value })
        return
      }

      setTimeout(() => {
        scheduleEnabled.value = true
        runType.value = 'like'
        deviceInformation.value.forEach((item) => growAccount({ udid: item.deviceId, comment: getContentList(), isComment: common.value }))
      }, 1000)

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

    }
  })
}

async function stopCurrentMode() {
  // 如果你希望“只停当前片段的设备”，也可以用 stopScript 针对设备循环
  await stopAll(2000)
}

/** 恢复：回到 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)

  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

      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)
    getListtimer = null
    await logout({ userId: userdata.id, tenantId: userdata.tenantId })
  } finally {
    router.push('/')
  }
}
function stopOne(deviceId) {

  stopScript({ udid: deviceId }).then((res) => {
    // ElMessage.success('停止成功');

  })
}

//查看主播库主播信息
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 = 5000

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] = false
    }

    // 不是最后一台才等待 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 false
  }
}

/** 返回 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;
    }
  });
};
</script>

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