<!--
 * 文件说明：节点标题栏，负责重命名、删除和单节点执行。
 * 作用范围：仅服务于流程设计器模块。
 -->
<template>
  <div class="node-title">
    <div class="node-title__main">
      <div class="node-title__identity">
        <div class="node-title__icon">
          <img v-if="props.icon" :src="props.icon" alt="" />
          <el-icon v-else><Operation /></el-icon>
        </div>

        <div class="node-title__text">
          <div v-if="!isEditing" class="node-title__name-row">
            <span class="node-title__name" :title="localName">{{ localName }}</span>
            <span v-if="typeLabel" class="node-title__type">{{ typeLabel }}</span>
            <el-tooltip content="重命名" placement="top">
              <button
                type="button"
                class="node-icon-button node-title__edit nodrag nopan"
                aria-label="重命名节点"
                @mousedown.stop
                @click.stop="startEditing"
              >
                <el-icon><EditPen /></el-icon>
              </button>
            </el-tooltip>
          </div>

          <div v-else class="node-title__editor nodrag nopan" @mousedown.stop @click.stop>
            <el-input
              ref="nameInputRef"
              v-model="localName"
              maxlength="40"
              @keydown.enter.prevent="confirmNodeName"
              @keydown.esc.prevent="cancelNodeName"
            />
            <button type="button" class="node-icon-button is-confirm" aria-label="确认名称" @click="confirmNodeName">
              <el-icon><Select /></el-icon>
            </button>
            <button type="button" class="node-icon-button" aria-label="取消重命名" @click="cancelNodeName">
              <el-icon><CloseBold /></el-icon>
            </button>
          </div>
        </div>
      </div>

      <div class="node-title__actions nodrag nopan" @mousedown.stop>
        <el-tooltip v-if="canExecute" content="执行当前节点" placement="top">
          <button type="button" class="node-icon-button is-run" aria-label="执行当前节点" @click.stop="execute">
            <el-icon><VideoPlay /></el-icon>
          </button>
        </el-tooltip>
        <el-tooltip :content="isSelectArea ? '解散分组' : '删除节点'" placement="top">
          <button
            type="button"
            class="node-icon-button is-danger"
            :aria-label="isSelectArea ? '解散分组' : '删除节点'"
            @click.stop="deleteNode"
          >
            <el-icon><FolderRemove v-if="isSelectArea" /><Delete v-else /></el-icon>
          </button>
        </el-tooltip>
      </div>
    </div>

    <div v-if="props.nodeDesc" class="node-title__description" :title="props.nodeDesc">
      {{ props.nodeDesc }}
    </div>
  </div>
</template>

<script setup>
import { computed, nextTick, onMounted, ref, watch } from "vue";
import { CloseBold, Delete, EditPen, FolderRemove, Operation, Select, VideoPlay } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { emitter } from "@/utils/eventBus";

const props = defineProps({
  icon: { type: String, default: "" },
  nodeId: { type: String, default: "" },
  nodeProperties: { type: Object, default: () => ({}) },
  nodeDesc: { type: String, default: "" },
  nodeName: { type: String, default: "" },
  nodeType: { type: String, default: "" },
});

const emits = defineEmits(["setNodeName"]);
const isEditing = ref(false);
const localName = ref("");
const nameInputRef = ref();
const isSelectArea = computed(() => props.nodeType === "selectArea");
const flowContextActions = new Set([
  "NONE",
  "START",
  "END",
  "SUB_START",
  "SUB_END",
  "START_LOOP",
  "STOP_LOOP",
  "BRANCH",
  "GET_CURRENT_OBJECT",
]);
const canExecute = computed(() => {
  if (isSelectArea.value) return false;
  const action = String(props.nodeProperties?.action || "").toUpperCase();
  return Boolean(action) && !flowContextActions.has(action);
});
const typeLabel = computed(() => {
  if (!props.nodeType || ["NONE", "selectArea"].includes(props.nodeType)) return "";
  return { EDGE: "设备", LLM: "模型", AE: "评估" }[props.nodeType] || props.nodeType;
});

