<script setup lang="ts">
interface ComponentItem {
  id: number | string;
  name: string;
}

const 组件列表 = ref<ComponentItem[]>([
  { id: 1, name: '组件1' },
  { id: 2, name: '组件2' },
  { id: '3', name: '组件3' },
  { id: 4, name: '组件4' },
  { id: 5, name: '组件5' },
]);

const 流程起点列表 = ref<ComponentItem[]>([]);
const 流程终点列表 = ref<ComponentItem[]>([]);

// 用于存储当前拖拽的组件 ID (类型与ComponentItem.id保持一致)
const draggingItemId = ref<ComponentItem['id'] | null>(null);

function getItemIndex(item: ComponentItem): number {
  return 组件列表.value.findIndex((comp) => comp.id === item.id);
}

function getItemById(id: number | string): ComponentItem | undefined {
  return 组件列表.value.find((comp) => comp.id === id);
}

// ===== 业务逻辑 =====
/**
 * 添加元素到起点列表
 */
function addToStartList(item: ComponentItem): boolean {
  const newStartIndex = getItemIndex(item);
  const endIndex = 流程终点列表.value.length > 0 ? getItemIndex(流程终点列表.value[0]) : -1;

  // 约束检查：如果终点已存在，则新起点必须在终点之前
  if (endIndex !== -1 && newStartIndex >= endIndex) {
    console.debug('⚠️ 流程起点必须在流程终点之前');
    return false; // 表示添加失败
  }

  // 更新数据模型
  if (流程起点列表.value.length > 0) {
    流程起点列表.value.splice(0, 1, item); // 替换
  } else {
    流程起点列表.value.push(item); // 添加
  }
  return true; // 表示添加成功
}

/**
 * 添加元素到终点列表
 */
function addToEndList(item: ComponentItem): boolean {
  const newEndIndex = getItemIndex(item);
  const startIndex = 流程起点列表.value.length > 0 ? getItemIndex(流程起点列表.value[0]) : -1;

  // 约束检查：如果起点已存在，则新终点必须在起点之后
  if (startIndex !== -1 && newEndIndex <= startIndex) {
    console.debug('⚠️ 流程终点必须在流程起点之后');
    return false; // 表示添加失败
  }

  // 更新数据模型
  if (流程终点列表.value.length > 0) {
    流程终点列表.value.splice(0, 1, item); // 替换
  } else {
    流程终点列表.value.push(item); // 添加
  }
  return true; // 表示添加成功
}

// --- 原生拖拽事件处理 ---

// ===== 拖拽事件处理 =====
/**
 * 处理源列表项开始拖拽
 */
function handleDragStart(event: DragEvent, item: ComponentItem) {
  if (event.dataTransfer) {
    event.dataTransfer.setData('text/plain', String(item.id));
    event.dataTransfer.effectAllowed = 'copyMove'; // 允许复制或移动
    draggingItemId.value = item.id as ComponentItem['id']; // 记录拖拽的 ID
    console.debug(`👆 开始拖拽: ${item.name} (ID: ${item.id})`);
  }
}

/**
 * 处理已放置项开始拖拽
 */
function handlePlacedItemDragStart(event: DragEvent, item: ComponentItem, listType: 'end' | 'start') {
  // ESLint: 'end' 优先
  if (event.dataTransfer) {
    event.dataTransfer.setData('text/plain', String(item.id));
    event.dataTransfer.setData('sourceList', listType); // 标记来源列表
    event.dataTransfer.effectAllowed = 'move'; // 只允许移动
    draggingItemId.value = item.id as ComponentItem['id'];
    console.debug(`👆 从 ${listType === 'start' ? '起点' : '终点'} 列表开始拖拽: ${item.name} (ID: ${item.id})`);
  }
}

/**
 * 处理拖拽结束事件
 */
function handleDragEnd() {
  const item = draggingItemId.value ? getItemById(draggingItemId.value) : null;
  if (item) {
    console.debug(`👇 拖拽结束: ${item.name}`);
  } else {
    console.debug('👇 拖拽结束: 未知组件');
  }
  draggingItemId.value = null; // 清除拖拽 ID
}

/**
 * 处理拖拽进入目标区域
 */
function handleDragEnter(event: DragEvent) {
  event.preventDefault();
  const target = event.currentTarget as HTMLElement;
  target.classList.add('drag-over'); // 添加视觉反馈
}

/**
 * 处理拖拽在目标区域上方移动
 */
function handleDragOver(event: DragEvent) {
  event.preventDefault(); // 必须阻止默认行为才能触发 drop
  if (event.dataTransfer) {
    // 根据 effectAllowed 判断是复制还是移动
    event.dataTransfer.dropEffect = event.dataTransfer.effectAllowed === 'copyMove' ? 'copy' : 'move';
  }
}

/**
 * 处理拖拽离开目标区域
 */
