<template>
  <div class="detail-container common-layout" id="migrationDetail">
    <div class="mainDetail">
      <div class="leftContent">
        <div class="top-content basic-title">
          <img src="@/assets/images/list.png" width="40" height="40" />
          <div class="title-right">
            <TextTooltip class="name-text" :content="subTaskId"></TextTooltip>
            <el-tag v-if="execSubStatusMap(subTaskInfo.execStatus, subTaskInfo.isAutoFinish)"
                    :type="statusColorMap(subTaskInfo?.execStatus, subTaskInfo?.isAutoFinish) || ''" class="status-tag">{{
                execSubStatusMap(subTaskInfo.execStatus, subTaskInfo.isAutoFinish) }}
            </el-tag>
          </div>
        </div>
        <div class="bottom-content basic-info">
          <div class="info-title">{{ $t('components.SubTaskDetail.baseInfo') }}</div>
          <div class="basicItem" :key="item.label" v-for="(item) in descData" style="margin-bottom:16px">
            <TextTooltip class="basicLable" :content="item.label"></TextTooltip>
            <TextTooltip class="basicValue" :content="item.value"></TextTooltip>
          </div>
        </div>
      </div>
      <div class="rightContent">
        <div class="switchUpdate">
          <div class="switchDesc">
            {{ switchRefreshText }}
          </div>

          <el-switch v-model="autoRefresh" :active-value="true" :inactive-value="false" size="small"></el-switch>
        </div>
        <div class="row-content">
          <el-tabs stretch v-model="currentTab">
            <!-- with lazy attribute, this card would not render until first click,-->
            <el-tab-pane :label="t('components.SubTaskDetail.migrationProcess')" name="migrationProcess" lazy>
              <MigrationProcess :subTaskMode="subTaskMode" :subTaskDbType="subTaskDbType" v-if="currentTab === 'migrationProcess'"
                                :processObj="processObj" :subTaskStep="subTaskStep" :fullProcessCount="fullProcessCount">
              </MigrationProcess>
            </el-tab-pane>
            <el-tab-pane :label="t('components.SubTaskDetail.abnormalAlarms')" name="errorAlert" lazy>
              <template #label>
                <div>{{ t('components.SubTaskDetail.abnormalAlarms') }}
                  <el-tag type="round" size="small" class="errorAlertCount">{{ phaseNums.total || 0 }}</el-tag>
                </div>
              </template>
              <ErrorAlert v-if="currentTab === 'errorAlert'" :phaseNums="phaseNums" :sourceDbType="sourceDbType"/>
            </el-tab-pane>
            <el-tab-pane :label="t('components.SubTaskDetail.log')" name="log" lazy>
              <MigrationLog />
            </el-tab-pane>
          </el-tabs>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount, h, reactive, computed, provide } from 'vue';
import {
  subTaskInfoDetail,
  subTaskBasicInfo,
  getTotalAlarmNum,
  refreshStatus,
} from '@/api/detail';
import TextTooltip from "@/components/textTooltip/index.vue";
import { useI18n } from 'vue-i18n';
import { MIGRATION_MODE, SUB_TASK_STATUS } from '@/utils/constants';
import MigrationProcess from './migrationProcess/index.vue';
import ErrorAlert from './errorAlert';
import MigrationLog from './migrationLogs.vue';
import { useSubTaskStore } from '@/store';
import Socket from '@/utils/websocket'
import { JDBCType } from "@/types/jdbc";
const { t } = useI18n();
// The various states included in the migration progress
const stepSet0 = new Set([SUB_TASK_STATUS.NOT_RUN, SUB_TASK_STATUS.CHECK_FAILED]);
const stepSet1 = new Set([SUB_TASK_STATUS.FULL_START, SUB_TASK_STATUS.FULL_RUNNING, SUB_TASK_STATUS.FULL_FINISH, SUB_TASK_STATUS.REVERSE_CONNECT_ERROR])
const stepSet2 = new Set([SUB_TASK_STATUS.FULL_CHECK_START, SUB_TASK_STATUS.FULL_CHECKING, SUB_TASK_STATUS.FULL_CHECK_FINISH, SUB_TASK_STATUS.CHECK_FAILED])
const stepSet3 = new Set([SUB_TASK_STATUS.INCREMENTAL_START, SUB_TASK_STATUS.INCREMENTAL_RUNNING, SUB_TASK_STATUS.INCREMENTAL_FINISHED, SUB_TASK_STATUS.INCREMENTAL_STOPPED, SUB_TASK_STATUS.INCREMENTAL_CONNECT_ERROR])
const stepSet4 = new Set([SUB_TASK_STATUS.REVERSE_START, SUB_TASK_STATUS.REVERSE_RUNNING, SUB_TASK_STATUS.REVERSE_STOP, SUB_TASK_STATUS.REVERSE_CONNECT_ERROR])
const stepSet5 = new Set([SUB_TASK_STATUS.MIGRATION_FINISH]);
// Here for states of 100, 500, 1000, 2000, another field is required
const stepJudge = new Set([SUB_TASK_STATUS.MIGRATION_FINISH, SUB_TASK_STATUS.MIGRATION_ERROR, SUB_TASK_STATUS.WAIT_RESOURCE, SUB_TASK_STATUS.INSTALL_PORTAL])