/**
 * 处理 sourceName 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const sourceName = () => props.nodeProperties?.name || props.nodeName || "未命名节点";

watch(sourceName, (value) => {
  if (!isEditing.value) localName.value = value;
});

/**
 * 处理 startEditing 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const startEditing = async () => {
  isEditing.value = true;
  await nextTick();
  nameInputRef.value?.focus?.();
  nameInputRef.value?.select?.();
};

/**
 * 确认 confirmNodeName 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const confirmNodeName = () => {
  const name = localName.value.trim();
  if (!name) return;
  localName.value = name;
  emits("setNodeName", name);
  isEditing.value = false;
};

/**
 * 处理 cancelNodeName 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const cancelNodeName = () => {
  localName.value = sourceName();
  isEditing.value = false;
};

/**
 * 删除 deleteNode 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const deleteNode = async () => {
  try {
    await ElMessageBox.confirm(
      isSelectArea.value
        ? "解散后内部节点和连线会保留，是否继续？"
        : "删除后相关连线也会一并移除，是否继续？",
      isSelectArea.value ? "解散分组" : "删除节点",
      {
      confirmButtonText: isSelectArea.value ? "解散" : "删除",
      cancelButtonText: "取消",
      confirmButtonClass: "el-button--danger",
      type: "warning",
    });
    if (isSelectArea.value) window.lf?.ungroup(props.nodeId);
    else window.lf?.deleteNode(props.nodeId);
    emitter.emit("deleteNode", props.nodeId);
  } catch (error) {
    // Cancelled by the user.
  }
};

/**
 * 执行 execute 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const execute = () => {
  document.dispatchEvent(new CustomEvent("singleNodeExecution", {
    bubbles: false,
    cancelable: false,
    // 携带节点 ID，供单节点执行弹窗按节点恢复上次选择的机器人。
    detail: { ...props.nodeProperties, nodeId: props.nodeId },
  }));
};

onMounted(() => {
  localName.value = sourceName();
});
</script>

<style lang="scss" scoped>
.node-title { min-width: 0; }

.node-title__main {
  display: flex;
  min-height: 38px;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.node-title__identity {
  display: flex;
  min-width: 0;
  flex: 1;
  align-items: center;
  gap: 10px;
}

.node-title__icon {
  display: grid;
  width: 36px;
  height: 36px;
  flex: 0 0 36px;
  place-items: center;
  border-radius: 7px;
  background: #f1f5f9;
  color: var(--node-accent, #475569);

  img {
    width: 22px;
    height: 22px;
    object-fit: contain;
  }
}

.node-title__text { min-width: 0; flex: 1; }

.node-title__name-row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 6px;
}

.node-title__name {
  overflow: hidden;
  color: #1f2937;
  font-size: 15px;
  font-weight: 650;
  line-height: 22px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.node-title__type {
  flex: 0 0 auto;
  padding: 1px 6px;
  border: 1px solid #dfe4ec;
  border-radius: 4px;
  background: #f8fafc;
  color: #697386;
  font-size: 10px;
  font-weight: 600;
}

.node-title__edit {
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  opacity: 0;
}

.node-title__actions {
  display: flex;
  flex: 0 0 auto;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.16s ease;
}

.node-title:hover,
.node-title:focus-within {
  .node-title__actions,
  .node-title__edit { opacity: 1; }
}

.node-icon-button {
  display: grid;
  width: 28px;
  height: 28px;
  padding: 0;
  place-items: center;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  color: #7b8494;
  cursor: pointer;
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;

  &:hover {
    border-color: #dfe4ec;
    background: #f8fafc;
    color: #344054;
  }

  &.is-run:hover {
    border-color: #b7dfc7;
    background: #effaf3;
    color: #168447;
  }

  &.is-danger:hover {
    border-color: #f2c5c2;
    background: #fff3f2;
    color: #d93025;
  }

  &.is-confirm { color: #168447; }
}

.node-title__editor {
  display: grid;
  grid-template-columns: minmax(100px, 1fr) 28px 28px;
  gap: 4px;
  align-items: center;
}

.node-title__description {
  display: -webkit-box;
  overflow: hidden;
  margin-top: 8px;
  color: #7a8494;
  font-size: 12px;
  line-height: 18px;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

@media (hover: none) {
  .node-title__actions,
  .node-title__edit { opacity: 1; }
}
</style>
