<script setup lang="ts">
import {
  detailDeviceApi,
  probeDeviceApi,
  rebootSecurityBoxApi,
  turnCitcuitStatusApi,
  updateDeviceApi,
  type LinkDescription,
  type NdmDeviceResultVO,
  type NdmSecurityBoxCircuit,
  type NdmSecurityBoxLinkDescription,
  type NdmSecurityBoxResultVO,
  type Station,
} from '@/apis';
import { SecurityBoxCircuitLinkModal } from '@/components';
import { usePermission } from '@/composables';
import { SELECT_DEVICE_FN_INJECTION_KEY } from '@/constants';
import { PERMISSION_TYPE_LITERALS } from '@/enums';
import { useDeviceStore, useSettingStore } from '@/stores';
import { parseErrorFeedback } from '@/utils';
import { useMutation } from '@tanstack/vue-query';
import { watchImmediate } from '@vueuse/core';
import { isCancel } from 'axios';
import destr from 'destr';
import { cloneDeep, isFunction } from 'es-toolkit';
import { PowerIcon } from 'lucide-vue-next';
import { NButton, NCard, NDescriptions, NDescriptionsItem, NDropdown, NFlex, NIcon, NPopconfirm, NPopover, NSwitch, NTag, useThemeVars, type DropdownOption, type TagProps } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, inject, ref, toRefs } from 'vue';

const props = defineProps<{
  ndmDevice: NdmSecurityBoxResultVO;
  station: Station;
  circuits?: NdmSecurityBoxCircuit[];
}>();

const themeVars = useThemeVars();

const deviceStore = useDeviceStore();
const { lineDevices } = storeToRefs(deviceStore);

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

const { hasPermission } = usePermission();

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

const showCard = computed(() => !!circuits.value && circuits.value.length > 0);

const localCircuits = ref<NdmSecurityBoxCircuit[]>([]);

watchImmediate(circuits, (newCircuits) => {
  localCircuits.value = newCircuits?.map((circuit) => ({ ...circuit })) ?? [];
});

const getCircuitStatusTagType = (circuit: NdmSecurityBoxCircuit): TagProps['type'] => {
  const { status } = circuit;
  return status === 0 ? 'error' : status === 1 ? 'success' : 'warning';
};

const getCircuitStatusText = (circuit: NdmSecurityBoxCircuit) => {
  const { status } = circuit;
  return status === 0 ? '关闭' : status === 1 ? '开启' : '-';
};

const getCircuitStatusClassName = (circuit: NdmSecurityBoxCircuit) => {
  const { status } = circuit;
  return status === 0 ? 'circuit-off' : status === 1 ? 'circuit-on' : 'circuit-unknown';
};

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

const { mutate: turnStatus, isPending: turning } = useMutation({
  mutationFn: async (params: { circuitIndex: number; newStatus: boolean }) => {
    abortController.value.abort();
    abortController.value = new AbortController();

    window.$loadingBar.start();

    const { circuitIndex, newStatus } = params;
    if (!ndmDevice.value.ipAddress) {
      throw new Error('设备IP地址不存在');
    }
    const status = newStatus ? 1 : 0;
    const stationCode = station.value.code;
    const signal = abortController.value.signal;
    await turnCitcuitStatusApi(ndmDevice.value.ipAddress, circuitIndex, status, { stationCode, signal });
    await probeDeviceApi(ndmDevice.value, { stationCode, signal });
    return status;
  },
  onSuccess: (status, { circuitIndex }) => {
    window.$loadingBar.finish();
    const circuit = localCircuits.value.at(circuitIndex);
    if (circuit) circuit.status = status;
  },
  onError: (error) => {
    window.$loadingBar.error();
    if (isCancel(error)) return;
    console.error(error);
    const errorFeedback = parseErrorFeedback(error);
    window.$message.error(errorFeedback);
  },
});

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

    window.$loadingBar.start();

    if (!ndmDevice.value.ipAddress) {
      throw new Error('设备IP地址不存在');
    }

    const stationCode = station.value.code;
    const signal = abortController.value.signal;
    await rebootSecurityBoxApi(ndmDevice.value.ipAddress, { stationCode, signal });
  },
  onSuccess: () => {
    window.$loadingBar.finish();
    window.$message.success('设备重启成功');
  },
  onError: (error) => {
    window.$loadingBar.error();
    if (isCancel(error)) return;
    console.error(error);
    const errorFeedback = parseErrorFeedback(error);
    window.$message.error(errorFeedback);
  },
});

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 getLowerDeviceByCircuitIndex = (circuitIndex: number) => {
  if (!upperDeviceLinkDescription.value) return null;
  const downstream = upperDeviceLinkDescription.value.downstream;
  if (!downstream) return null;
  const deviceStoreIndex = downstream[circuitIndex];
  if (!deviceStoreIndex) return null;
  const { stationCode, deviceType, deviceDbId } = deviceStoreIndex;
  const stationDevices = lineDevices.value[stationCode];
  if (!stationDevices) return null;
  const devices = stationDevices[deviceType];
  const lowerDevice = devices.find((device) => device.id === deviceDbId);
  if (!lowerDevice) {
    // 下游设备不存在时解除关联
    const modifiedUpperDevice = cloneDeep(ndmDevice.value);
    const modifiedUpperLinkDescription = cloneDeep(upperDeviceLinkDescription.value);
    delete modifiedUpperLinkDescription.downstream?.[circuitIndex];
    modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperLinkDescription);
    // 不需要等待异步
    const stationCode = station.value.code;
    updateDeviceApi(modifiedUpperDevice, { stationCode }).then(() => {
      detailDeviceApi(modifiedUpperDevice, { stationCode }).then((upperDevice) => {
        if (!upperDevice) return;
        deviceStore.patchDevice(stationCode, { ...upperDevice });
      });
    });
    return null;
  }
  return lowerDevice;
};

