<script lang="ts" setup>
/**
 * 药品搜索选择组件（自定义实现）
 *
 * @description 自定义药品搜索下拉选择器，不依赖antd的Select组件
 *              - 支持中药/西药类型切换
 *              - 下拉选项展示：药品图片、名称、供应商、规格、价格
 *              - 选择时返回完整药品数据（包含默认用法字段）
 *              - isCard：单选确认场景（如仓绑药）选中后以内嵌卡片展示已选药品
 * @author 系统
 * @date 2024
 */
import { ref, watch, onMounted, onUnmounted } from 'vue';

import { SearchOutlined, LoadingOutlined } from '@ant-design/icons-vue';
import { Button, Input, Spin } from 'ant-design-vue';
import { debounce } from 'lodash-es';

import { getProductListDoctorReception } from '#/views/doctor/doctor-reception/api';

/** 无图时占位（中药/西药等统一兜底，避免仓绑药下拉只剩文字） */
const DRUG_PLACEHOLDER_IMAGE =
  'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250510/cd470ebf97e31c4048ba502ba71b66a3.jpg';

// ==================== Props 定义 ====================

interface Props {
  /**
   * 药品类型
   * 1-中药，2-西药
   */
  type?: number;
  /**
   * 多药品类型（优先于 type；有值时支持空关键词拉全量）
   * 例如 [2,4] 表示西药+中成药
   */
  types?: number[];
  /**
   * 占位提示文本
   */
  placeholder?: string;
  /**
   * 诊所ID（接诊/开方必传；仓药绑定等平台场景可省略，默认 0 走主库 types 检索）
   */
  storeId?: number;
  /**
   * 挂号ID（用于取价门店与毛利率权限）
   */
  registerId?: number;
  /**
   * 是否禁用
   */
  disabled?: boolean;
  /**
   * 是否以卡片展示已选药品（仓绑药等单选确认场景；开方默认 false）
   */
  isCard?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  type: 1,
  types: () => [],
  placeholder: '输入药品名称搜索',
  // 默认 0：平台无门店；诊所场景由调用方传 :store-id
  storeId: 0,
  registerId: undefined,
  disabled: false,
  isCard: false,
});

// ==================== Emits 定义 ====================

const emit = defineEmits<{
  /**
   * 选中药品时触发
   * @param drug 完整的药品数据
   */
  (e: 'select', drug: any): void;
}>();

// ==================== 响应式数据 ====================

/**
 * 搜索关键词
 */
const searchKeyword = ref('');

/**
 * 是否正在搜索
 */
const isSearching = ref(false);

/**
 * 搜索结果列表
 */
const searchResults = ref<any[]>([]);

/**
 * 是否显示下拉列表
 */
const showDropdown = ref(false);

/**
 * 组件容器引用
 */
const containerRef = ref<HTMLElement | null>(null);

/**
 * 当前高亮的选项索引（用于键盘导航）
 */
const highlightIndex = ref(-1);

/**
 * isCard 模式下已选药品（用于输入框下方卡片）
 */
const selectedDrug = ref<any | null>(null);

/**
 * 搜索框引用，便于「重新选择」时聚焦
 */
const inputRef = ref<{ focus?: () => void } | null>(null);

/**
 * 解析下拉展示图：优先真图，无图用统一占位
 */
function resolveDropdownImage(item: any): string {
  return item._image || DRUG_PLACEHOLDER_IMAGE;
}

// ==================== 方法定义 ====================

/**
 * 搜索药品
 * @param keyword 搜索关键词
 * @description 调用后端API搜索药品，支持按名称和拼音搜索；
 *              传入 types 时允许空关键词拉取该类型全量列表（用于下拉展开）
 */
