<!-- 使用 type="home" 属性设置首页，其他页面不需要设置，默认为page；推荐使用json5，更强大，且允许注释 -->
<route lang="json5">
{
  style: {
    navigationBarTitleText: '打印任务列表',
  },
}
</route>
<template>
  <view class="page-list">
    <wd-sticky>
      <view class="sticky-box">
        <wd-tabs v-model="queryParams.completed" auto-line-width @change="resetDataList">
          <wd-tab v-for="item of completedList" :key="item.value" :title="item.label" :name="item.value">
          </wd-tab>
        </wd-tabs>

        <!-- <wd-search
          placeholder="请输入打印任务名称"
          placeholder-left
          hide-cancel
          v-model="queryParams.title"
          @change="onSearch"
          @clear="onSearch"
        >
          <template v-slot:prefix>
            <wd-drop-menu custom-class="search-menu">
              <wd-drop-menu-item v-model="queryParams.completed" :options="completedList" @change="onSearch" />
            </wd-drop-menu>
          </template>
        </wd-search> -->
      </view>
    </wd-sticky>

    <view class="list-contain">
      <view v-if="queryParams.printer" class="printer">{{ `当前打印机: ${queryParams.printer} ${queryParams.asname ? `(${queryParams.asname})` : ''}` }}</view>

      <template v-if="dataList.length > 0 || loadStatus == 'loading'">
        <view v-for="item of dataList" :key="item.id" class="item-contain" @click="toDetail(item)">
          <view class="item-info">
            <view class="image">
              <wd-img
                :src="getFileType(item.title)"
                :width="60"
                :height="60"
                mode="aspectFit"
              ></wd-img>
            </view>
            <view class="name">
              <view class="main-text">
                <view>{{ item.title }}</view>
              </view>
              <view class="sub-text">
                <text>任务状态: </text>
                <text :class="`color-${getLabelColor(item.jobStatus, jobStatus)}`">{{ getLabel(item.jobStatus, jobStatus) + (item.statusMsg ? `(${item.statusMsg})` : "") }}</text>
              </view>
              <view class="sub-text">
                {{ `创建时间: ${item.createTime}` }}
              </view>
              <!-- <view class="sub-text flex-center">
                <view class="flex-1">xxx</view>
                <view class="flex-1">xxx</view>
              </view> -->
            </view>
          </view>
          <view class="item-opt">
            <view v-if="item.reprint == '1'" class="opt-btn" @click.stop="handleReprint(item.id)">重打</view>
            <view v-if="['3', '4', '5', '6'].includes(item.jobStatus)" class="opt-btn" @click.stop="handleCancel(item.id)">取消</view>
            <view class="opt-btn delete-btn" @click.stop="handleDelete(item)">删除</view>
          </view>
        </view>
        <!-- 加载更多 -->
        <wd-loadmore custom-class="loadmore" :state="loadStatus" />
      </template>
      <wd-status-tip v-else image="content" tip="暂无内容" />
    </view>
  </view>
</template>

<script lang="ts" setup>
import { useMessage, useToast } from 'wot-design-uni'
import { getUserHubJobsPage, reprintUserHubJobs, cancelUserHubJobs, deleteUserHubJobs } from '@/service/api'
import { getLabel, getLabelColor } from '@/utils'
import { debounce } from 'wot-design-uni/components/common/util'
import { useUserStore } from '@/store'
import { toLoginWait } from '@/utils'

