<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { Select, Divider, Row } from 'ant-design-vue';
import { ChevronLeft, ChevronRight } from '@vben/icons';
import { postTreeModelDevicePatchListAsync } from '#/api-client';
import { $t } from '#/locales';
import { useDebounceFn } from '@vueuse/core';

interface Props {
  value?: string;
  placeholder?: string;
  disabled?: boolean;
  allowClear?: boolean;
  ioTDataType?: string;
}

const props = withDefaults(defineProps<Props>(), {
  placeholder: $t('common.pleaseSelect') + $t('abp.log.deviceInfo'),
  disabled: false,
  allowClear: true,
  ioTDataType: '',
});

const emit = defineEmits<{
  'update:value': [string];
  change: [string];
  'device-change': [any];
}>();

const VNodes = (_, { attrs }: any) => {
  return attrs.vnodes;
};

const options = ref<any[]>([]);
const query = ref({
  pageIndex: 1,
  pageSize: 10,
  deviceAddress: '', // 搜索关键词
});
const total = ref(0);
const loading = ref<boolean>(false);
const searchInput = ref<string>('');
// 预设加载（如从设备列表跳转带入地址）后，自动选中返回列表的第一个元素并对外抛出，
// 以便父级表单以真实设备标识触发查询；仅由预设/初始路径置位，手动搜索/翻页不置位。
const pendingAutoSelectFirst = ref<boolean>(false);
// 选中值时 antd Select 会清空搜索框并触发一次 @search('')，用此标志抑制该次“空搜索”，
// 避免清掉当前地址过滤、导致 DevicePatchListAsync 丢失设备地址并冲掉已选项。
const suppressEmptySearchOnce = ref<boolean>(false);

// 最大页码
const maxPage = computed(() => {
  return Math.ceil(total.value / query.value.pageSize);
});

/**
 * 获取设备列表数据
 */
const fetchData = async () => {
  if (!props.ioTDataType) {
    options.value = [];
    total.value = 0;
    return;
  }

  loading.value = true;
  try {
    const { data } = await postTreeModelDevicePatchListAsync({
      body: {
        pageIndex: query.value.pageIndex,
        pageSize: query.value.pageSize,
        ioTDataType: props.ioTDataType,
        deviceAddress: query.value.deviceAddress || undefined,
      },
    });

    if (data?.items) {
      options.value = data.items.map((item: any) => ({
        label: item.Device,
        value: item.Device,
        ...item,
      }));
      total.value = data.totalCount || 0;

      // 预设加载后自动选中第一个：过滤返回的设备标识（item.Device）通常与传入的原始地址不完全相等，
      // 若不主动选中，值匹配不到选项、表格也无法以真实标识发起查询。
      if (pendingAutoSelectFirst.value) {
        pendingAutoSelectFirst.value = false;
        if (options.value.length > 0) {
          const first = options.value[0];
          // 设置值会触发 antd 的空搜索，抑制之，避免清掉刚加载的地址过滤与选项
          suppressEmptySearchOnce.value = true;
          if (props.value !== first.value) {
            emit('update:value', first.value);
            emit('change', first.value);
          }
          emit('device-change', first);
        }
      }
    }
    loading.value = false;
  } catch (error) {
    console.error('获取设备列表失败:', error);
    loading.value = false;
  }
};

// ioTDataType 与 props.value 两个 immediate 侦听在初值到达时序错开时会各调一次 fetchData，
// 导致 DevicePatchListAsync 重复请求。用防抖把相邻触发合并为一次，执行时读取最终 query 状态。
const scheduleFetch = useDebounceFn(() => fetchData(), 100);

/**
 * 处理搜索输入
 */
const handleSearch = useDebounceFn((value: string) => {
  // 抑制“选中即清空搜索”引发的空搜索，避免清掉地址过滤、丢失设备地址
  if (!value && suppressEmptySearchOnce.value) {
    suppressEmptySearchOnce.value = false;
    return;
  }
  query.value.deviceAddress = value;
  query.value.pageIndex = 1; // 搜索时重置到第一页
  fetchData();
}, 300);