const searchDrugs = debounce(async (keyword: string) => {
  // 单类型模式：空关键词清空；多类型模式：空关键词仍请求后端拉全量
  if ((!keyword || keyword.length < 1) && props.types.length === 0) {
    searchResults.value = [];
    showDropdown.value = false;
    return;
  }

  isSearching.value = true;
  showDropdown.value = true;

  try {
    // types 优先：有多类型时只传 types，不传 type，避免后端被单 type 覆盖
    const typeParams =
      props.types.length > 0
        ? { types: props.types }
        : { type: props.type };
    const res = await getProductListDoctorReception({
      name: keyword || '',
      ...typeParams,
      store_id: props.storeId,
      ...(props.registerId ? { register_id: props.registerId } : {}),
    });

    // 处理返回数据（兼容门店 relation 与平台主库打平结构）
    if (Array.isArray(res)) {
      searchResults.value = res.map((item: any) => {
        const drug = item.drug || {};
        return {
          ...item,
          _id: item.id,
          _drugId: item.drug_id || drug.id || item.id,
          _drugName: drug.drug_name || item.drug_name || '',
          _image: drug.image || item.image || '',
          _specification: drug.specification || item.specification || '',
          _supplier: drug.supplier?.name || item.supplier?.name || '',
          _price: Number(item.price ?? 0),
          // 已绑定配送仓：列表展示「多仓」标识
          _hasDeliveryWarehouse: Number(item.has_delivery_warehouse ?? 0) === 1,
          _timeId: drug.time_id || 0,
          _typeId: drug.type_id || 0,
          _frequencyId: drug.frequency_id || 0,
          _unitId: drug.unit_id || 0,
          _number: drug.number || 1,
          _useNum: drug.useNum,
          _useType: drug.useType,
          _useFrequency: drug.useFrequency,
        };
      });
    } else {
      searchResults.value = [];
    }
  } catch (error) {
    console.error('搜索药品失败:', error);
    searchResults.value = [];
  } finally {
    isSearching.value = false;
  }
}, 300);

/**
 * 处理输入变化
 * @param e 输入事件
 */
function handleInput(e: Event) {
  const target = e.target as HTMLInputElement;
  searchKeyword.value = target.value;
  highlightIndex.value = -1;
  searchDrugs(target.value);
}

/**
 * 处理输入框获得焦点
 * @description 多类型模式聚焦即拉取列表并展开；单类型模式仅在已有结果时展示下拉
 */
function handleFocus() {
  if (props.types.length > 0) {
    searchDrugs('');
    showDropdown.value = true;
    return;
  }
  if (searchResults.value.length > 0) {
    showDropdown.value = true;
  }
}

/**
 * 处理选中药品
 * @param drug 选中的药品数据
 * @description isCard 时保留 selectedDrug 用于卡片展示；否则清空选中态
 */
function handleSelectDrug(drug: any) {
  emit('select', drug);
  searchKeyword.value = '';
  searchResults.value = [];
  showDropdown.value = false;
  highlightIndex.value = -1;
  if (props.isCard) {
    selectedDrug.value = drug;
  } else {
    selectedDrug.value = null;
  }
}

/**
 * 清空已选卡片并重新搜索（仅 isCard）
 * 同步 emit null，便于父级清空 drug_id
 */
function handleReselect() {
  selectedDrug.value = null;
  searchKeyword.value = '';
  searchResults.value = [];
  showDropdown.value = false;
  highlightIndex.value = -1;
  emit('select', null);
  // 下一帧聚焦搜索框，方便重新选药
  setTimeout(() => {
    inputRef.value?.focus?.();
  }, 0);
}

/**
 * 处理键盘事件
 * @param e 键盘事件
 */
function handleKeydown(e: KeyboardEvent) {
  if (!showDropdown.value || searchResults.value.length === 0) return;

  switch (e.key) {
    case 'ArrowDown':
      e.preventDefault();
      highlightIndex.value = Math.min(
        highlightIndex.value + 1,
        searchResults.value.length - 1,
      );
      break;
    case 'ArrowUp':
      e.preventDefault();
      highlightIndex.value = Math.max(highlightIndex.value - 1, 0);
      break;
    case 'Enter':
      e.preventDefault();
      if (highlightIndex.value >= 0) {
        handleSelectDrug(searchResults.value[highlightIndex.value]);
      }
      break;
    case 'Escape':
      showDropdown.value = false;
      highlightIndex.value = -1;
      break;
  }
}

