<template>
  <div class="app-container" id="pluginManage">
    <div class="main-bd">
      <a-spin :loading="doLoading" style="display: block;">
        <div class="search-con">
          <a-form :model="form" layout="inline">
            <a-form-item field="keyWords" style="margin-left: -17px;">
              <a-input v-model="form.keyWords" allow-clear :placeholder="$t('manage.index.5m5v55wx37o0')" style="width: 200px;" @change="getList"></a-input>
            </a-form-item>
            <a-form-item field="status" :label="$t('manage.index.5m5v55wxj5s0')">
              <a-select v-model="form.status" :placeholder="$t('manage.index.5m5v55wxjf40')" allow-clear style="width: 200px;" @change="getList">
                <a-option :value="2">{{$t('manage.index.5m5v55wxjjs0')}}</a-option>
                <a-option :value="1">{{$t('manage.index.5m5v55wxjo00')}}</a-option>
              </a-select>
            </a-form-item>
            <a-form-item>
              <a-button type="outline" @click="getList">
                <template #icon>
                  <icon-search />
                </template>
                <template #default>{{$t('manage.index.5m5v55wxjsg0')}}</template>
              </a-button>
              <a-button style="margin-left: 10px;" @click="resetQuery">
                <template #icon>
                  <icon-sync />
                </template>
                <template #default>{{$t('manage.index.5m5v55wxjwg0')}}</template>
              </a-button>
            </a-form-item>
          </a-form>
          <div class="btn-con">
            <a-button type="primary" @click="handleUpload">
              <template #icon>
                <icon-plus />
              </template>
              <template #default>{{$t('manage.index.5m5v55wxk140')}}</template>
            </a-button>
          </div>
        </div>
        <div v-if="!loading && pluginList.length" class="list-con">
          <div v-for="item in pluginList" :key="item.id" class="list-item">
            <div class="item-info">
              <div class="plugin-logo">
                <img :src="item.logoPath" alt="">
              </div>
              <div class="plugin-info">
                <div class="plugin-name-con">
                  <div class="name" :title="item.pluginId">{{item.pluginId}}</div>
                  <div class="version" :title="item.pluginVersion">{{item.pluginVersion}}</div>
                  <div class="author" v-if="item.pluginProvider" :title="item.pluginProvider">{{item.pluginProvider}}</div>
                </div>
                <div class="plugin-desc" :title="currentLocale === 'zh-CN' ? item.pluginDesc : item.pluginDescEn">{{ currentLocale === 'zh-CN' ? item.pluginDesc : item.pluginDescEn }}</div>
              </div>
            </div>
            <div class="item-btn-con">
              <div class="btn-left">
                <a-popconfirm :content="$t('manage.index.else3', {id: item.pluginId})" @ok="handleUninstall(item)">
                  <a-button type="outline" size="mini">
                    <template #icon>
                      <icon-delete />
                    </template>
                    <template #default>{{$t('manage.index.5m5v55wxkrw0')}}</template>
                  </a-button>
                </a-popconfirm>

                <a-button
                  v-if="item.extendInfo && item.extendInfo.pluginLicenseType"
                  type="dashed"
                  shape="round"
                  size="mini"
                  status="success"
                  @click="handleAuthDetail(item, item.extendInfo)"
                >
                  <template #icon>
                    <icon-info-circle />
                  </template>
                  <template #default>
                    {{ $t(`manage.index.${item.extendInfo.pluginLicenseType}`) }}
                  </template>
                </a-button>

                <template v-if="item.extendInfo && item.extendInfo.authAddress">
                  <a-link
                    v-if="isAbsolutePath(item.extendInfo.authAddress)"
                    :href="item.extendInfo.authAddress"
                    target="_blank"
                    class="update-auth-link"
                  >
                    <template #icon>
                      <icon-link />
                    </template>
                    {{ $t('manage.index.update_authorization') }}
                  </a-link>
                  <router-link v-else class="update-auth-link" :to="`/static-plugin/${item.pluginId}${item.extendInfo.authAddress}`">
                    {{$t('manage.index.update_authorization')}}
                  </router-link>
                </template>
              </div>
              <div class="btn-right">
                <a-switch type="line" v-model="item.pluginStatus" :checked-value="1" :unchecked-value="2" @change="switchChange(item)"></a-switch>
                <span class="txt">{{item.pluginStatus === 1 ? $t('manage.index.5n6f62aai3k0') : $t('manage.index.5n6f62aajx80')}}</span>
              </div>
            </div>
          </div>
        </div>
        <div v-if="!pluginList.length" class="loading-con">
          <a-spin v-if="loading" :size="32" />
          <a-empty v-else />
        </div>
      </a-spin>
    </div>

    <!-- plugin auth detail -->
    <a-modal
      :title="$t('manage.index.authorization_detail')"
      v-model:visible="authDetail.open"
      :footer="false"
      @cancel="handleAuthDetail(null)"
      width="600px"
      title-align="start"
    >
      <div v-if="authDetail.info && authDetail.extendInfo" class="auth-detail">
        <div class="auth-detail__item">
          <label class="auth-detail__label">{{ $t('manage.index.plugin_name') }}:</label>
          <div class="auth-detail__value">
            <a-space size="mini">
              <img :src="authDetail.info.logoPath" :alt="authDetail.info.pluginId" class="auth-detail__logo">
              <span>{{ authDetail.info.pluginId }}</span>
            </a-space>
          </div>
        </div>
        <div class="auth-detail__item">
          <label class="auth-detail__label">{{ $t('manage.index.authorization') }}:</label>
          <div class="auth-detail__value">
            <a-button
              type="dashed"
              shape="round"
              size="mini"
              status="success"
              class="auth-detail__auth-type"
            >
              {{ $t(`manage.index.${authDetail.extendInfo.pluginLicenseType}`) }}
            </a-button>
          </div>
        </div>
        <div class="auth-detail__item" v-if="authDetail.extendInfo.pluginActivationTime">
          <label class="auth-detail__label">{{ $t('manage.index.activation_time') }}:</label>
          <div class="auth-detail__value">
            {{ dayjs(authDetail.extendInfo.pluginActivationTime).format('YYYY-MM-DD HH:mm:ss') }}
          </div>
        </div>
        <div class="auth-detail__item" v-if="authDetail.extendInfo.pluginExpirationTime">
          <label class="auth-detail__label">{{ $t('manage.index.expires') }}:</label>
          <div class="auth-detail__value">
            {{ dayjs(authDetail.extendInfo.pluginExpirationTime).format('YYYY-MM-DD HH:mm:ss') }}
          </div>
        </div>
        <div class="auth-detail__item full">
          <label class="auth-detail__label">{{ $t('manage.index.plugin_description') }}:</label>
          <div class="auth-detail__value">{{ authDetail.info.pluginDesc }}</div>
        </div>
        <div class="auth-detail__item full" v-if="authDetail.extendInfo.pluginDevelopmentCompany">
          <label class="auth-detail__label">{{ $t('manage.index.company') }}:</label>
          <div class="auth-detail__value">{{ authDetail.extendInfo.pluginDevelopmentCompany }}</div>
        </div>
        <div class="auth-detail__item" v-if="authDetail.extendInfo.phoneNumber">
          <label class="auth-detail__label">{{ $t('manage.index.phone') }}:</label>
          <div class="auth-detail__value">{{ authDetail.extendInfo.phoneNumber }}</div>
        </div>
        <div class="auth-detail__item" v-if="authDetail.extendInfo.email">
          <label class="auth-detail__label">{{ $t('manage.index.email') }}:</label>
          <div class="auth-detail__value">{{ authDetail.extendInfo.email }}</div>
        </div>
        <div class="auth-detail__item full" v-if="authDetail.extendInfo.companyAddress">
          <label class="auth-detail__label">{{ $t('manage.index.address') }}:</label>
          <div class="auth-detail__value">{{ authDetail.extendInfo.companyAddress }}</div>
        </div>
        <div class="auth-detail__item" v-if="authDetail.extendInfo.userGuide">
          <label class="auth-detail__label">{{ $t('manage.index.manual') }}:</label>
          <div class="auth-detail__value">
            <a-link :href="authDetail.extendInfo.userGuide" target="_blank">
              {{ authDetail.extendInfo.userGuide }}
            </a-link>
          </div>
        </div>
        <div class="auth-detail__item" v-if="authDetail.extendInfo.demoAddress">
          <label class="auth-detail__label">{{ $t('manage.index.demo') }}:</label>
          <div class="auth-detail__value">
            <a-link :href="authDetail.extendInfo.demoAddress" target="_blank">
              {{ authDetail.extendInfo.demoAddress }}
            </a-link>
          </div>
        </div>
      </div>
    </a-modal>
    <plugin-install v-model:visible="uploadModal" :wsId="wsBusinessId" @downloadStart="downloadStart" />
    <download-notification ref="downloadNotRef" :percentage="currPercent" :msg="$t('manage.index.onlineDownload')" :iconClass="'rar-file'"
                           :fileName="uploadName"></download-notification>
    <plugin-config v-model:open="configVisible" :plugin-id="configPluginId" :configAttrs="configAttrs" :configData="configData" />
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted, watch } from 'vue'
import { useAppStore, useTabBarStore } from '@/store'
import { Message } from '@arco-design/web-vue'
import { getToken } from '@/utils/auth'
import { list, start, stop, uninstall, extendInfoList } from '@/api/plugin'
import { getUserInfo } from '@/api/user'
import PluginConfig from './components/PluginConfig.vue'
import PluginInstall from './components/PluginInstall.vue'
import { destroyPluginApp } from '@/utils/pluginApp'
import useLocale from '@/hooks/locale'
import dayjs from 'dayjs'
import downloadNotification from '@/components/downloadNotification'
import showMessage from "@/hooks/showMessage";

