<script lang="ts"></script>

<script setup lang="ts">
import type { NdmNvrResultVO, NdmRecordCheck, RecordInfo, RecordItem } from '@/apis/models';
import { getRecordCheckByParentId as getRecordCheckByParentIdApi, reloadAllRecordCheck as reloadAllRecordCheckApi } from '@/apis/requests';
import { useMutation } from '@tanstack/vue-query';
import { formatDuration } from '@/utils/format-duration';
import { VideocamOutline, TimeOutline, WarningOutline, CheckmarkCircleOutline, RefreshOutline } from '@vicons/ionicons5';
import dayjs from 'dayjs';
import { destr } from 'destr';
import { groupBy } from 'es-toolkit';
import { NCard, NFlex, NText, NTag, NTimeline, NTimelineItem, NIcon, NEmpty, NStatistic, NGrid, NGridItem, NCollapse, NCollapseItem, NButton, NPopconfirm } from 'naive-ui';
import { computed, onMounted, ref, toRefs } from 'vue';

type NvrRecordDiag = {
  gbCode: string;
  channelName: string;
  recordDuration: RecordItem;
  lostRecordList: RecordItem[];
};

// 过滤出丢失的录像时间段
const filterLostRecordList = (rawRecordChecks: NdmRecordCheck[]): NvrRecordDiag[] => {
  // 1. 解析diagInfo
  const recordChecks = rawRecordChecks.map((recordCheck) => {
    return {
      ...recordCheck,
      diagInfo: destr<RecordInfo>(recordCheck.diagInfo),
    };
  });
  // 2.按国标码分组
  const recordChecksByGbCode = groupBy(recordChecks, (recordCheck) => recordCheck.gbCode);
  // 3. 提取分组后的国标码和录像诊断记录
  const channelGbCodes = Object.keys(recordChecksByGbCode);
  const groupedRecordChecks = Object.values(recordChecksByGbCode);
  // 4. 初始化每个通道的诊断数据结构
  let recordDiagList = channelGbCodes.map((gbCode, index) => ({
    gbCode,
    channelName: groupedRecordChecks[index].at(-1)?.name ?? '',
    records: [] as RecordItem[],
    lostRecords: [] as RecordItem[],
  }));
  // 5. 写入同一gbCode的录像片段
  groupedRecordChecks.forEach((recordCheckGroup, index) => {
    recordCheckGroup.forEach((recordCheck) => {
      recordDiagList[index].records.push(...recordCheck.diagInfo.recordList);
    });
  });
  // 6. 过滤掉没有录像记录的通道
  recordDiagList = recordDiagList.filter((chunk) => chunk.records.length > 0);
  // 7. 计算每个通道丢失的录像时间片段
  recordDiagList.forEach((chunk) => {
    chunk.records.forEach((record, index, recordList) => {
      if (recordList[index + 1]) {
        // 如果下一段录像的开始时间不等于当前录像的结束时间，则判定为丢失
        if (recordList[index + 1].startTime !== record.endTime) {
          chunk.lostRecords.push({
            startTime: record.endTime,
            endTime: recordList[index + 1].startTime,
          });
        }
      }
    });
  });
  return recordDiagList.map((recordDiag) => ({
    gbCode: recordDiag.gbCode,
    channelName: recordDiag.channelName,
    recordDuration: {
      startTime: recordDiag.records.at(0)?.startTime ?? '',
      endTime: recordDiag.records.at(-1)?.endTime ?? '',
    },
    lostRecordList: recordDiag.lostRecords,
  }));
};

const formatTime = (time: string) => {
  return dayjs(time).format('MM-DD HH:mm:ss');
};

const props = defineProps<{
  stationCode: string;
  ndmNvr: NdmNvrResultVO;
}>();

const { stationCode, ndmNvr } = toRefs(props);

const recordCheckList = ref<NdmRecordCheck[]>([]);

const lostRecordList = computed(() => filterLostRecordList(recordCheckList.value));

const recordDiagStatistics = computed(() => {
  const channelCount = lostRecordList.value.length;
  const channelWithLossCount = lostRecordList.value.filter((diag) => diag.lostRecordList.length > 0).length;
  const lossCount = lostRecordList.value.reduce((count, diag) => count + diag.lostRecordList.length, 0);

  const totalLossDuration = lostRecordList.value.reduce((totalDuration, diag) => {
    const channelLossDuration = diag.lostRecordList.reduce((duration, loss) => {
      return duration + dayjs(loss.endTime).diff(dayjs(loss.startTime));
    }, 0);
    return totalDuration + channelLossDuration;
  }, 0);

  const formatTotalLossDuration = (duration: number) => {
    const dayjsDuration = dayjs.duration(duration);
    const asHours = dayjsDuration.asHours();
    const h = Math.floor(asHours);
    const m = dayjsDuration.minutes();
    const s = dayjsDuration.seconds();
    if (asHours >= 1) {
      return `${h}小时${m}分钟${s}秒`;
    }
    return `${m}分钟${s}秒`;
  };

  return {
    totalChannels: channelCount,
    channelsWithLoss: channelWithLossCount,
    totalLossCount: lossCount,
    totalLossDuration: formatTotalLossDuration(totalLossDuration),
  };
});

