<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import { useElementSize } from '@vueuse/core';
import type { InputInstance, TreeInstance } from 'element-plus';
import { ArrowDown, Close, Search } from '@element-plus/icons-vue';

defineOptions({ name: 'RequirementTreePicker' });

export interface RequirementTreePickerNode {
  id: string;
  title: string;
  statusCode?: string;
  children?: RequirementTreePickerNode[];
}

interface Props {
  data: RequirementTreePickerNode[];
  /** 编辑模式回显：modelValue 在 data 中找不到（已删除/不在当前可见范围）时显示这个文本 */
  selectedName?: string | null;
  placeholder?: string;
  disabled?: boolean;
  clearable?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  selectedName: null,
  placeholder: '请选择',
  disabled: false,
  clearable: true
});

const modelValue = defineModel<string | null>({ default: null });

const visible = ref(false);
const searchText = ref('');
const treeRef = ref<TreeInstance | null>(null);
const triggerRef = ref<HTMLElement | null>(null);
const searchInputRef = ref<InputInstance | null>(null);
/** 选中时正在播放弹性动画的节点 id；让 ✓ scale 完整跑完再关闭弹层 */
const animatingNodeId = ref<string | null>(null);

// popover 宽度跟随 trigger 实时变化，确保展开后下拉与上方输入框对齐
const { width: triggerWidth } = useElementSize(triggerRef);
const popoverWidth = computed(() => triggerWidth.value || 300);

const treeProps = { value: 'id', label: 'title', children: 'children' };

function findNodeTitle(tree: RequirementTreePickerNode[], id: string): string | null {
  for (const node of tree) {
    if (node.id === id) return node.title;
    if (node.children) {
      const found = findNodeTitle(node.children, id);
      if (found) return found;
    }
  }
  return null;
}

const displayLabel = computed(() => {
  if (!modelValue.value) return '';
  const fromTree = findNodeTitle(props.data, modelValue.value);
  if (fromTree) return fromTree;
  return props.selectedName || '';
});

const showClear = computed(() => props.clearable && Boolean(modelValue.value) && !props.disabled);

watch(searchText, value => {
  treeRef.value?.filter(value);
});

function filterNode(value: string, data: Record<string, unknown>) {
  if (!value) return true;
  const title = typeof data.title === 'string' ? data.title : '';
  return title.includes(value);
}

function handleSelect(node: RequirementTreePickerNode) {
  // 先播 200ms 弹性动画再关弹层；中途又点别的节点会让动画 id 转到新节点上
  animatingNodeId.value = node.id;
  window.setTimeout(() => {
    modelValue.value = node.id;
    visible.value = false;
    animatingNodeId.value = null;
  }, 200);
}

function handleClear() {
  modelValue.value = null;
  visible.value = false;
}

watch(visible, async value => {
  if (value) {
    // 弹层打开后下一帧自动 focus 到搜索框，避免用户再点一下才能输入
    await nextTick();
    searchInputRef.value?.focus();
  } else {
    searchText.value = '';
    animatingNodeId.value = null;
  }
});
</script>