// 获取从父组件注入的selectDevice函数
const selectDeviceFn = inject(SELECT_DEVICE_FN_INJECTION_KEY);
// 跳转到下游设备
const navigateToLowerDevice = (circuitIndex: number) => {
  const lowerDevice = getLowerDeviceByCircuitIndex(circuitIndex);
  if (!lowerDevice) return;
  if (!!selectDeviceFn && !!selectDeviceFn.value) {
    selectDeviceFn.value(lowerDevice, station.value.code);
  }
};

const showModal = ref(false);
const contextmenu = ref<{ x: number; y: number; circuitIndex?: number }>({ x: 0, y: 0 });
const showContextmenu = ref(false);
const contextmenuOptions = computed<DropdownOption[]>(() => [
  {
    label: '关联设备',
    key: 'link-device',
    onSelect: () => {
      showContextmenu.value = false;
      showModal.value = true;
    },
  },
  {
    label: '解除关联',
    key: 'unlink-device',
    onSelect: () => {
      showContextmenu.value = false;
      const circuitIndex = contextmenu.value.circuitIndex;
      if (circuitIndex === undefined) return;
      const lowerDevice = getLowerDeviceByCircuitIndex(circuitIndex);
      if (!lowerDevice) return;
      window.$dialog.warning({
        title: '确认解除关联吗？',
        content: `将解除【电路${circuitIndex + 1}】与【${lowerDevice.name}】的关联关系。`,
        style: { width: '600px' },
        contentStyle: { height: '60px' },
        negativeText: '取消',
        positiveText: '确认',
        onNegativeClick: () => {
          window.$dialog.destroyAll();
        },
        onPositiveClick: () => {
          window.$dialog.destroyAll();
          unlinkDevice({ circuitIndex, lowerDevice });
        },
      });
    },
  },
]);
const onSelectDropdownOption = (key: string, option: DropdownOption) => {
  const onSelect = option['onSelect'];
  if (isFunction(onSelect)) {
    onSelect();
  }
};
const onContextmenu = (payload: PointerEvent, circuitIndex: number) => {
  payload.stopPropagation();
  payload.preventDefault();
  if (!hasPermission(station.value.code, PERMISSION_TYPE_LITERALS.OPERATION)) return;
  const { clientX, clientY } = payload;
  contextmenu.value = { x: clientX, y: clientY, circuitIndex };
  showContextmenu.value = true;
};

const { mutate: unlinkDevice } = useMutation({
  mutationFn: async (params: { circuitIndex: number; lowerDevice: NdmDeviceResultVO }) => {
    abortController.value.abort();
    abortController.value = new AbortController();

    window.$loadingBar.start();

    const { circuitIndex, lowerDevice } = params;

    if (!upperDeviceLinkDescription.value) return;

    // 1.从下游设备的linkDescription的upstream字段中删除当前上游设备
    const modifiedLowerDevice = cloneDeep(lowerDevice);
    // 解除关联时下游设备的linkDescription一定存在
    const modifiedLowerDeviceLinkDescription = destr<LinkDescription>(modifiedLowerDevice.linkDescription);
    // upstream字段存在时才进行删除操作
    if (modifiedLowerDeviceLinkDescription.upstream) {
      const index = modifiedLowerDeviceLinkDescription.upstream.findIndex((deviceStoreIndex) => deviceStoreIndex.deviceDbId === ndmDevice.value.id);
      if (index !== -1) {
        modifiedLowerDeviceLinkDescription.upstream.splice(index, 1);
      }
    }
    modifiedLowerDevice.linkDescription = JSON.stringify(modifiedLowerDeviceLinkDescription);

    // 2. 修改上游设备的linkDescription的downstream字段
    const modifiedUpperDevice = cloneDeep(ndmDevice.value);
    const modifiedUpperLinkDescription = cloneDeep(upperDeviceLinkDescription.value);
    delete modifiedUpperLinkDescription.downstream?.[circuitIndex];
    modifiedUpperDevice.linkDescription = JSON.stringify(modifiedUpperLinkDescription);

    // 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: (data) => {
    window.$loadingBar.finish();
    window.$message.success('解除成功');
    if (!data) return;
    const { upperDevice, lowerDevice } = data;
    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);
  },
});
</script>