/**
 * 处理点击外部关闭下拉
 * @param e 点击事件
 */
function handleClickOutside(e: MouseEvent) {
  if (containerRef.value && !containerRef.value.contains(e.target as Node)) {
    showDropdown.value = false;
    highlightIndex.value = -1;
  }
}

// ==================== 生命周期 ====================

onMounted(() => {
  document.addEventListener('click', handleClickOutside);
});

onUnmounted(() => {
  document.removeEventListener('click', handleClickOutside);
});

// ==================== 监听 ====================

/**
 * 监听类型变化，清空搜索结果
 */
watch(
  () => props.type,
  () => {
    searchResults.value = [];
    showDropdown.value = false;
    searchKeyword.value = '';
    selectedDrug.value = null;
  },
);
</script>

<template>
  <div ref="containerRef" class="drug-search-select">
    <!-- 搜索输入框 -->
    <Input
      ref="inputRef"
      v-model:value="searchKeyword"
      :placeholder="placeholder"
      :disabled="disabled"
      allow-clear
      @input="handleInput"
      @focus="handleFocus"
      @keydown="handleKeydown"
    >
      <template #prefix>
        <LoadingOutlined v-if="isSearching" class="text-gray-400" />
        <SearchOutlined v-else class="text-gray-400" />
      </template>
    </Input>

    <!-- isCard：已选药品卡片（与下拉项同结构，带边框区分） -->
    <div
      v-if="isCard && selectedDrug"
      class="drug-item drug-item--selected-card"
    >
      <div class="drug-item__image">
        <img
          :src="resolveDropdownImage(selectedDrug)"
          alt=""
          class="drug-item__img"
          @error="
            (e) => ((e.target as HTMLImageElement).src = DRUG_PLACEHOLDER_IMAGE)
          "
        />
      </div>
      <div class="drug-item__info">
        <div class="drug-item__name">{{ selectedDrug._drugName }}</div>
        <div class="drug-item__meta">
          <span class="drug-item__id">ID：{{ selectedDrug._drugId }}</span>
          <span v-if="selectedDrug._specification" class="drug-item__spec">
            规格：{{ selectedDrug._specification }}
          </span>
          <span v-if="selectedDrug._supplier" class="drug-item__supplier">
            供应商：{{ selectedDrug._supplier }}
          </span>
        </div>
      </div>
      <div class="drug-item__price">
        <template v-if="Number(selectedDrug._price) > 0">
          ¥{{ Number(selectedDrug._price).toFixed(2) }}
        </template>
        <span v-else class="drug-item__price--empty">暂无</span>
      </div>
      <Button
        type="link"
        size="small"
        class="drug-item__reselect"
        @click="handleReselect"
      >
        重新选择
      </Button>
    </div>

    <!-- 下拉列表 -->
    <div v-if="showDropdown" class="drug-dropdown">
      <!-- 加载中 -->
      <div v-if="isSearching" class="drug-dropdown__loading">
        <Spin size="small" />
        <span>搜索中...</span>
      </div>

      <!-- 无结果 -->
      <div
        v-else-if="searchResults.length === 0"
        class="drug-dropdown__empty"
      >
        暂无匹配药品
      </div>

      <!-- 结果列表 -->
      <div v-else class="drug-dropdown__list">
        <div
          v-for="(item, index) in searchResults"
          :key="item._id"
          class="drug-item"
          :class="{ 'drug-item--active': index === highlightIndex }"
          @click="handleSelectDrug(item)"
          @mouseenter="highlightIndex = index"
        >
          <!-- 药品图片 -->
          <div class="drug-item__image">
            <img
              v-if="resolveDropdownImage(item)"
              :src="resolveDropdownImage(item)"
              alt=""
              class="drug-item__img"
              @error="(e) => (e.target as HTMLImageElement).style.display = 'none'"
            />
            <div v-else class="drug-item__img-placeholder">无图</div>
          </div>

          <!-- 药品信息：药名 / 多仓 / ID / 规格 / 供应商 -->
          <div class="drug-item__info">
            <div class="drug-item__name-row">
              <div class="drug-item__name">{{ item._drugName }}</div>
              <span v-if="item._hasDeliveryWarehouse" class="drug-item__multi-wh">多仓</span>
            </div>
            <div class="drug-item__meta">
              <span class="drug-item__id">ID：{{ item._drugId }}</span>
              <span v-if="item._specification" class="drug-item__spec">
                规格：{{ item._specification }}
              </span>
              <span v-if="item._supplier" class="drug-item__supplier">
                供应商：{{ item._supplier }}
              </span>
            </div>
          </div>

          <!-- 价格：无价（平台主库常见）展示「暂无」 -->
          <div class="drug-item__price">
            <template v-if="Number(item._price) > 0">
              ¥{{ Number(item._price).toFixed(2) }}
            </template>
            <span v-else class="drug-item__price--empty">暂无</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