function handleDragLeave(event: DragEvent) {
  const target = event.currentTarget as HTMLElement;
  target.classList.remove('drag-over'); // 移除视觉反馈
}

/**
 * 处理在起点列表区域放置
 */
function handleDropStart(event: DragEvent) {
  event.preventDefault();
  const target = event.currentTarget as HTMLElement;
  target.classList.remove('drag-over'); // 移除视觉反馈

  if (!draggingItemId.value || !event.dataTransfer) {
    console.debug('⚠️ 放置失败：无法获取拖拽数据');
    return;
  }

  const itemId = draggingItemId.value;
  const sourceList = event.dataTransfer.getData('sourceList'); // 获取来源列表标记
  const newItem = getItemById(itemId);

  if (!newItem) {
    console.debug(`❌ 放置失败：找不到 ID 为 ${itemId} 的组件`);
    return;
  }

  console.debug(`✋ 尝试放置到起点列表: ${newItem.name} (ID: ${itemId}), 来源: ${sourceList || '源列表'}`);

  // 尝试添加到起点列表
  const added = addToStartList(newItem);

  if (added) {
    console.debug(`✅ 成功放置到起点: ${newItem.name}`);
    // 如果是从终点列表拖过来的，需要清空终点列表
    if (sourceList === 'end') {
      流程终点列表.value = [];
      console.debug('🔄 已清空终点列表，因为项已移动到起点');
    }
  } else {
    console.debug(`⚠️ 放置到起点失败 (约束检查未通过): ${newItem.name}`);
  }
}

/**
 * 处理在终点列表区域放置
 */
function handleDropEnd(event: DragEvent) {
  event.preventDefault();
  const target = event.currentTarget as HTMLElement;
  target.classList.remove('drag-over'); // 移除视觉反馈

  if (!draggingItemId.value || !event.dataTransfer) {
    console.debug('⚠️ 放置失败：无法获取拖拽数据');
    return;
  }

  const itemId = draggingItemId.value;
  const sourceList = event.dataTransfer.getData('sourceList'); // 获取来源列表标记
  const newItem = getItemById(itemId);

  if (!newItem) {
    console.debug(`❌ 放置失败：找不到 ID 为 ${itemId} 的组件`);
    return;
  }

  console.debug(`✋ 尝试放置到终点列表: ${newItem.name} (ID: ${itemId}), 来源: ${sourceList || '源列表'}`);

  // 尝试添加到终点列表
  const added = addToEndList(newItem);

  if (added) {
    console.debug(`✅ 成功放置到终点: ${newItem.name}`);
    // 如果是从起点列表拖过来的，需要清空起点列表
    if (sourceList === 'start') {
      流程起点列表.value = [];
      console.debug('🔄 已清空起点列表，因为项已移动到终点');
    }
  } else {
    console.debug(`⚠️ 放置到终点失败 (约束检查未通过): ${newItem.name}`);
  }
}

// ===== 计算属性 =====
/**
 * 计算流程节点(包含起点和终点)
 */
const 完整流程节点 = computed(() => {
  const nodes: ComponentItem[] = [];
  // 添加起点
  if (流程起点列表.value.length > 0) {
    nodes.push(流程起点列表.value[0]);
  }

  // 添加中间节点
  if (流程起点列表.value.length > 0 && 流程终点列表.value.length > 0) {
    const startIndex = getItemIndex(流程起点列表.value[0]);
    const endIndex = getItemIndex(流程终点列表.value[0]);

    if (startIndex !== -1 && endIndex !== -1 && startIndex < endIndex - 1) {
      nodes.push(...组件列表.value.slice(startIndex + 1, endIndex));
    }
  }

  // 添加终点
  if (流程终点列表.value.length > 0) {
    nodes.push(流程终点列表.value[0]);
  }

  return nodes;
});
watchEffect(() => {
  console.debug(`完整流程节点 :>> `, JSON.stringify(完整流程节点.value, null, 2));
});
</script>

