<script setup lang="ts">
import { detailDeviceApi, updateDeviceApi, type NdmCameraLinkDescription, type NdmDeviceResultVO, type NdmSecurityBoxLinkDescription, type NdmSecurityBoxResultVO, type Station } from '@/apis';
import { DeviceTree } from '@/components';
import { tryGetDeviceType } from '@/enums';
import { useDeviceStore, useSettingStore } from '@/stores';
import { parseErrorFeedback } from '@/utils';
import { useMutation } from '@tanstack/vue-query';
import { isCancel } from 'axios';
import destr from 'destr';
import { cloneDeep } from 'es-toolkit';
import { NButton, NFlex, NModal } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, ref, toRefs } from 'vue';

const props = defineProps<{
  ndmDevice: NdmSecurityBoxResultVO;
  station: Station;
  circuitIndex?: number;
}>();

const show = defineModel<boolean>('show', { default: false });

const deviceStore = useDeviceStore();

const settingStore = useSettingStore();
const { useLocalDB } = storeToRefs(settingStore);

const { ndmDevice, station, circuitIndex } = toRefs(props);

const upperDeviceLinkDescription = computed(() => {
  const result = destr<any>(ndmDevice.value.linkDescription);
  if (!result) return null;
  if (typeof result !== 'object') return null;
  return result as NdmSecurityBoxLinkDescription;
});

const lowerDevice = ref<NdmDeviceResultVO>();
const lowerDeviceLinkDescription = computed<NdmCameraLinkDescription | null>(() => {
  if (!lowerDevice.value) return null;
  const result = destr<any>(lowerDevice.value.linkDescription);
  if (!result) return null;
  if (typeof result !== 'object') return null;
  return result;
});
const onAfterSelectDevice = (device: NdmDeviceResultVO) => {
  lowerDevice.value = device;
};

const abortController = ref<AbortController>(new AbortController());