let socketBaseUrl = import.meta.env.VITE_SOCKET_BASEURL
const userStore = useUserStore()
const token = userStore.token
const message = useMessage()
const toast = useToast()
const total = ref(0)
const queryParams: any = reactive({
  pageNo: 1,
  pageSize: 10,
  completed: "all",
  title: '',
  printer: "",
  hubId: '',
  asname: '', // 打印助手名称
})
const completedList: any = ref([
  { label: '全部', value: 'all' },
  { label: '当前任务', value: '0' },
  { label: '历史任务', value: '1' }
])
//  0连接中 3 等待中 4 已暂停 5 处理中 6已停止 7已取消 8已中止 9已完成
const jobStatus: any = ref([
  { label: '连接中', value: '0', color: 'primary' },
  { label: '等待中', value: '3', color: 'info' },
  { label: '已暂停', value: '4', color: 'info' },
  { label: '处理中', value: '5', color: 'primary' },
  { label: '已停止', value: '6', color: 'danger' },
  { label: '已取消', value: '7', color: 'danger' },
  { label: '已中止', value: '8', color: 'danger' },
  { label: '已完成', value: '9', color: 'success' },
])
const dataList = ref([])
// 加载中: loading; 没有数据: finished; 错误: error;
const loadStatus: any = ref('loading')
const isToTop = ref(false)
// 文件类型
const imageSrc = '/static/images/image.png'
const docSrc = '/static/images/doc.png'
const xlsSrc = '/static/images/xls.png'
const pptSrc = '/static/images/ppt.png'
const pdfSrc = '/static/images/pdf.png'
const txtSrc = '/static/images/txt.png'
const otherSrc = '/static/images/other.png'
// websocket连接对象
const socketTask = ref(null)
// 是否正在重连
const isReconnecting = ref(false)
// 当前重连次数
const reconnectAttempts = ref(0)
// 最大重连次数
const maxReconnectAttempts = ref(5)
// 重连间隔时间
const reconnectInterval = 1000

defineOptions({
  name: 'Job',
})

// 根据文件类型获取图片
// 允许的文件类型 allowType = ['txt', 'pdf', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'pptx', 'gif', 'png', 'jpg', 'jpeg', 'webp']
function getFileType(fileName) {
  let src = otherSrc
  let fileType = ""
  if (fileName) {
    const lastIndex = fileName.lastIndexOf('.')
    if (lastIndex != -1) fileType = fileName.substring(lastIndex + 1)
    switch (fileType) {
      case "doc":
      case "docx":
        src = docSrc
        break;
      case "xls":
      case "xlsx":
        src = xlsSrc;
        break;
      case "ppt":
      case "pptx":
        src = pptSrc;
        break;
      case "png":
      case "jpg":
      case "jpeg":
      case "gif":
      case "webp":
        src = imageSrc;
        break;
      case "txt":
        src = txtSrc;
        break;
      case "pdf":
        src = pdfSrc;
        break;
    }
  }
  return src
}

// 获取列表数据
function getDataList() {
  loadStatus.value = 'loading'
  const params = { ...queryParams }
  if (params.title) {
    params.title = '*' + params.title + '*'
  } else {
    delete params.title
  }
  if (params.completed === '' || params.completed === 'all') delete params.completed
  if (!params.printer) delete params.printer
  if (!params.hubId) delete params.hubId
  getUserHubJobsPage(params)
    .then((res: any) => {
      if (res.code === 0 && res.body) {
        total.value = res.body.total
        if (queryParams.pageNo === 1) {
          dataList.value = res.body.records || []
        } else {
          dataList.value.push(...(res.body.records || []))
        }
      }
    })
    .catch((e) => {})
    .finally(() => {
      loadStatus.value = 'finished'
    })
}

const onSearch = debounce(() => {
  queryParams.pageNo = 1
  getDataList()
}, 500)

// 查看详情
function toDetail(item: any) {
  if (['0'].includes(item.jobStatus)) return
  uni.navigateTo({
    url: `/pages/print/jobDetail?id=${item.id}`,
  })
}

// 重打任务
function handleReprint(id: string) {
  reprintUserHubJobs({id}).then((res: any) => {
    if (res.code === 0) {
      toast.success('已发送重新打印')
      getDataList()
    }
  })
  .catch((error) => {
    console.log(error)
  })
}