/**
 * 上下页
 * @param type 1: 下一页, 0: 上一页
 */
const changePage = (type: number) => {
  if (type === 1) {
    if (query.value.pageIndex >= maxPage.value) return;
    query.value.pageIndex += 1;
    fetchData();
  } else {
    if (query.value.pageIndex <= 1) return;
    query.value.pageIndex -= 1;
    fetchData();
  }
};

// 监听 ioTDataType 变化，重新加载数据
watch(
  () => props.ioTDataType,
  (newVal) => {
    query.value.pageIndex = 1;
    // 有预设值（如从设备列表跳转带入的设备地址）时按其过滤加载，确保目标设备出现在选项，
    // 并在加载完成后自动选中第一个；否则按空关键词加载第一页
    query.value.deviceAddress = props.value || '';
    searchInput.value = '';
    pendingAutoSelectFirst.value = !!props.value;
    if (newVal) {
      scheduleFetch();
    } else {
      options.value = [];
      total.value = 0;
    }
  },
  { immediate: true },
);

// 监听预设值（如从设备列表跳转带入的设备地址）：当地址在挂载后才到达时，
// 兜底把该地址作为过滤参数传给 DevicePatchListAsync，确保目标设备被查出并回显。
// 防重复：已按该地址加载或该值已在选项中则跳过。
watch(
  () => props.value,
  (newVal) => {
    if (!newVal || !props.ioTDataType) {
      return;
    }
    if (query.value.deviceAddress === newVal) {
      return;
    }
    if (options.value.some((option) => option.value === newVal)) {
      return;
    }
    query.value.pageIndex = 1;
    query.value.deviceAddress = newVal;
    pendingAutoSelectFirst.value = true;
    scheduleFetch();
  },
  { immediate: true },
);

// 处理值变化
const handleValueChange = (value: string) => {
  // 选中值会触发 antd 的空搜索，抑制之，避免清掉当前地址过滤
  if (value) {
    suppressEmptySearchOnce.value = true;
  }
  emit('update:value', value);
  emit('change', value);

  if (value) {
    const selectedDevice = options.value.find((option) => option.value === value);
    if (selectedDevice) {
      emit('device-change', selectedDevice);
    }
  } else {
    emit('device-change', null);
  }
};

// 暴露方法给父组件调用
defineExpose({
  getSelectedDevice: () => {
    if (props.value) {
      return options.value.find((option) => option.value === props.value);
    }
    return null;
  },
});
</script>

<template>
  <Select
    :value="value"
    :showSearch="true"
    :options="options"
    :placeholder="placeholder"
    :filter-option="false"
    :disabled="disabled || !ioTDataType"
    :allow-clear="allowClear"
    :loading="loading"
    :search-value="searchInput"
    @change="handleValueChange"
    @search="handleSearch"
  >
    <template #dropdownRender="{ menuNode: menu }">
      <v-nodes :vnodes="menu" />
      <Divider style="margin: 4px 0" />
      <div @mousedown="(e) => e.preventDefault()">
        <Row type="flex" justify="space-around" align="middle">
          <ChevronLeft
            @click="changePage(0)"
            :class="{ 'text-gray-400': query.pageIndex <= 1 }"
            style="cursor: pointer; width: 16px; height: 16px"
          />
          <div>{{ `${query.pageIndex}/${maxPage}` }}</div>
          <ChevronRight
            @click="changePage(1)"
            :class="{ 'text-gray-400': query.pageIndex >= maxPage }"
            style="cursor: pointer; width: 16px; height: 16px"
          />
        </Row>
      </div>
    </template>
  </Select>
</template>

<style lang="less" scoped>
.text-gray-400 {
  color: #9ca3af;
  cursor: not-allowed !important;
}
</style>
