<template>
  <div ref="rootRef" class="stock-lot-select">
    <div class="stock-lot-control" :class="{ 'stock-lot-control-open': dropdownOpen }" @click="focusSearch">
      <span
        v-for="lot in selectedLots"
        :key="lot.lot_id"
        class="stock-lot-pill"
        :title="lotLabel(lot)"
      >
        <strong>{{ lot.lot_no }}</strong>
        <small>{{ formatWeight(displayWeight(lot)) }}</small>
        <button type="button" aria-label="移除库存批次号" @click.stop="removeLot(lot)">×</button>
      </span>
      <input
        ref="searchInputRef"
        v-model.trim="searchText"
        class="stock-lot-search"
        type="text"
        :placeholder="selectedLots.length ? '' : placeholder"
        @focus="dropdownOpen = true"
        @keydown.down.prevent="moveActive(1)"
        @keydown.up.prevent="moveActive(-1)"
        @keydown.enter.prevent="chooseActive"
        @keydown.esc.prevent="dropdownOpen = false"
        @keydown.backspace="removeLastWhenEmpty"
      />
    </div>

    <div v-if="dropdownOpen" class="stock-lot-dropdown">
      <button
        v-for="(lot, index) in filteredOptions"
        :key="lot.lot_id"
        class="stock-lot-option"
        :class="{
          'stock-lot-option-active': index === activeIndex,
          'stock-lot-option-disabled': !canSelect(lot),
          'stock-lot-option-selected': isSelected(lot)
        }"
        type="button"
        :disabled="!canSelect(lot)"
        @mouseenter="activeIndex = index"
        @click="selectLot(lot)"
      >
        <span class="stock-lot-option-main">
          <strong>{{ lot.lot_no }}</strong>
          <small>{{ lot.item_code }} · {{ lot.item_name }}</small>
          <small v-if="lot.source_stock_lot_no">废料库存批次号：{{ lot.source_stock_lot_no }}</small>
        </span>
        <span class="stock-lot-option-meta">
          <span>{{ optionWeightLabel }} {{ formatWeight(displayWeight(lot)) }}</span>
          <span>¥{{ formatAmount(lot.unit_cost) }}/kg</span>
          <em v-if="lot.disabled_reason">{{ lot.disabled_reason }}</em>
          <em v-else-if="isSelected(lot)">已选择</em>
        </span>
      </button>
      <div v-if="!filteredOptions.length" class="stock-lot-empty">没有匹配的库存批次号</div>
    </div>
  </div>
</template>

<script setup>
import { computed, onMounted, onUnmounted, ref, watch } from "vue";

import { formatAmount, formatWeight } from "../utils/formatters";

const props = defineProps({
  modelValue: {
    type: Array,
    default: () => []
  },
  options: {
    type: Array,
    default: () => []
  },
  placeholder: {
    type: String,
    default: "搜索并选择库存批次号"
  },
  allowUnavailable: {
    type: Boolean,
    default: false
  },
  maxSelections: {
    type: Number,
    default: 0
  },
  displayWeightField: {
    type: String,
    default: "remaining_weight_kg"
  },
  optionWeightLabel: {
    type: String,
    default: "剩余"
  }
});

const emit = defineEmits(["update:modelValue"]);

const rootRef = ref(null);
const searchInputRef = ref(null);
const searchText = ref("");
const dropdownOpen = ref(false);
const activeIndex = ref(0);

const selectedLots = computed(() => props.modelValue || []);
const selectedLotIds = computed(() => new Set(selectedLots.value.map((lot) => Number(lot.lot_id))));
const sortedOptions = computed(() =>
  [...(props.options || [])].sort((left, right) => {
    const leftUsable = left.is_available_for_issue ? 1 : 0;
    const rightUsable = right.is_available_for_issue ? 1 : 0;
    if (leftUsable !== rightUsable) {
      return rightUsable - leftUsable;
    }
    return String(left.lot_no || "").localeCompare(String(right.lot_no || ""), "zh-CN", { numeric: true });
  })
);
const filteredOptions = computed(() => {
  const keywords = normalizeText(searchText.value).split(/\s+/).filter(Boolean);
  if (!keywords.length) {
    return sortedOptions.value;
  }
  return sortedOptions.value.filter((lot) => {
    const body = normalizeText(`${lot.lot_no} ${lot.source_stock_lot_no} ${lot.source_material_sub_batch_no} ${lot.item_code} ${lot.item_name} ${lot.disabled_reason}`);
    return keywords.every((keyword) => body.includes(keyword));
  });
});

watch([filteredOptions, selectedLotIds], ([rows]) => {
  resetActiveIndexToFirstSelectable(rows);
});

watch(dropdownOpen, (isOpen) => {
  if (isOpen) {
    resetActiveIndexToFirstSelectable();
  }
});

function resetActiveIndexToFirstSelectable(rows = filteredOptions.value) {
  if (!rows.length) {
    activeIndex.value = 0;
    return;
  }
  const firstSelectableIndex = rows.findIndex((lot) => canSelect(lot));
  activeIndex.value = firstSelectableIndex >= 0 ? firstSelectableIndex : 0;
}

function syncActiveIndexToSelectable(rows = filteredOptions.value) {
  if (!rows.length) {
    activeIndex.value = 0;
    return;
  }
  const clampedIndex = Math.min(Math.max(activeIndex.value, 0), rows.length - 1);
  if (canSelect(rows[clampedIndex])) {
    activeIndex.value = clampedIndex;
    return;
  }
  const firstSelectableIndex = rows.findIndex((lot) => canSelect(lot));
  activeIndex.value = firstSelectableIndex >= 0 ? firstSelectableIndex : clampedIndex;
}