// 取消任务
function handleCancel(id: string) {
  cancelUserHubJobs({id}).then((res: any) => {
    if (res.code === 0) {
      toast.success('取消打印任务成功')
      getDataList()
    }
  })
  .catch((error) => {
    console.log(error)
  })
}

// 删除任务
function handleDelete(item) {
  message
    .confirm({
      title: '提示',
      msg: `是否删除打印任务: ${item.title} ?`,
    })
    .then(() => {
      deleteUserHubJobs({id: item.id})
        .then((res: any) => {
          if (res.code === 0) {
            toast.success('删除成功')
            queryParams.pageNo = 1
            getDataList()
          }
        })
        .catch((error) => {
          console.log(error)
        })
    })
    .catch((error) => {
      console.log(error)
    })
}

// 查询列表, 滚动到顶部, 返回第一页
function resetDataList() {
  uni.pageScrollTo({
    scrollTop: 0,
    duration: 300,
  })
  dataList.value = []
  onSearch()
}

// 页面加载
onLoad((option) => {
  initWebSocket()
  // 指定打印机 & 助手ID
  if (option && option.printer && option.hubId) {
    queryParams.printer = option.printer
    queryParams.hubId = option.hubId
    queryParams.asname = option.asname
  }
  if (option && option.tab) {
    queryParams.completed = option.tab
  }
  getDataList()
  // 监听刷新
  uni.$on('refreshData', () => {
    resetDataList()
    isToTop.value = true
  })
})

onUnload(() => {
  closeWebSocket()
})

// 页面显示
onShow(() => {
  if (isToTop.value) {
    uni.pageScrollTo({
      scrollTop: 0,
      duration: 300,
    })
    isToTop.value = false
  }
})

// 滚动到底部
onReachBottom(() => {
  if (dataList.value.length < total.value) {
    queryParams.pageNo++
    getDataList()
  } else {
    loadStatus.value = 'finished'
  }
})

// 创建websocket连接
function initWebSocket() {
  socketTask.value = uni.connectSocket({
    url: `${socketBaseUrl}/print/ws`,
    header: {
      'token': token
    },
    success: () => {
      console.log("连接成功");
    },
    fail: (err) => {
      console.log("连接失败: ", err);
      handleReconnect()
    }
  })

  // 监听 WebSocket 连接打开事件
  socketTask.value.onOpen(() => {
    console.log('WebSocket 连接已打开');

    // 发送数据到服务器(心跳)
    // socketTask.send({
    //   data: JSON.stringify({ message: 'Hello, Server!' }),
    //   success: () => {
    //     console.log('消息发送成功');
    //   },
    //   fail: (err) => {
    //     console.error('消息发送失败', err);
    //   }
    // });
  })

  // 监听 WebSocket 接收到消息事件
  socketTask.value.onMessage((res) => {
    console.log('Received message:', res.data);

    // 处理接收到的数据
    const lines = res.data.split(/\r?\n|\r/).filter(line => line.trim() !== '');
    const message = lines.reduce((obj, item) => {
      const [key, value] = item.split(/:(.*)/);
      obj[key] = key == "data" ? JSON.parse(value) : value;
      return obj;
    }, {});
    console.log('解析后的消息:', message);

    handleWebsocketMessage(message)
  })

  // 监听 WebSocket 错误事件
  socketTask.value.onError((err) => {
    console.error('WebSocket 发生错误', err);
  });

  // 监听 WebSocket 连接关闭事件 (线上存在一分钟就断开重连的问题)
  socketTask.value.onClose((res) => {
    console.log('WebSocket 连接已关闭:', res); // 异常断开 {code: 1006, reason: "abnormal closure"}
    if (res.code !== 1000) handleReconnect()
  });
}

// 断开websocket连接 
function closeWebSocket() {
  if (socketTask.value) {
    socketTask.value.close({
      success: () => {
        console.log('断开 WebSocket 连接成功');
      },
      fail: (err) => {
        console.error('断开 WebSocket 连接失败', err);
      },
    });
    socketTask.value = null
  }
}