const subTaskStore = useSubTaskStore();
const subTaskStep = ref(1);
const subTaskMode = ref(2);
const subTaskDbType = ref('')
const props = defineProps({
  open: Boolean,
  taskInfo: Object,
  subTaskId: [String, Number],
  tab: [String, Number]
});
const subTaskId = ref();
provide('subTaskId', subTaskId);
const currentTab = ref('');// The data definition is empty, and the tab content is rendered after mounted

const currentWS = ref()

// Objects passed to child components - child components need to watch for rendering, because there is a delay in rendering time on the parent component's side
const processObj = ref()

// Data on the number of abnormal alarms
const phaseNums = ref({
  '1': 0,
  '2': 0,
  '3': 0,
  '4': 0,
  total: 0,
})

const fullProcessCount = ref({
  totalErrorCount: 0,
  totalRunningCount: 0,
  totalSuccessCount: 0,
  totalWaitCount: 0,
})

const descValueObj = ref({
  subTaskName: '',
  fatherTask: '',
  executionMode: '',
  isMigrationObject: null,
  sourceIpPort: '',
  sourceLibrary: '',
  sourceDbType: '',
  sinkIpPort: '',
  sinkLibrary: '',
  executeTime: '',
  createTime: '',
  initiateTime: '',
})

const descData = computed(() => [
  {
    label: t('components.SubTaskDetail.subTaskName'),
    value: descValueObj.value.subTaskName || '--',
    prop: 'subTaskName'
  },
  {
    label: t('components.SubTaskDetail.fatherTask'),
    value: descValueObj.value.fatherTask || '--',
    prop: 'fatherTask'
  },
  {
    label: t('components.SubTaskDetail.executionMode'),
    value: getMigrationModelName(),
    prop: 'executionMode'
  },
  {
    label: t('components.SubTaskDetail.isMigrationObject'),
    value: descValueObj.value.isMigrationObject === null ? '--' : descValueObj.value.isMigrationObject ? 'true' : 'false',
    prop: 'isMigrationObject'
  },
  {
    label: t('detail.index.sourceIpPort'),
    value: descValueObj.value.sourceIpPort || '--',
    prop: 'sourceLibrary'
  },
  {
    label: t('components.SubTaskDetail.sourceLibrary'),
    value: descValueObj.value.sourceLibrary || '--',
    prop: 'sourceLibrary'
  },
  {
    label: t('components.SubTaskDetail.sourceLibraryType'),
    value: descValueObj.value.sourceDbType || '--',
    prop: 'sourceDbType'
  },
  {
    label: t('detail.index.targetIpPort'),
    value: descValueObj.value.sinkIpPort || '--',
    prop: 'sourceLibrary'
  },
  {
    label: t('components.SubTaskDetail.sinkLibrary'),
    value: descValueObj.value.sinkLibrary || '--',
    prop: 'sinkLibrary'
  },
  {
    label: t('components.SubTaskDetail.executedTime'),
    value: descValueObj.value.executeTime || '--',
    prop: 'executeTime'
  },
  {
    label: t('components.SubTaskDetail.createTime'),
    value: descValueObj.value.createTime || '--',
    prop: 'createTime'
  },
  {
    label: t('components.SubTaskDetail.initiateTime'),
    value: descValueObj.value.initiateTime || '--',
    prop: 'initiateTime'
  }
])

const getMigrationModelName = () => {
  const maps = {
    1: t('components.SubTaskDetail.offLineMigration'),
    2: t('components.SubTaskDetail.onLineMigration'),
    3: t('components.SubTaskDetail.offLineWithoutCheck'),
    4: t('components.SubTaskDetail.onLineWithoutCheck')
  }

  if (descValueObj.value.sourceDbType.toLocaleUpperCase() === JDBCType.MySQL
    || descValueObj.value.sourceDbType.toLocaleUpperCase() === JDBCType.PostgreSQL) {
    return maps[descValueObj.value.executionMode]
  }

  return t('components.SubTaskDetail.fullMigration')
}