/* 只用框架主题变量，随亮暗/主题色自动适配，不写 .dark 硬编码覆盖 */
.drug-search-select {
  position: relative;
  width: 100%;
}

.drug-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 1050;
  margin-top: 4px;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-radius: 8px;
  box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
  max-height: 400px;
  overflow: hidden;

  &__loading,
  &__empty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 24px;
    color: hsl(var(--muted-foreground));
    font-size: 14px;
  }

  &__list {
    max-height: 400px;
    overflow-y: auto;
    padding: 4px 0;

    &::-webkit-scrollbar {
      width: 6px;
    }

    &::-webkit-scrollbar-thumb {
      background: hsl(var(--border));
      border-radius: 3px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
    }
  }
}

.drug-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  cursor: pointer;
  transition: background-color 0.2s;

  &:hover,
  &--active {
    background-color: hsl(var(--accent-hover));
  }

  /* 已选确认卡片：固定展示，非下拉浮层 */
  &--selected-card {
    margin-top: 8px;
    cursor: default;
    background: hsl(var(--muted) / 0.3);
    border: 1px solid hsl(var(--border));
    border-radius: 8px;

    &:hover {
      background: hsl(var(--muted) / 0.3);
    }
  }

  &__reselect {
    flex-shrink: 0;
    padding: 0 4px;
  }

  &__image {
    flex-shrink: 0;
    width: 48px;
    height: 48px;
  }

  &__img {
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid hsl(var(--border));
  }

  &__img-placeholder {
    width: 48px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: hsl(var(--accent));
    border-radius: 6px;
    font-size: 12px;
    color: hsl(var(--muted-foreground));
  }

  &__info {
    flex: 1;
    min-width: 0;
    overflow: hidden;
  }

  &__name-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 4px;
    min-width: 0;
  }

  &__name {
    font-size: 14px;
    font-weight: 500;
    color: hsl(var(--foreground));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }

  /* 配送仓绑定药品标识：小号胶囊，不抢主信息 */
  &__multi-wh {
    flex-shrink: 0;
    padding: 0 6px;
    height: 18px;
    line-height: 16px;
    font-size: 11px;
    color: #2b6de5;
    background: rgba(43, 109, 229, 0.08);
    border: 1px solid rgba(43, 109, 229, 0.45);
    border-radius: 999px;
  }

  &__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    font-size: 12px;
    color: hsl(var(--muted-foreground));
  }

  &__id {
    color: hsl(var(--muted-foreground));
  }

  &__spec {
    color: hsl(var(--muted-foreground));
  }

  &__supplier {
    color: hsl(var(--primary));
  }

  &__price {
    flex-shrink: 0;
    font-size: 16px;
    font-weight: 600;
    color: hsl(var(--destructive));

    &--empty {
      font-size: 13px;
      font-weight: 400;
      color: hsl(var(--muted-foreground));
    }
  }
}
</style>