// 处理重连机制
function handleReconnect() {
    console.log('reconnectAttempts.value: ', reconnectAttempts.value);
  if (isReconnecting.value || reconnectAttempts.value >= maxReconnectAttempts.value) {
    console.log('已达到最大重连次数，停止重连');
    return;
  }

  isReconnecting.value = true
  reconnectAttempts.value = reconnectAttempts.value++
  
  setTimeout(() => {
    initWebSocket();
    isReconnecting.value = false;
  }, reconnectInterval)
}

// 处理Websocket消息
function handleWebsocketMessage(message: any) {
  switch (message.event) {
    case "job-update":
      handleUpdate(message.data)
      break;
    case "not-verify":
      toast.warning('token失效, 请重新登录')
      toLoginWait(1500)
      break;
  }
}

// 处理更新数据
function handleUpdate(data) {
  if (data.id && dataList.value.findIndex(i => i.id == data.id) !== -1) {
    // 更新状态
    let target: any = dataList.value.find(i => i.id == data.id)
    target.jobStatus = String(data.jobStatus)
    target.statusMsg = data.statusMsg
    // 通知
    switch (String(data.jobStatus)) {
      case "9":
        toast.success(`打印完成: ${data.title}`)
        break;
      case "6":
        toast.error(`打印错误: ${data.title}`)
        break;
      default:
        toast.info(`打印信息: ${data.title} ${getLabel(data.jobStatus, jobStatus.value)}`)
        break; 
    }
  }
}
</script>

<style lang="scss" scoped>
.sticky-box {
  width: 100vw;
  height: 100rpx;
  background-color: #ffffff;
}
.search-type {
  position: relative;
  height: 60rpx;
  padding: 0 16rpx 0 32rpx;
  line-height: 60rpx;
}
.search-type::after {
  position: absolute;
  top: 10rpx;
  right: 0;
  bottom: 10rpx;
  width: 2rpx;
  content: '';
  background: rgba(0, 0, 0, 0.25);
}
.search-type {
  :deep(.icon-arrow) {
    display: inline-block;
    font-size: 40rpx;
    vertical-align: middle;
  }
}
.printer {
  margin-bottom: 20rpx;
}
.list-contain {
  padding: 10rpx 30rpx 30rpx;
  overflow-y: auto;

  .item-contain {
    padding: 30rpx;
    margin-bottom: 30rpx;
    border: 2rpx solid #eee;

    .item-info {
      display: flex;
      align-items: flex-start;
      .image {
        display: flex;
        padding-right: 14rpx;
      }
      .name {
        flex: 1;
        .main-text {
          display: flex;
          align-items: center;
          word-break: break-all;
        }
        .sub-text {
          margin-top: 14rpx;
          font-size: 28rpx;
          color: #00000073;
        }
      }
      .opt {
        width: 80rpx;
        text-align: center;
      }
    }

    .item-opt {
      margin-top: 20rpx;
      border-top: 2rpx solid #eee;
      padding-top: 20rpx;
      text-align: right;
      font-size: 28rpx;
      
      .opt-btn {
        display: inline-block;
        padding: 4rpx 20rpx;
        margin-left: 20rpx;
        color: var(--wot-color-theme);
        border: 2rpx solid var(--wot-color-theme);
        border-radius: 8rpx;
      }
      .opt-btn:first-of-type {
        margin-left: 0;
      }
      .delete-btn {
        color: #fa4350;
        border-color: #fa4350;
      }
    }
  }
}
//搜索下拉菜单
:deep(.search-menu .wd-drop-menu__list) {
  background: none;
}
:deep(.search-menu .wd-drop-menu__item) {
  height: 62rpx;
  line-height: 62rpx;
}
:deep(.search-menu .wd-drop-menu__item-title::after) {
  content: none;
}
:deep(.wd-message-box__content) {
  word-break: break-all;
}
</style>