const { mutate: linkPortToDevice, isPending: linking } = useMutation({
  mutationFn: async () => {
    abortController.value.abort();
    abortController.value = new AbortController();

    window.$loadingBar.start();

    const upperDeviceType = tryGetDeviceType(ndmDevice.value.deviceType);
    if (!upperDeviceType) throw new Error('本设备类型未知');
    const upperDeviceDbId = ndmDevice.value.id;
    if (!upperDeviceDbId) throw new Error('本设备没有ID');

    if (circuitIndex.value === undefined) throw new Error('该电路不存在');

    if (!lowerDevice.value) throw new Error('请选择要关联的设备');
    const lowerDeviceType = tryGetDeviceType(lowerDevice.value?.deviceType);
    if (!lowerDeviceType) throw new Error('该设备类型未知');
    const lowerDeviceDbId = lowerDevice.value?.id;
    if (!lowerDeviceDbId) throw new Error('该设备没有ID');

    // 0. 检查是否会导致循环关联，
    if (upperDeviceDbId === lowerDeviceDbId) throw new Error('不能关联到自身');
    // 以及检查上游设备的linkDescription的downstream字段是否存在某个端口已经关联下游设备
    const duplicated = Object.entries(upperDeviceLinkDescription.value?.downstream ?? {}).find(([, deviceStoreIndex]) => {
      return deviceStoreIndex.deviceDbId === lowerDeviceDbId;
    });
    if (duplicated) {
      const [portName] = duplicated;
      throw new Error(`该设备已关联到端口${portName}`);
    }

    // 1. 修改上游设备的linkDescription的downstream字段
    const modifiedUpperDevice = cloneDeep(ndmDevice.value);
    let modifiedUpperDeviceLinkDescription: NdmSecurityBoxLinkDescription;
    if (!upperDeviceLinkDescription.value) {
      modifiedUpperDeviceLinkDescription = {
        downstream: {
          [circuitIndex.value]: {
            stationCode: station.value.code,
            deviceType: lowerDeviceType,
            deviceDbId: lowerDeviceDbId,
          },
        },
      };
    } else {
      modifiedUpperDeviceLinkDescription = {
        ...upperDeviceLinkDescription.value,
        downstream: {
          ...upperDeviceLinkDescription.value.downstream,
          [circuitIndex.value]: {
            stationCode: station.value.code,
            deviceType: lowerDeviceType,
            deviceDbId: lowerDeviceDbId,
          },
        },
      };
    }
    modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperDeviceLinkDescription);

    // 2. 修改下游设备的linkDescription的upstream字段
    const modifiedLowerDevice = cloneDeep(lowerDevice.value);
    let modifiedLowerDeviceLinkDescription: NdmCameraLinkDescription;
    if (!lowerDeviceLinkDescription.value) {
      modifiedLowerDeviceLinkDescription = {
        upstream: [
          {
            stationCode: station.value.code,
            deviceType: upperDeviceType,
            deviceDbId: upperDeviceDbId,
          },
        ],
      };
    } else {
      const upstream = cloneDeep(lowerDeviceLinkDescription.value.upstream);
      if (!upstream) {
        modifiedLowerDeviceLinkDescription = {
          ...lowerDeviceLinkDescription.value,
          upstream: [
            {
              stationCode: station.value.code,
              deviceType: upperDeviceType,
              deviceDbId: upperDeviceDbId,
            },
          ],
        };
      } else {
        const deviceStoreIndex = upstream.find((deviceStoreIndex) => deviceStoreIndex.deviceDbId === upperDeviceDbId);
        if (!deviceStoreIndex) {
          upstream.push({
            stationCode: station.value.code,
            deviceType: upperDeviceType,
            deviceDbId: upperDeviceDbId,
          });
        }
        modifiedLowerDeviceLinkDescription = {
          ...lowerDeviceLinkDescription.value,
          upstream,
        };
      }
    }
    modifiedLowerDevice.linkDescription = JSON.stringify(modifiedLowerDeviceLinkDescription);

    // 3. 发起update请求并获取最新的设备详情（使用本地数据库时直接修改本地数据）
    if (useLocalDB.value) {
      return { upperDevice: modifiedUpperDevice, lowerDevice: modifiedLowerDevice };
    }
    const stationCode = station.value.code;
    const signal = abortController.value.signal;
    await updateDeviceApi(modifiedUpperDevice, { stationCode, signal });
    await updateDeviceApi(modifiedLowerDevice, { stationCode, signal });
    const latestUpperDevice = await detailDeviceApi(modifiedUpperDevice, { stationCode, signal });
    const latestLowerDevice = await detailDeviceApi(modifiedLowerDevice, { stationCode, signal });
    return { upperDevice: latestUpperDevice, lowerDevice: latestLowerDevice };
  },
  onSuccess: ({ upperDevice, lowerDevice }) => {
    show.value = false;
    window.$loadingBar.finish();
    window.$message.success('关联成功');
    if (!!upperDevice && !!lowerDevice) {
      deviceStore.patchDevice(station.value.code, { ...upperDevice });
      deviceStore.patchDevice(station.value.code, { ...lowerDevice });
    }
  },
  onError: (error) => {
    window.$loadingBar.error();
    if (isCancel(error)) return;
    console.error(error);
    const errorFeedback = parseErrorFeedback(error);
    window.$message.error(errorFeedback);
  },
});

const onLink = () => {
  linkPortToDevice();
};

const onCancel = () => {
  abortController.value.abort();
  show.value = false;
};
</script>

<template>
  <NModal v-model:show="show" preset="card" style="width: 600px; height: 600px" :content-style="{ height: '100%', overflow: 'hidden' }" @close="onCancel" @esc="onCancel">
    <template #header>
      <span>{{ ndmDevice.name }}</span>
      <span> - </span>
      <span>电路{{ circuitIndex ? circuitIndex + 1 : '-' }}</span>
      <span> - </span>
      <span>关联设备</span>
    </template>
    <template #default>
      <DeviceTree :station="station" :events="['select']" :device-prefix-label="'选择'" @after-select-device="onAfterSelectDevice" />
    </template>
    <template #action>
      <NFlex justify="end">
        <NButton size="small" quaternary @click="onCancel">取消</NButton>
        <NButton size="small" type="primary" :disabled="!lowerDevice" :loading="linking" @click="onLink">关联</NButton>
      </NFlex>
    </template>
  </NModal>
</template>

<style scoped lang="scss"></style>