function normalizeText(value) {
  return String(value || "").trim().toLowerCase();
}

function lotLabel(lot) {
  const stockLotSuffix = lot.source_stock_lot_no ? ` · 废料库存批次号：${lot.source_stock_lot_no}` : "";
  return `${lot.lot_no} · ${lot.item_code} · ${lot.item_name}${stockLotSuffix}`;
}

function toFiniteNumber(value) {
  const numericValue = Number(value);
  return Number.isFinite(numericValue) ? numericValue : 0;
}

function displayWeight(lot) {
  const configuredValue = lot?.[props.displayWeightField];
  if (configuredValue !== undefined && configuredValue !== null && configuredValue !== "") {
    return toFiniteNumber(configuredValue);
  }
  return toFiniteNumber(lot?.remaining_weight_kg);
}

function normalizeSelectedIssuedWeight(lot) {
  return toFiniteNumber(lot?.issued_weight_kg ?? lot?.remaining_weight_kg ?? 0);
}

function canSelect(lot) {
  if (!lot || (!props.allowUnavailable && !lot.is_available_for_issue) || isSelected(lot)) {
    return false;
  }
  return !props.maxSelections || selectedLots.value.length < props.maxSelections;
}

function isSelected(lot) {
  return selectedLotIds.value.has(Number(lot?.lot_id));
}

function focusSearch() {
  dropdownOpen.value = true;
  searchInputRef.value?.focus();
}

function selectLot(lot) {
  if (!canSelect(lot)) {
    return;
  }
  const nextSelectedLots = props.maxSelections === 1 ? [] : selectedLots.value;
  emit("update:modelValue", [
    ...nextSelectedLots,
    {
      ...lot,
      issued_weight_kg: normalizeSelectedIssuedWeight(lot)
    }
  ]);
  searchText.value = "";
  dropdownOpen.value = false;
  activeIndex.value = 0;
}

function removeLot(lot) {
  emit("update:modelValue", selectedLots.value.filter((item) => Number(item.lot_id) !== Number(lot.lot_id)));
}

function removeLastWhenEmpty() {
  if (searchText.value || !selectedLots.value.length) {
    return;
  }
  emit("update:modelValue", selectedLots.value.slice(0, -1));
}

function moveActive(delta) {
  const rows = filteredOptions.value;
  if (!rows.length) {
    return;
  }
  let nextIndex = activeIndex.value;
  for (let step = 0; step < rows.length; step += 1) {
    nextIndex = (nextIndex + delta + rows.length) % rows.length;
    if (canSelect(rows[nextIndex])) {
      activeIndex.value = nextIndex;
      return;
    }
  }
}

function chooseActive() {
  const row = filteredOptions.value[activeIndex.value];
  if (row) {
    selectLot(row);
  }
}

function handlePointerDown(event) {
  if (!rootRef.value?.contains(event.target)) {
    dropdownOpen.value = false;
  }
}

onMounted(() => {
  document.addEventListener("pointerdown", handlePointerDown, true);
});

onUnmounted(() => {
  document.removeEventListener("pointerdown", handlePointerDown, true);
});
</script>

<style scoped>
.stock-lot-select {
  position: relative;
}

.stock-lot-control {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  min-height: 46px;
  padding: 6px 10px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(31, 45, 61, 0.04);
  cursor: text;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.stock-lot-control-open {
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.12);
}

.stock-lot-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 4px 8px;
  border: 1px solid #d9ecff;
  border-radius: 4px;
  background: #ecf5ff;
  color: #409eff;
  font-size: 12px;
}

.stock-lot-pill strong {
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stock-lot-pill small {
  color: #67a8e8;
}

.stock-lot-pill button {
  border: 0;
  background: transparent;
  color: #409eff;
  cursor: pointer;
  font: inherit;
  font-size: 16px;
  line-height: 1;
  padding: 0;
}

.stock-lot-search {
  flex: 1 1 220px;
  min-width: 160px;
  border: 0;
  outline: none;
  background: transparent;
  color: #606266;
  font: inherit;
  padding: 6px 2px;
}

.stock-lot-search::placeholder {
  color: #c0c4cc;
}

.stock-lot-dropdown {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 320px;
  overflow: auto;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  background: #ffffff;
  box-shadow: 0 10px 28px rgba(31, 45, 61, 0.16);
}

.stock-lot-option {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  width: 100%;
  min-height: 54px;
  padding: 10px 12px;
  border: 0;
  border-bottom: 1px solid #f0f2f5;
  background: #ffffff;
  color: #606266;
  cursor: pointer;
  font: inherit;
  text-align: left;
}

.stock-lot-option:hover,
.stock-lot-option-active {
  background: #f5f7fa;
  color: #409eff;
}

.stock-lot-option-selected {
  background: #ecf5ff;
}

.stock-lot-option-disabled {
  cursor: not-allowed;
  opacity: 0.48;
}

.stock-lot-option-main,
.stock-lot-option-meta {
  display: grid;
  gap: 4px;
}

.stock-lot-option-main {
  min-width: 0;
}

.stock-lot-option-main strong,
.stock-lot-option-main small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stock-lot-option-main small,
.stock-lot-option-meta {
  color: #909399;
  font-size: 12px;
}

.stock-lot-option-meta {
  justify-items: end;
  white-space: nowrap;
}

.stock-lot-option-meta em {
  color: #f56c6c;
  font-style: normal;
}

.stock-lot-empty {
  color: #909399;
  padding: 18px 12px;
  text-align: center;
}

@media (max-width: 640px) {
  .stock-lot-option {
    grid-template-columns: 1fr;
  }

  .stock-lot-option-meta {
    justify-items: start;
  }
}
</style>