// Obtain the current step and execution time of the step bar
const getTopExpressInfo = (info) => {
  subTaskInfo.value.execStatus = info?.execStatus
  subTaskInfo.value.currentExecStatus = info?.currentExecStatus
  subTaskInfo.value.isAutoFinish = info?.isAutoFinish
  // Gets the state of the runtime returned by the current interface
  let subTaskStatus = info?.execStatus
  // Determine whether to run success/failure status, these states cannot directly determine the step, you need to use the previous state
  if (stepJudge.has(subTaskStatus)) {
    subTaskStatus = info?.currentExecStatus
  }
  if (stepSet0.has(subTaskStatus)) {
    subTaskStep.value = 0
  } else if (stepSet5.has(subTaskStatus)) {
    subTaskStep.value = 5
  } else if (stepSet4.has(subTaskStatus)) {
    subTaskStep.value = 4
  } else if (stepSet3.has(subTaskStatus)) {
    subTaskStep.value = 3
  } else if (stepSet2.has(subTaskStatus)) {
    subTaskStep.value = 2
  } else {
    subTaskStep.value = 1
  }
  fullProcessCount.value.totalErrorCount = info?.totalErrorCount;
  fullProcessCount.value.totalRunningCount = info?.totalRunningCount;
  fullProcessCount.value.totalSuccessCount = info?.totalSuccessCount;
  fullProcessCount.value.totalWaitCount = info?.totalWaitCount;
  // Time Conversion - Here it is updated every 5 seconds, and it is necessary to judge whether the seconds are displayed or not
  try {
    if (!isNaN(Number(info?.executedTime))) {
      descValueObj.value.executeTime = getExectedTime(info?.executedTime)
      for (let i = 0; i < descData.value.length; i++) {
        if (descData.value[i].prop === 'executedTime') {
          descData.value[i].value = getExectedTime(info?.executedTime)
          return
        }
      }
    }
  } catch (e) {
    console.error(e)
  }
}

const getExectedTime = (timer) => {
  if (!timer) {
    return '--'
  }
  const hours = parseInt(timer / 3600)
  const min = parseInt((timer % 3600) / 60)
  const sec = timer - 3600 * hours - 60 * min
  let result = ''
  if (timer >= 3600) {
    // Turn to hours
    result = hours + t('components.SubTaskDetail.hour') + min + t('components.SubTaskDetail.min') + sec + t('components.SubTaskDetail.sec')
  } else if (timer >= 60) {
    result = min + t('components.SubTaskDetail.min') + sec + t('components.SubTaskDetail.sec')
  } else {
    result = sec + t('components.SubTaskDetail.sec')
  }
  return result
}

const currentWsKey = ref(0)
// Determine whether the object can be transferred
const testWebsocketFunc = () => {
  currentWsKey.value = new Date().getTime()
  const socketUrl = `data-migration/taskInfo_${subTaskId.value}_${currentWsKey.value}`
  const websocket = new Socket({ url: socketUrl })
  currentWS.value = websocket
  websocket.onopen(() => {
    console.log('open')
    isWSConnect.value = true
    subTaskInfoDetail(subTaskId.value, `taskInfo_${subTaskId.value}_${currentWsKey.value}`)
      .then(res => {
        console.log('ws connect success')
      })
      .catch(error => {
        console.error('ws connect error:', error)
      })
  })

  websocket.onmessage((data) => {
    if (data) {
      isWSConnect.value = true;
      try {
        const subTaskDetailObj = JSON.parse(data)
        // Pass information to child components
        processObj.value = subTaskDetailObj;
        if (subTaskDetailObj) {
          subTaskStore.updateSubTaskData(subTaskDetailObj)
        }
        // Here, the information obtained by the websocket is processed accordingly
        getTopExpressInfo(subTaskDetailObj)
      } catch (e) {
        console.error('subTaskDetail websocket message parse failed')
      }
    }
  })
  websocket.onerror((error) => {
    console.log('error-subTaskDetail-webscoket', error)
    isWSConnect.value = false
  })

  websocket.onclose(() => {
    console.warn('close-subTaskDetail-webscoket')
  })
}
const isWSConnect = ref(false)

onMounted(() => {
  subTaskId.value = window.$wujie?.props.data.id
  currentTab.value = window.$wujie?.props.data.tab || 'migrationProcess'

  descValueObj.value.sourceIpPort = sessionStorage.getItem('sourceIpPort')
  descValueObj.value.sinkIpPort = sessionStorage.getItem('sinkIpPort')
  // The webSocket is tested here
  if(!isWSConnect.value) {
    testWebsocketFunc()
  }
  // Get the basics here
  getSubTaskBasicInfo();
});