<template>
  <ElPopover
    v-model:visible="visible"
    placement="bottom-start"
    trigger="click"
    :width="popoverWidth"
    popper-class="requirement-tree-picker__popper"
    :disabled="props.disabled"
    :show-arrow="false"
    :hide-after="0"
  >
    <template #reference>
      <div ref="triggerRef" class="requirement-tree-picker__trigger" :class="{ 'is-disabled': props.disabled }">
        <ElInput
          :model-value="displayLabel"
          :placeholder="props.placeholder"
          :disabled="props.disabled"
          readonly
          class="requirement-tree-picker__input"
        >
          <template #suffix>
            <ElIcon v-if="showClear" class="requirement-tree-picker__suffix-icon is-clear" @click.stop="handleClear">
              <Close />
            </ElIcon>
            <ElIcon v-else class="requirement-tree-picker__suffix-icon" :class="{ 'is-open': visible }">
              <ArrowDown />
            </ElIcon>
          </template>
        </ElInput>
      </div>
    </template>

    <div class="requirement-tree-picker__panel">
      <ElInput
        ref="searchInputRef"
        v-model="searchText"
        placeholder="搜索需求"
        clearable
        :prefix-icon="Search"
        size="small"
        class="requirement-tree-picker__search"
      />
      <div class="requirement-tree-picker__tree-wrap">
        <ElTree
          ref="treeRef"
          :data="props.data"
          :props="treeProps"
          node-key="id"
          :expand-on-click-node="true"
          :default-expand-all="!searchText"
          :filter-node-method="filterNode"
          empty-text="暂无数据"
          class="requirement-tree-picker__tree"
        >
          <template #default="{ data: nodeData }">
            <div
              class="requirement-tree-picker__node"
              @dblclick.stop="handleSelect(nodeData as RequirementTreePickerNode)"
            >
              <span
                class="requirement-tree-picker__node-label"
                :class="{ 'is-active': nodeData.id === modelValue }"
                :title="nodeData.title"
              >
                {{ nodeData.title }}
              </span>
              <ElTooltip
                :content="nodeData.id === modelValue ? '当前已选' : '点击或双击节点选择'"
                placement="left"
                :show-after="300"
              >
                <span
                  class="requirement-tree-picker__select-icon"
                  :class="{
                    'is-active': nodeData.id === modelValue,
                    'is-animating': nodeData.id === animatingNodeId
                  }"
                  @click.stop="handleSelect(nodeData as RequirementTreePickerNode)"
                >
                  <svg
                    class="requirement-tree-picker__check-svg"
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="3.5"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                    aria-hidden="true"
                  >
                    <polyline points="5 12 10 17 19 7" />
                  </svg>
                </span>
              </ElTooltip>
            </div>
          </template>
        </ElTree>
      </div>
    </div>
  </ElPopover>
</template>

<style scoped lang="scss">
.requirement-tree-picker__trigger {
  display: block;
  width: 100%;
}

.requirement-tree-picker__input :deep(.el-input__inner) {
  cursor: pointer;
}

.requirement-tree-picker__trigger.is-disabled .requirement-tree-picker__input :deep(.el-input__inner) {
  cursor: not-allowed;
}

.requirement-tree-picker__suffix-icon {
  transition: transform 0.2s;
}

.requirement-tree-picker__suffix-icon.is-open {
  transform: rotate(180deg);
}

.requirement-tree-picker__suffix-icon.is-clear {
  cursor: pointer;

  &:hover {
    color: var(--el-color-danger);
  }
}

.requirement-tree-picker__panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.requirement-tree-picker__search {
  flex: 0 0 auto;
}

.requirement-tree-picker__tree-wrap {
  flex: 1 1 auto;
  overflow: auto;
  max-height: 320px;
}

.requirement-tree-picker__node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding-right: 8px;
}

.requirement-tree-picker__node-label {
  flex: 1 1 auto;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.requirement-tree-picker__node-label.is-active {
  color: var(--el-color-primary);
  font-weight: 600;
}

.requirement-tree-picker__select-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  margin-left: 8px;
  border: 1.5px solid var(--el-border-color);
  border-radius: 4px;
  background: #fff;
  color: var(--el-text-color-placeholder);
  cursor: pointer;
  transition:
    border-color 0.15s ease,
    background 0.15s ease,
    color 0.15s ease,
    transform 0.15s ease;
}

.requirement-tree-picker__check-svg {
  width: 14px;
  height: 14px;
}

.requirement-tree-picker__select-icon:hover {
  border-color: var(--el-color-primary);
  color: var(--el-color-primary);
  transform: scale(1.08);
}

.requirement-tree-picker__select-icon.is-active {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary);
  color: #fff;
}

.requirement-tree-picker__select-icon.is-active:hover {
  background: var(--el-color-primary-light-3);
  border-color: var(--el-color-primary-light-3);
}

.requirement-tree-picker__select-icon.is-animating {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary);
  color: #fff;
  animation: requirement-tree-picker-select-pop 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes requirement-tree-picker-select-pop {
  0% {
    transform: scale(1);
  }

  45% {
    transform: scale(1.28);
  }

  100% {
    transform: scale(1);
  }
}
</style>

<style lang="scss">
.requirement-tree-picker__popper.el-popover {
  padding: 8px;
}
</style>