<template>
  <div class="flex gap-5 p-5 bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200 font-sans min-h-100">
    <!-- 组件选择区域 -->
    <div class="flex-1 p-4 rounded border border-gray-200">
      <h3 class="text-center mb-5 text-gray-700 font-bold">组件选择</h3>
      <div class="min-h-150px rounded p-2.5 bg-gray-50 dark:bg-gray-800">
        <!-- 源列表项：添加 draggable 和 dragstart 事件 -->
        <div
          v-for="item in 组件列表"
          :key="item.id"
          class="py-2.5 px-4 mb-2 bg-white dark:bg-gray-700 border border-blue-500 dark:border-blue-400 text-gray-800 dark:text-gray-200 rounded cursor-grab text-center drag-item component-item hover:bg-blue-50 dark:hover:bg-gray-600"
          :data-id="item.id"
          draggable="true"
          @dragstart="handleDragStart($event, item)"
          @dragend="handleDragEnd"
        >
          {{ item.name }}
        </div>
      </div>
    </div>

    <!-- 构建流程区域 -->
    <div class="flex-1 p-4 rounded border border-gray-200">
      <h3 class="text-center mb-5 text-gray-700 font-bold">构建流程</h3>
      <div class="flex items-start mb-2.5">
        <div class="w-20 text-right mr-2.5 pt-2.5 text-[#ccc] dark:text-gray-400">流程起点：</div>
        <!-- 起点列表区域：添加拖放事件监听器 -->
        <div
          class="flex-1 min-h-150px rounded p-2.5 border-2 border-dashed border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 mt-1.25 mb-5 target-list start-list"
          @dragenter="handleDragEnter"
          @dragover="handleDragOver"
          @dragleave="handleDragLeave"
          @drop="handleDropStart"
        >
          <!-- 占位符 -->
          <div
            v-if="流程起点列表.length === 0"
            class="flex justify-center items-center h-full min-h-130px text-[#888] dark:text-gray-500 text-center placeholder pointer-events-none"
          >
            拖入组件作为流程起点
          </div>
          <!-- 已放置的起点项：添加 draggable 和 dragstart/dragend 事件 -->
          <div
            v-for="item in 流程起点列表"
            :key="item.id"
            class="py-2.5 px-4 mb-2 bg-white dark:bg-gray-700 border border-blue-500 dark:border-blue-400 text-gray-800 dark:text-gray-200 rounded cursor-grab text-center drag-item dropped-item hover:bg-blue-50 dark:hover:bg-gray-600"
            :data-id="item.id"
            draggable="true"
            @dragstart="handlePlacedItemDragStart($event, item, 'start')"
            @dragend="handleDragEnd"
          >
            {{ item.name }}
          </div>
        </div>
      </div>

      <div class="flex items-start mb-2.5">
        <div class="w-20 text-right mr-2.5 pt-2.5 text-[#ccc] dark:text-gray-400">流程终点：</div>
        <!-- 终点列表区域：添加拖放事件监听器 -->
        <div
          class="flex-1 min-h-150px rounded p-2.5 border-2 border-dashed border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 mt-1.25 target-list end-list"
          @dragenter="handleDragEnter"
          @dragover="handleDragOver"
          @dragleave="handleDragLeave"
          @drop="handleDropEnd"
        >
          <!-- 占位符 -->
          <div
            v-if="流程终点列表.length === 0"
            class="flex justify-center items-center h-full min-h-130px text-[#888] dark:text-gray-500 text-center placeholder pointer-events-none"
          >
            拖入组件作为流程终点
          </div>
          <!-- 已放置的终点项：添加 draggable 和 dragstart/dragend 事件 -->
          <div
            v-for="item in 流程终点列表"
            :key="item.id"
            class="py-2.5 px-4 mb-2 bg-white dark:bg-gray-700 border border-blue-500 dark:border-blue-400 text-gray-800 dark:text-gray-200 rounded cursor-grab text-center drag-item dropped-item hover:bg-blue-50 dark:hover:bg-gray-600"
            :data-id="item.id"
            draggable="true"
            @dragstart="handlePlacedItemDragStart($event, item, 'end')"
            @dragend="handleDragEnd"
          >
            {{ item.name }}
          </div>
        </div>
      </div>
    </div>

    <!-- 预览区域 -->
    <div class="flex-1 p-4 rounded border border-gray-200">
      <h3 class="text-center mb-5 text-gray-700 font-bold">预览</h3>
      <div
        class="mt-2.5 p-2.5 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded min-h-100px flex flex-col items-stretch justify-start gap-2"
      >
        <template v-if="完整流程节点.length > 0">
          <div
            v-for="item in 完整流程节点"
            :key="item.id"
            class="py-2.5 px-4 bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded cursor-default flex-shrink-0 text-center drag-item component-item preview-item border border-gray-300 dark:border-gray-600"
          >
            {{ item.name }}
          </div>
        </template>
        <div
          v-else
          class="flex justify-center items-center h-20 text-[#888] dark:text-gray-500 text-center w-full preview-placeholder placeholder"
        >
          拖入起点和终点组件生成预览
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* 拖拽悬停时的目标区域样式 */
.target-list.drag-over {
  border-color: #3b82f6;
  background-color: #f0f9ff; /* 拖拽悬停反馈 */
}
.app-dark .target-list.drag-over {
  background-color: #1e3a8a;
}

/* 拖拽过程中的源项样式 (可选) */
.component-item[draggable='true']:active {
  opacity: 0.7;
  cursor: grabbing;
  box-shadow: 0 0 0 2px #3b82f6;
}
.app-dark .component-item[draggable='true']:active {
  box-shadow: 0 0 0 2px #93c5fd;
}

/* 放置后的项样式 */
.dropped-item {
  cursor: grab; /* 允许再次拖动 */
}

/* 占位符样式 */
.placeholder {
  user-select: none; /* 防止文本被选中 */
  pointer-events: none; /* 确保不干扰拖放事件 */
}
</style>