const sourceDbType = computed(() => {
  return descValueObj.value.sourceDbType?.toUpperCase?.() || ''
})

// Query the basic details of a subtask
const getSubTaskBasicInfo = () => {
  subTaskBasicInfo(subTaskId.value).then(res => {
    if (Number(res.code) === 200) {
      descValueObj.value.subTaskName = res.data?.subTaskId
      descValueObj.value.fatherTask = res.data?.taskName
      descValueObj.value.sourceDbType = JDBCType.normalize(res.data?.sourceDbType)
      // offline = 1; online = 2; other = 3
      let types = [JDBCType.MySQL, JDBCType.PostgreSQL].map(String)
      let shouldShowDbColumn =  types.includes(descValueObj.value.sourceDbType.toLocaleUpperCase() || '')
      descValueObj.value.executionMode = shouldShowDbColumn && res.data?.execMode !== undefined? res.data.execMode: 3
      descValueObj.value.isMigrationObject = res.data?.isMigrationObject
      descValueObj.value.sourceLibrary = res.data?.sourceDb || '--'
      descValueObj.value.sinkLibrary = res.data?.targetDb
      // The fifth one is assigned in the websocket
      if (res.data?.createTime) {
        // descData.value[6].value = transTimeType(res.data?.createTime)
        descValueObj.value.createTime = transTimeType(res.data?.createTime)
      }
      if (res.data?.startTime) {
        // descData.value[7].value = transTimeType(res.data?.startTime)
        descValueObj.value.initiateTime = transTimeType(res.data?.startTime)
      }
      subTaskMode.value = res.data?.execMode || 2;
      subTaskDbType.value = res.data?.sourceDbType || ''
    }
  }).catch(error => {
    console.error(error)
  })
}

const transTimeType = (isoTypeTime) => {
  const date = new Date(isoTypeTime);
  // Change the timestamp to year, month, and day Hours, minutes, seconds
  const Y = date.getFullYear() + '-';
  const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
  const D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
  const h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
  const m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
  const s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
  return Y + M + D + h + m + s;
}

// Before/off the corresponding webscoket before leaving the page
const closeWS = () => {
  console.log('close')
  if (currentWS.value) {
    currentWS.value?.destroy()
    currentWS.value = null;
  }
}

const autoRefresh = ref(true);
provide('autoRefresh', autoRefresh);
const emits = defineEmits(['update:open']);
const loading = ref(false);
const subTaskInfo = ref({});

const switchRefreshText = computed(() => {
  return autoRefresh.value ? t('components.SubTaskDetail.autoRefresh') : t('components.SubTaskDetail.stopRefresh')
})
// sub task status map
const execSubStatusMap = (status, isAutoFinish) => {
  const maps = {
    0: t('components.SubTaskDetail.5q09prnznzg0'),
    1: t('components.SubTaskDetail.5q09prnzo3s0'),
    2: t('components.SubTaskDetail.5q09prnzo6g0'),
    3: t('components.SubTaskDetail.5q09prnzoa00'),
    4: t('components.SubTaskDetail.5q09prnzodo0'),
    5: t('components.SubTaskDetail.5q09prnzohc0'),
    6: t('components.SubTaskDetail.5q09prnzok00'),
    7: t('components.SubTaskDetail.5q09prnzong0'),
    8: t('components.SubTaskDetail.5q09prnzoq80'),
    9: t('components.SubTaskDetail.5q09prnzotc0'),
    10: t('components.SubTaskDetail.5q09prnzotc1'),
    11: t('components.SubTaskDetail.5q09prnzoxc0'),
    12: t('components.SubTaskDetail.5q09prnzp000'),
    13: t('components.SubTaskDetail.5q09prnzp2k0'),
    30: t('components.SubTaskDetail.incrementError'),
    40: t('components.SubTaskDetail.reverseError'),
    100: t('list.index.5q08sf2dhj00'),
    500: t('components.SubTaskDetail.5q09prnzp740'),
    1000: t('components.SubTaskDetail.5q09prnzp980'),
    3000: t('detail.index.5q09asiwlca0')
  };

  if (status === 100 && isAutoFinish) {
    return t('components.SubTaskDetail.5q09prnzp540')
  }
  return maps[status];
};