const appStore = useAppStore()
const tabBarStore = useTabBarStore()
const { currentLocale } = useLocale()
const loading = ref<boolean>(true)
const doLoading = ref<boolean>(false)

const form = reactive({
  keyWords: undefined,
  status: undefined
})

const upload = reactive({
  open: false,
})

const authDetail = reactive<any>({
  open: false,
  info: null,
  extendInfo: null
})

const pluginList = ref<any[]>([])
const configPluginId = ref<string | number>('')
const configVisible = ref<boolean>(false)
const configAttrs = ref<any[]>([])
const configData = ref<any>({})

const getList = () => {
  const params = {
    pluginId: form.keyWords,
    pluginStatus: form.status
  }

  loading.value = true
  Promise.all([
    list(params),
    extendInfoList(params)
  ])
    .then(([listRes, extendInfoRes]: any[]) => {
      const nextPluginList = (listRes.rows ?? []).map((item: any) => Object.assign(item, {
        extendInfo: extendInfoRes.rows.find((info: any) => info.pluginId === item.pluginId) ?? null
      }))

      pluginList.value = nextPluginList
    })
    .finally(() => {
      loading.value = false
      doLoading.value = false
    })
}

const resetQuery = () => {
  form.keyWords = undefined
  form.status = undefined
  getList()
}
const uploadModal = ref<boolean>(false)
const handleUpload = () => {
  webSocketOpen()
  uploadModal.value = true
  upload.open = true
}

