<script setup lang="ts">
import { onBeforeUnmount, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal } from 'ant-design-vue/es';
import DissectionTree from '../tshark/components/DissectionTree.vue';
import DissectionDump from '../tshark/components/DissectionDump.vue';
import PacketTable from '../tshark/components/PacketTable.vue';
import { usePCAP, NO_SELECTION } from '../tshark/hooks/usePCAP';
import {
  RESULT_CODE_ERROR,
  RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { filePullTask } from '@/api/trace/task';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import useI18n from '@/hooks/useI18n';
import useTabsStore from '@/store/modules/tabs';
import saveAs from 'file-saver';
const route = useRoute();
const router = useRouter();
const tabsStore = useTabsStore();
const ws = new WS();
const { t } = useI18n();
const {
  state,
  handleSelectedTreeEntry,
  handleSelectedFindSelection,
  handleSelectedFrame,
  handleScrollBottom,
  handleFilterFrames,
  handleLoadFile,
} = usePCAP();

/**跟踪编号 */
const traceId = ref<string>(route.query.traceId as string);

/**关闭跳转 */
function fnClose() {
  const to = tabsStore.tabClose(route.path);
  if (to) {
    router.push(to);
  } else {
    router.back();
  }
}

/**下载触发等待 */
let downLoading = ref<boolean>(false);

/**信息文件下载 */
function fnDownloadFile() {
  if (downLoading.value) return;
  const fileName = `trace_${traceId.value}.pcap`;
  Modal.confirm({
    title: t('common.tipTitle'),
    content: t('views.logManage.neFile.downTip', { fileName }),
    onOk() {
      downLoading.value = true;
      const hide = message.loading(t('common.loading'), 0);
      filePullTask(traceId.value)
        .then(res => {
          if (res.code === RESULT_CODE_SUCCESS) {
            message.success({
              content: t('common.msgSuccess', {
                msg: t('common.downloadText'),
              }),
              duration: 2,
            });
            saveAs(res.data, `${fileName}`);
          } else {
            message.error({
              content: t('views.logManage.neFile.downTipErr'),
              duration: 2,
            });
          }
        })
        .finally(() => {
          hide();
          downLoading.value = false;
        });
    },
  });
}

/**获取PCAP文件 */
function fnFilePCAP() {
  filePullTask(traceId.value).then(res => {
    if (res.code === RESULT_CODE_SUCCESS) {
      handleLoadFile(res.data);
    }
  });
}

/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
  const { code, requestId, data } = res;
  if (code === RESULT_CODE_ERROR) {
    console.warn(res.msg);
    return;
  }

  // 建联时发送请求
  if (!requestId && data.clientId) {
    fnFilePCAP();
    return;
  }

  // 订阅组信息
  if (!data?.groupId) {
    return;
  }
  if (data.groupId === `2_${traceId.value}`) {
    fnFilePCAP();
  }
}

/**建立WS连接 */
function fnWS() {
  const options: OptionsType = {
    url: '/ws',
    params: {
      /**订阅通道组
       *
       * 跟踪任务PCAP文件 (GroupID:2_traceId)
       */
      subGroupID: `2_${traceId.value}`,
    },
    onmessage: wsMessage,
    onerror: (ev: any) => {
      // 接收数据后回调
      console.error(ev);
    },
  };
  //建立连接
  ws.connect(options);
}

watch(
  () => state.initialized,
  v => {
    v && fnWS();
  }
);

onBeforeUnmount(() => {
  ws.close();
});
</script>