const statusColorMap = (status, isAutoFinish) => {
  const maps = {
    0: 'info',
    1: 'primary',
    2: 'primary',
    3: 'primary',
    4: 'primary',
    5: 'primary',
    6: 'primary',
    7: 'primary',
    8: 'primary',
    9: 'primary',
    10: 'primary',
    11: 'primary',
    12: 'primary',
    13: 'primary',
    30: 'danger',
    40: 'danger',
    100: 'warning',
    500: 'danger',
    1000: 'primary',
    3000: 'danger'
  }

  if (status === 100 && isAutoFinish) {
    return 'success'
  }
  return maps[status]
};
// timer
const intervalid = ref(null);

// You need to poll for the number of abnormal alarms
const getErrorTotal = async (type) => {
  if (type === 'loopQuery' && !autoRefresh.value) {
    // If it is polled and the auto-refresh has now ended, the query is no longer made
    return;
  }
  if (!subTaskId.value) {
    return;
  }
  try {
    const { data, code } = await getTotalAlarmNum(subTaskId.value)
    if (code === 200) {
      phaseNums.value = data ?? {}
    }
  } catch (error) {
    console.error(error)
  }
}

watch(() => autoRefresh.value, () => {
  if (autoRefresh.value) {
    if (intervalid.value == null) {
      intervalid.value = setInterval(() => {
        getErrorTotal('loopQuery')
      }, 6000);
    }
  } else {
    cancelInterval()
  }
}, { immediate: true })

// Dismiss the timer query
const cancelInterval = () => {
  if (intervalid.value != null) {
    clearInterval(intervalid.value)
    intervalid.value = null
  }
}

onBeforeUnmount(() => {
  closeWS();
  cancelInterval();
});
</script>

<style lang="less" scoped>
.detail-container {
  height: calc(100vh - 114px);
  background-color: var(--o-bg-color-light);
  padding: 20px 24px 28px 20px;
  overflow-x: auto;

  .mainDetail {
    display: flex;
    gap: 24px;
    min-height: 100%;
    min-width: 1060px;

    .leftContent {
      width: 292px;
      display: flex;
      flex-direction: column;
      gap: 16px;

      .basic-title {
        height: 80px;
        background-color: var(--o-bg-color-light2);
        display: flex;
        padding: 0 24px;
        align-items: center;
        gap: 16px;

        img {
          width: 40px;
          height: 40px;
        }

        .title-right {
          flex: 1;
          width: 0;
          display: flex;
          flex-direction: column;
          gap: 8px;
          color: var(--o-text-color-primary);

          .name-text {
            height: 24px;
            line-height: 24px;
            font-size: 16px;
            font-weight: bolder;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }

          .el-tag {
            width: fit-content;
            padding: 4px 24px;
            font-size: 12px;
            max-width: 172px !important;
          }
        }
      }

      .basic-info {
        flex: 1;
        padding: 20px 22px;
        background-color: var(--o-bg-color-light2);
        color: var(--o-text-color-primary);

        .info-title {
          height: 24px;
          line-height: 24px;
          font-size: 16px;
          font-weight: bolder;
        }

        .basicItem {
          display: flex;
          height: 24px;
          line-height: 24px;
          margin-top: 12px;
          gap: 4px;

          .basicLable {
            min-width: 106px;
            color: var(--o-text-color-secondary)
          }

          .basicValue {
            flex: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }
        }
      }
    }

    .rightContent {
      flex: 1;
      display: flex;
      flex-direction: column;
      width: 0;
      gap: 16px;
      position: relative;

      .switchUpdate {
        position: absolute;
        width: fit-content;
        right: 0;
        top: 0;
        display: flex;
        gap: 8px;
        z-index: 11;

        .switchDesc {
          color: var(--o-text-color-primary);
        }
      }

      ::v-deep(.row-content) {
        height: 100%;

        .el-tabs {
          height: 100%;
        }

        .el-tab-pane {
          height: 100%;
        }

        .el-tabs__nav.is-stretch {
          gap: 32px;

          .el-tabs__item {
            max-width: fit-content;
            padding: 4px 0;
          }
        }

        .errorAlertCount {
          margin-left: 0px;
        }
      }

      .top-content {
        height: 80px;
        display: flex;
        width: 100%;

        .card-area {
          flex: 1;
          display: flex;
          gap: 4px;
          max-width: calc(100% - 192px);

          .main-card {
            max-width: 182px;
            width: calc(100% - 20px) / 6;
          }
        }
      }

      .bottom-content {
        flex: 1;
        display: flex;
        flex-direction: column;
        padding: 24px;
        background-color: var(--o-bg-color-light2);

        .list-title {
          height: 24px;
          font-size: 16px;
          font-weight: 600;
          color: #000;
          margin-bottom: 16px;
        }

        .main-table {
          flex: 1;
          display: flex;
          flex-direction: column;
          justify-content: space-between;
        }
      }
    }
  }
}
</style>