watch(
  () => uploadModal.value,
  (oldVal, newVal) => {
    if (newVal && !oldVal) {
      appStore.fetchServerMenuConfig()
      getList()
    }
  }
)
const switchChange = async (row: any) => {
  const pluginId = row.pluginId
  if (row.pluginStatus !== 2) {
    try {
      doLoading.value = true
      await start(pluginId)
      doLoading.value = false
      Message.success('Start success')
    } catch (e) {
      doLoading.value = false
    }
  } else {
    try {
      doLoading.value = true
      await stop(pluginId)
      doLoading.value = false
      Message.success('Stop success')
      const tagList: any[] = [...tabBarStore.tagList]
      tagList.forEach((item: any) => {
        if (~item.fullPath.indexOf(`/${pluginId}/`)) {
          tabBarStore.deleteTags(item)
          destroyPluginApp(item.fullPath)
        }
      })
    } catch (e) {
      doLoading.value = false
    }
  }
  appStore.fetchServerMenuConfig()
  getList()
}

const handleUninstall = async (row: any) => {
  const pluginId = row.pluginId
  try {
    doLoading.value = true
    await uninstall(pluginId)
    doLoading.value = false
    Message.success('Uninstall success')
    const tagList: any[] = [...tabBarStore.tagList]
    tagList.forEach((item: any) => {
      if (~item.fullPath.indexOf(`/${pluginId}/`)) {
        tabBarStore.deleteTags(item)
        destroyPluginApp(item.fullPath)
      }
    })
  } catch (e) {
    doLoading.value = false
  }
  appStore.fetchServerMenuConfig()
  getList()
}