<template>
  <NCard v-if="showCard" hoverable size="small">
    <template #header>
      <NFlex align="center">
        <span>电路状态</span>
        <NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => reboot()">
          <template #trigger>
            <NButton secondary size="small" :loading="rebooting">重合闸</NButton>
          </template>
          <span>确定要执行重合闸操作吗？</span>
        </NPopconfirm>
      </NFlex>
    </template>
    <template #default>
      <div style="display: grid" :style="{ 'grid-template-columns': `repeat(${Math.min(localCircuits.length, 4)}, 1fr)` }">
        <template v-for="(circuit, circuitIndex) in localCircuits" :key="circuitIndex">
          <NPopover :delay="300">
            <template #trigger>
              <div style="display: flex; justify-content: center; align-items: center" @contextmenu="(payload) => onContextmenu(payload, circuitIndex)">
                <NFlex vertical class="pointer-cursor circuit" style="padding: 12px" :class="getCircuitStatusClassName(circuit)">
                  <NFlex align="center">
                    <NTag class="pointer-cursor" size="small" :type="getCircuitStatusTagType(circuit)">
                      <template #icon>
                        <NIcon :component="PowerIcon" />
                      </template>
                      <template #default>
                        <span>{{ getCircuitStatusText(circuit) }}</span>
                      </template>
                    </NTag>
                    <span>电路{{ circuitIndex + 1 }}</span>
                  </NFlex>
                  <NFlex justify="end" align="center">
                    <NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => turnStatus({ circuitIndex: circuitIndex, newStatus: circuit.status !== 1 })">
                      <template #trigger>
                        <NSwitch size="small" :value="circuit.status === 1" :loading="turning" />
                      </template>
                      <template #default>
                        <span>确定要{{ circuit.status === 1 ? '关闭' : '开启' }}电路{{ circuitIndex + 1 }}吗？</span>
                      </template>
                    </NPopconfirm>
                  </NFlex>
                </NFlex>
              </div>
            </template>
            <template #default>
              <NDescriptions bordered size="small" label-placement="left" :column="1">
                <NDescriptionsItem label="电压">{{ circuit.voltage }}V</NDescriptionsItem>
                <NDescriptionsItem label="电流">{{ circuit.current }}A</NDescriptionsItem>
                <NDescriptionsItem label="关联设备">
                  <span v-if="getLowerDeviceByCircuitIndex(circuitIndex)" style="text-decoration: underline; cursor: pointer" @click="() => navigateToLowerDevice(circuitIndex)">
                    {{ getLowerDeviceByCircuitIndex(circuitIndex)?.name || '-' }}
                  </span>
                  <span v-else>-</span>
                </NDescriptionsItem>
              </NDescriptions>
            </template>
          </NPopover>
        </template>
      </div>
    </template>
  </NCard>

  <SecurityBoxCircuitLinkModal v-model:show="showModal" :ndm-device="ndmDevice" :station="station" :circuit-index="contextmenu.circuitIndex" />

  <NDropdown
    placement="bottom-start"
    trigger="manual"
    :show="showContextmenu"
    :x="contextmenu.x"
    :y="contextmenu.y"
    :options="contextmenuOptions"
    @select="onSelectDropdownOption"
    @clickoutside="() => (showContextmenu = false)"
  />
</template>

<style scoped lang="scss">
.pointer-cursor {
  cursor: pointer;
}

.circuit {
  &.circuit-on {
    &:hover {
      background-color: #18a05816;
    }
  }

  &.circuit-off {
    &:hover {
      background-color: #d0305016;
    }
  }

  &.circuit-unknown {
    &:hover {
      background-color: #f0a02016;
    }
  }

  &:hover {
    transform: translateY(-2px);
    transition: transform 0.2s ease-in-out;
    box-shadow: v-bind('themeVars.boxShadow1');
  }
}
</style>