const { mutate: getRecordCheckByParentId, isPending: loading } = useMutation({
  mutationFn: async () => {
    const recordCheckList = await getRecordCheckByParentIdApi(stationCode.value, ndmNvr.value, 90);
    return recordCheckList;
  },
  onSuccess: (checkList) => {
    recordCheckList.value = checkList;
  },
  onError: (error) => {
    console.error(error);
    window.$message.error(error.message);
  },
});

const { mutate: reloadAllRecordCheck, isPending: reloading } = useMutation({
  mutationFn: async () => {
    await reloadAllRecordCheckApi(stationCode.value, 90);
  },
  onSuccess: () => {
    window.$message.success('正在逐步刷新中，请稍后点击刷新按钮查看');
  },
  onError: (error) => {
    console.error(error);
    window.$message.error(error.message);
  },
});

onMounted(() => {
  getRecordCheckByParentId();
});
</script>

<template>
  <NCard size="small" hoverable>
    <template #header>
      <NFlex :align="'center'" :size="24">
        <div>录像诊断</div>
        <NPopconfirm @positive-click="() => reloadAllRecordCheck()">
          <template #trigger>
            <NButton secondary size="small" :loading="reloading">
              <span>点击更新所有通道录像诊断</span>
            </NButton>
          </template>
          <template #default>
            <span>确认更新所有通道录像诊断吗？</span>
          </template>
        </NPopconfirm>
      </NFlex>
    </template>
    <template #header-extra>
      <NButton size="small" quaternary circle :loading="loading" @click="() => getRecordCheckByParentId()">
        <template #icon>
          <NIcon>
            <RefreshOutline />
          </NIcon>
        </template>
      </NButton>
    </template>
    <NFlex vertical :size="16">
      <!-- 统计信息 -->
      <NGrid :cols="4" :x-gap="12">
        <NGridItem>
          <NStatistic label="总通道数" :value="recordDiagStatistics.totalChannels" />
        </NGridItem>
        <NGridItem>
          <NStatistic label="有缺失通道" :value="recordDiagStatistics.channelsWithLoss" />
        </NGridItem>
        <NGridItem>
          <NStatistic label="缺失次数" :value="recordDiagStatistics.totalLossCount" />
        </NGridItem>
        <NGridItem>
          <NStatistic label="缺失时长" :value="recordDiagStatistics.totalLossDuration" />
        </NGridItem>
      </NGrid>

      <!-- 通道录像缺失详情 -->
      <div v-if="lostRecordList.length > 0">
        <NCollapse>
          <NCollapseItem v-for="channel in lostRecordList" :key="channel.gbCode" :name="channel.gbCode">
            <template #header>
              <NFlex align="center" :size="8">
                <NIcon size="16" :color="channel.lostRecordList.length > 0 ? '#f5222d' : '#52c41a'">
                  <VideocamOutline />
                </NIcon>
                <NText strong>{{ channel.channelName }}</NText>
                <NTag :type="channel.lostRecordList.length > 0 ? 'error' : 'success'" size="small">
                  {{ channel.lostRecordList.length > 0 ? `${channel.lostRecordList.length}次缺失` : '正常' }}
                </NTag>
              </NFlex>
            </template>

            <template #header-extra>
              <NFlex align="center" :size="4">
                <NIcon size="14" color="#666">
                  <TimeOutline />
                </NIcon>
                <NText depth="3" style="font-size: 12px"> {{ formatTime(channel.recordDuration.startTime) }} ~ {{ formatTime(channel.recordDuration.endTime) }} </NText>
              </NFlex>
            </template>

            <div style="padding-left: 24px">
              <!-- 录像缺失时间轴 -->
              <div v-if="channel.lostRecordList.length > 0">
                <NText depth="2" style="margin-bottom: 12px; display: block"> 录像缺失时间段： </NText>
                <NTimeline>
                  <NTimelineItem v-for="(loss, index) in channel.lostRecordList" :key="index" type="error" :time="formatTime(loss.startTime)">
                    <template #icon>
                      <NIcon>
                        <WarningOutline />
                      </NIcon>
                    </template>
                    <template #default>
                      <NFlex vertical :size="4">
                        <NText> 缺失时段：{{ formatTime(loss.startTime) }} ~ {{ formatTime(loss.endTime) }} </NText>
                        <NText depth="3" style="font-size: 12px"> 持续时长：{{ formatDuration(loss.startTime, loss.endTime) }} </NText>
                      </NFlex>
                    </template>
                  </NTimelineItem>
                </NTimeline>
              </div>

              <!-- 无缺失状态 -->
              <div v-else>
                <NFlex align="center" :size="8" style="padding: 16px 0">
                  <NIcon size="16" color="#52c41a">
                    <CheckmarkCircleOutline />
                  </NIcon>
                  <NText type="success">该通道录像完整，无缺失时间段</NText>
                </NFlex>
              </div>
            </div>
          </NCollapseItem>
        </NCollapse>
      </div>

      <!-- 空状态 -->
      <div v-else>
        <NEmpty description="暂无录像诊断数据" style="padding: 40px 0" />
      </div>
    </NFlex>
  </NCard>
</template>

<style scoped lang="scss">
// 自定义样式
.n-collapse-item {
  margin-bottom: 8px;
}

.n-timeline {
  padding-left: 8px;
}

.n-statistic {
  text-align: center;
}
</style>