const isAbsolutePath = (url: string): boolean => url.startsWith('http')

const handleAuthDetail = (info: any, extendInfo: any = null): void => {
  authDetail.info = info
  authDetail.extendInfo = extendInfo
  authDetail.open = Boolean(info)
}
const wsBusinessId = ref('')
const currPercent = ref<number>(0)
const percentLoading = ref<boolean>(false)
const processVisible = ref<boolean>(false)

watch(currPercent, (newValue) => {
  if (newValue === 100) {
    processVisible.value = false
    closeNotification()
    appStore.fetchServerMenuConfig()
    getList()
  }
})
const notificationVisible = ref<boolean>(false)
const closeNotification = () => {
  notificationVisible.value = false
  uploadName.value = ''
}

const uploadName = ref('')
const downloadStart = (name: string) => {
  if (name === 'END') {
    getList()
    appStore.fetchServerMenuConfig()
  } else {
    uploadName.value = name
    notificationVisible.value = true
  }
}

const timer = ref<any>(null)
const lastProcess = ref(0);
const nextProcess = ref(0);
const webSocketOpen = () => {
  currPercent.value = 0
  const socketKey = new Date().getTime()
  const wsPrefix = window.location.protocol.includes('https') ? 'wss' : 'ws'
  const socketUrl = `${wsPrefix}://${window.location.host}/websocket/1/downloadPlugin_${socketKey}`
  const websocket = new WebSocket(socketUrl)
  wsBusinessId.value = `downloadPlugin_${socketKey}`
  websocket.onopen = function (event) {
    wsBusinessId.value = `downloadPlugin_${socketKey}`
  }
  websocket.onmessage = function (event) {
    processVisible.value = true
    const messageData = event.data
    downloadNotRef.value?.createOrUpdateNotification(wsBusinessId, messageData, uploadName.value)
    if (messageData === 'File download Failed') {
      showMessage('error', t('components.Package.5mtcyb0rty52'))
      websocket.close()
      downloadNotRef.value?.closeNotifiCation(wsBusinessId);
      clearInterval(timer.value)
    } else {
      if (!isNaN(Number(messageData))) {
        const percent = Number(messageData)
        nextProcess.value = percent;
        clearInterval(timer.value);
        timer.value = setInterval(() => {
          if (nextProcess.value === 1) {
            lastProcess.value = 0;
            nextProcess.value = 0;
          } else if (nextProcess.value.toString() === lastProcess.value.toString()) {
            let warnningMsg = setTimeout(() => {
              showMessage('error', 'websocket长时间无响应,将关闭下载进度悬浮框')
              downloadNotRef.value?.closeNotifiCation(wsBusinessId);
              clearTimeout(warnningMsg)
            }, 3000)
            websocket.close();
            clearInterval(timer.value);
          } else {
            lastProcess.value = nextProcess.value
          }
        }, 10000)
        if (percent === 1) {
          clearInterval(timer.value);
          percentLoading.value = false
          lastProcess.value = 0;
          nextProcess.value = 0;
          websocket.close()
          downloadNotRef.value?.closeNotifiCation(wsBusinessId);
          doLoading.value = true
          downloadStart('END')
        }
      } else if (messageData === 'DOWNLOAD_FINISH') {
        percentLoading.value = false
        websocket.close()
        downloadNotRef.value?.closeNotifiCation(wsBusinessId);
        lastProcess.value = 0;
        nextProcess.value = 0;
        clearInterval(timer.value)
        doLoading.value = true
        downloadStart('END')
      } else {
        console.error('WebSocket error', messageData)
        websocket.close()
        downloadNotRef.value?.closeNotifiCation(wsBusinessId);
        lastProcess.value = 0;
        nextProcess.value = 0;
        clearInterval(timer.value)
      }
    }
  }
  websocket.onclose = function () {
    console.log('WebSocket Close')
  }
  websocket.onerror = function (error) {
    console.error('WebSocket error:', error)
  }
}