<template>
  <PageContainer>
    <a-card
      :bordered="false"
      :loading="!state.initialized"
      :body-style="{ padding: '12px' }"
    >
      <div class="toolbar">
        <a-space :size="8" class="toolbar-oper">
          <a-button type="default" @click.prevent="fnClose()">
            <template #icon><CloseOutlined /></template>
            {{ t('common.close') }}
          </a-button>
          <a-button
            type="primary"
            :loading="downLoading"
            @click.prevent="fnDownloadFile()"
          >
            <template #icon><DownloadOutlined /></template>
            {{ t('common.downloadText') }}
          </a-button>
          <span>
            {{ t('views.traceManage.task.traceId') }}:&nbsp;
            <strong>{{ traceId }}</strong>
          </span>
        </a-space>

        <div class="toolbar-info">
          <a-tag color="green" v-show="!!state.currentFilter">
            {{ state.currentFilter }}
          </a-tag>
          <span> Matched Frame: {{ state.totalFrames }} </span>
        </div>

        <!-- 包信息 -->
        <a-popover
          trigger="click"
          placement="bottomLeft"
          v-if="state.summary.filename"
        >
          <template #content>
            <div class="summary">
              <div class="summary-item">
                <span>Type:</span>
                <span>{{ state.summary.file_type }}</span>
              </div>
              <div class="summary-item">
                <span>Encapsulation:</span>
                <span>{{ state.summary.file_encap_type }}</span>
              </div>
              <div class="summary-item">
                <span>Packets:</span>
                <span>{{ state.summary.packet_count }}</span>
              </div>
              <div class="summary-item">
                <span>Duration:</span>
                <span>{{ Math.round(state.summary.elapsed_time) }}s</span>
              </div>
            </div>
          </template>
          <InfoCircleOutlined />
        </a-popover>
      </div>

      <!-- 包数据表过滤 -->
      <a-input-group compact>
        <a-input
          v-model:value="state.filter"
          placeholder="display filter, example: tcp"
          :allow-clear="true"
          style="width: calc(100% - 100px)"
          @pressEnter="handleFilterFrames"
        >
          <template #prefix>
            <FilterOutlined />
          </template>
        </a-input>
        <a-button
          type="primary"
          html-type="submit"
          style="width: 100px"
          @click="handleFilterFrames"
        >
          Filter
        </a-button>
      </a-input-group>
      <a-alert
        :message="state.filterError"
        type="error"
        v-if="state.filterError != null"
      />

      <!-- 包数据表 -->
      <PacketTable
        :columns="state.columns"
        :data="state.packetFrames"
        :selectedFrame="state.selectedFrame"
        :onSelectedFrame="handleSelectedFrame"
        :onScrollBottom="handleScrollBottom"
      ></PacketTable>

      <a-row>
        <a-col :lg="12" :md="12" :xs="24" class="tree">
          <!-- 帧数据 -->
          <DissectionTree
            id="root"
            :select="handleSelectedTreeEntry"
            :selected="state.selectedTreeEntry"
            :tree="state.selectedPacket.tree"
          />
        </a-col>
        <a-col :lg="12" :md="12" :xs="24" class="dump">
          <!-- 报文数据 -->
          <a-tabs
            v-model:activeKey="state.selectedDataSourceIndex"
            :tab-bar-gutter="16"
            :tab-bar-style="{ marginBottom: '8px' }"
          >
            <a-tab-pane
              :key="idx"
              :tab="v.name"
              v-for="(v, idx) in state.selectedPacket.data_sources"
              style="overflow: auto"
            >
              <DissectionDump
                :base64="v.data"
                :select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
                :selected="
                  idx === state.selectedTreeEntry.idx
                    ? state.selectedTreeEntry
                    : NO_SELECTION
                "
              />
            </a-tab-pane>
          </a-tabs>
        </a-col>
      </a-row>
    </a-card>
  </PageContainer>
</template>

<style scoped>
.toolbar {
  display: flex;
  align-items: center;
  margin-bottom: 12px;
}
.toolbar-info {
  flex: 1;
  text-align: right;
  padding-right: 8px;
}

.summary {
  display: flex;
  flex-direction: column;
}
.summary-item > span:first-child {
  font-weight: 600;
  margin-right: 6px;
}

.tree {
  font-size: 0.8125rem;
  line-height: 1.5rem;
  padding-bottom: 0.75rem;
  padding-top: 0.75rem;
  white-space: nowrap;
  overflow-y: auto;
  user-select: none;
  height: 100%;
}
.tree > ul.tree {
  min-height: 15rem;
}

.dump {
  padding-bottom: 0.75rem;
  padding-top: 0.75rem;
  overflow-y: auto;
  height: 100%;
}
.dump .ant-tabs-tabpane {
  min-height: calc(15rem - 56px);
}
</style>