const downloadNotRef = ref(null)

const init =() => {
  getList()
}
onMounted(() => {
  init()
})
</script>

<style lang="less" scoped>
.app-container {
  .main-bd {
    position: relative;
    .loading-con {
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      pointer-events: none;
    }
    .search-con {
      padding: 16px 20px;
      display: flex;
      justify-content: space-between;
    }
    .list-con {
      padding-left: 20px;
      padding-bottom: 20px;
      display: grid;
      grid-column-gap: 20px;
      grid-row-gap: 20px;
      grid-template-columns: repeat(auto-fill, 400px);
      justify-content: center;
      margin: auto;
      max-width: calc(400px * 3 + 20 * 3);
      .list-item {
        background: var(--color-bg-3);
        box-shadow: 0px 0px 8px 0px rgba(201,205,212,0.5);
        border-radius: 2px;
        padding: 16px;
        width: 100%;
        height: 190px;
        grid-column-start: auto;
        grid-row-start: auto;
        position: relative;
        .item-info {
          display: flex;
          align-items: flex-start;
          .plugin-logo {
            margin-right: 10px;
            width: 50px;
            height: 50px;
            padding: 5px;
            border: 1px solid transparent;
            display: flex;
            justify-content: center;
            align-items: center;
            img {
              width: 40px;
              height: 40px;
              border-radius: 50%;
            }
          }
          .plugin-info {
            .plugin-name-con {
              display: flex;
              align-items: center;
              width: 100%;
              margin-bottom: 8px;
              .name {
                font-size: 16px;
                color: var(--color-text-1);
              }
              .version {
                margin-top: 3px;
                margin-left: 5px;
                margin-right: 10px;
                font-size: 12px;
                color: rgb(var(--primary-6));
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
              }
              .author {
                border: 1px solid var(--color-text-2);
                height: 18px;
                line-height: 16px;
                padding: 0 5px;
                font-size: 12px;
                color: var(--color-text-2);
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
              }
            }
            .plugin-desc {
              font-size: 14px;
              color: var(--color-text-2);
              line-height: 20px;
              overflow: hidden;
              text-overflow: ellipsis;
              display: -webkit-box;
              -webkit-line-clamp: 4;
              -webkit-box-orient: vertical;
            }
          }
        }
        .item-btn-con {
          position: absolute;
          left: 0;
          bottom: 16px;
          display: flex;
          justify-content: space-between;
          align-items: center;
          width: 100%;
          padding: 0 20px;
          .btn-left {
            display: flex;
            align-items: center;
            gap: 12px;

            :deep(.arco-btn.arco-btn-size-mini) {
              display: inline-flex;
              align-items: center;
              line-height: 24px;
            }

            :deep(.update-auth-link) {
              font-size: 12px;
            }
          }
          .btn-right {
            display: flex;
            align-items: center;
            .txt {
              color: var(--color-text-2);
              font-size: 14px;
              margin-left: 5px;
            }
          }
        }
      }
    }
  }
}

.upload-info {
  background: var(--color-fill-2);
  border: 1px dotted var(--color-fill-4);
  height: 180px;
  width: 100%;
  border-radius: 2px;
  text-align: center;
  .upload-icon {
    margin-top: 44px;
    margin-bottom: 16px;
  }
  .tips-1 {
    font-size: 16px;
    color: var(--color-text-1);
    margin-bottom: 12px;
    .highlight {
      color: rgb(var(--primary-6));
    }
  }
  .tips-2 {
    font-size: 14px;
    color: var(--color-text-1);
  }
}
.modal-footer {
  text-align: center;
}

.auth-detail {
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  align-content: start;
  gap: 8px 20px;
  min-height: 200px;
  font-size: 12px;
  line-height: 22px;

  &__item {
    display: flex;
    min-width: calc(50% - 10px);
    width: calc(50% - 10px);
  }
  &__item.full {
    min-width: 100%;
    width: 100%;
  }

  &__label {
    width: 80px;
    font-weight: 500;
    text-align: right;
  }

  &__value {
    display: flex;
    align-items: center;
    flex: 1;
  }

  &__logo {
    width: 20px;
    height: 20px;
  }

  &__auth-type {
    padding-top: 2px;
    height: 22px;
    cursor: default;
  }
}
</style>