<template>
  <div
    class="flow-node-shell"
    :class="[`flow-node-shell--${flowType}`, { 'flow-node-shell--selected': selected }]"
  >
    <Handle
      v-if="showTarget"
      :id="targetHandleId"
      type="target"
      :position="Position.Left"
      :connectable="connectable && !flowStore.disableForm"
      :is-valid-connection="isValidLoopConnection"
      class="flow-handle flow-handle--target"
    />
    <div class="flow-node-content" :class="{ 'flow-node-card': !isGroup && flowType !== 'branch' }">
      <component
        :is="nodeComponent"
        ref="componentRef"
        :model="model"
        :graph-model="model"
        :properties="properties"
        :child-count="isGroup ? childNodeCount : undefined"
        @content-change="contentChanged"
        @bind-ref="bindComponent"
        @add-anchor="addAnchor"
        @remove-anchor="removeAnchor"
        @change-anchor="changeAnchor"
        @sync-anchors="syncBranchAnchors"
        @add-to-group="addToGroup"
      />
    </div>

    <template v-if="flowType === 'branch'">
      <template v-for="anchor in branchAnchors" :key="anchor.id">
        <Handle
          :id="anchor.id"
          type="source"
          :position="Position.Right"
          :connectable="connectable && !flowStore.disableForm"
          :is-valid-connection="isValidLoopConnection"
          class="flow-handle flow-handle--source flow-handle--branch"
          :style="{ top: `${Math.max(40, Number(anchor.height || 0))}px` }"
        />
      </template>
    </template>
    <Handle
      v-else-if="showSource"
      :id="`${id}_1`"
      type="source"
      :position="Position.Right"
      :connectable="connectable && !flowStore.disableForm"
      :is-valid-connection="isValidLoopConnection"
      class="flow-handle flow-handle--source"
    />
  </div>
</template>

<script setup>
import { computed, onMounted, ref } from "vue";
import { Handle, Position, useVueFlow } from "@vue-flow/core";
import { useFlowStore } from "@/store/modules/flow";
import StartNode from "../nodes/common/startNode.vue";
import EndNode from "../nodes/common/endNode.vue";
import ServiceNode from "../nodes/common/serviceNode.vue";
import SelectAreaNode from "../nodes/common/selectAreaNode.vue";
import LoopNode from "../nodes/function/loop.vue";
import BranchNode from "../nodes/function/switch.vue";
import StopLoopNode from "../nodes/function/stopLoop.vue";
import SubStartNode from "../nodes/function/subStart.vue";
import SubEndNode from "../nodes/function/subEnd.vue";
import SleepNode from "../nodes/function/sleep.vue";
import HttpNode from "../nodes/function/httpNode.vue";
import CodeNode from "../nodes/function/codeNode.vue";
import CurrentLoopNode from "../nodes/function/currentLoop.vue";
import SdAgentNode from "../nodes/function/sdAgent.vue";
import RecognizeNode from "../nodes/function/recognize.vue";
import DeviceUniversal from "../nodes/function/device_universal.vue";

const props = defineProps({
  id: String,
  data: Object,
  selected: Boolean,
  connectable: [Boolean, Number, String, Function],
});

const componentMap = {
  start: StartNode,
  end: EndNode,
  serviceNode: ServiceNode,
  selectArea: SelectAreaNode,
  loop: LoopNode,
  branch: BranchNode,
  stopLoop: StopLoopNode,
  subStart: SubStartNode,
  subEnd: SubEndNode,
  sleep: SleepNode,
  http: HttpNode,
  code: CodeNode,
  currentLoop: CurrentLoopNode,
  sdAgent: SdAgentNode,
  recognize: RecognizeNode,
  device_universal: DeviceUniversal,
};

const flowStore = useFlowStore();
const vueFlow = useVueFlow("main-flow");
const componentRef = ref();
const flowType = computed(() => props.data.flowType);
const properties = computed(() => props.data.properties || {});
const nodeComponent = computed(() => componentMap[flowType.value] || ServiceNode);
const isGroup = computed(() => ["loop", "selectArea"].includes(flowType.value));
const childNodeCount = computed(() => (
  (vueFlow.getNodes.value || []).filter((node) => node.parentNode === props.id).length
));
const showTarget = computed(() => !["start", "subStart", "selectArea"].includes(flowType.value));
const showSource = computed(() => !["end", "stopLoop", "subEnd", "selectArea", "branch"].includes(flowType.value));
const targetHandleId = computed(() => flowType.value === "branch" ? `${props.id}_entry` : `${props.id}_3`);
const branchAnchors = computed(() => {
  const reservedIds = new Set([`${props.id}_1`, `${props.id}_3`, `${props.id}_entry`]);
  const conditionDefs = properties.value.nodeParams || properties.value.conditions || [];
  const conditionIds = new Set(conditionDefs.map((condition) => condition?.id).filter(Boolean));
  const elseId = `${props.id}_else`;
  const seen = new Set();
  return (properties.value.anchor || []).filter((anchor) => {
    if (!anchor?.id || reservedIds.has(anchor.id) || seen.has(anchor.id)) return false;
    if (conditionIds.size && anchor.id !== elseId && !conditionIds.has(anchor.id)) return false;
    seen.add(anchor.id);
    return true;
  });
});
const model = computed(() => window.lf?.getNodeModelById(props.id));

const getLoopScope = (nodeId) => {
  const node = vueFlow.findNode(nodeId);
  const parent = node?.parentNode ? vueFlow.findNode(node.parentNode) : null;
  return parent?.data?.flowType === "loop" ? parent.id : null;
};

const isValidLoopConnection = (connection) => {
  if (!connection.source || !connection.target || connection.source === connection.target) return false;
  return getLoopScope(connection.source) === getLoopScope(connection.target);
};

const syncAnchors = (anchors) => {
  window.lf?.setProperties(props.id, { ...window.lf.getProperties(props.id), anchor: anchors });
  vueFlow.updateNodeInternals([props.id]);
};

const addAnchor = (height, anchorId) => {
  const anchors = [...branchAnchors.value];
  if (!anchors.some((anchor) => anchor.id === anchorId)) {
    anchors.push({ id: anchorId, name: "right", height });
    syncAnchors(anchors);
  }
};

const removeAnchor = (anchorId) => syncAnchors(branchAnchors.value.filter((anchor) => anchor.id !== anchorId));
const changeAnchor = (height, anchorId) => syncAnchors(branchAnchors.value.map((anchor) => anchor.id === anchorId ? { ...anchor, height } : anchor));
const syncBranchAnchors = (positions = []) => {
  if (!positions.length) return;
  const positionMap = new Map(positions.map((item) => [item.id, Number(item.height || 0)]));
  const current = [...(properties.value.anchor || [])];
  let changed = false;
  const anchors = current.map((anchor) => {
    if (!positionMap.has(anchor.id)) return anchor;
    const height = positionMap.get(anchor.id);
    positionMap.delete(anchor.id);
    if (Math.abs(Number(anchor.height || 0) - height) < 0.5) return anchor;
    changed = true;
    return { ...anchor, height };
  });
  positionMap.forEach((height, id) => {
    anchors.push({ id, name: "right", height });
    changed = true;
  });
  if (changed) syncAnchors(anchors);
  else vueFlow.updateNodeInternals([props.id]);
};
const contentChanged = () => {
  vueFlow.updateNodeInternals([props.id]);
  window.lf?.markChanged();
};
const bindComponent = (instance) => window.lf?.registerComponentInstance(props.id, instance);
const addToGroup = (childId, dropCenter) => window.lf?.addToGroup(props.id, childId, dropCenter);

onMounted(() => bindComponent(componentRef.value));
</script>

<style scoped lang="scss">
.flow-node-shell {
  --node-accent: #4f6b8a;
  width: 100%;
  min-height: 100%;
  position: relative;
  box-sizing: border-box;

  &--start,
  &--subStart { --node-accent: #239565; }

  &--end,
  &--subEnd,
  &--stopLoop { --node-accent: #d45252; }

  &--branch { --node-accent: #b7791f; }
  &--loop,
  &--currentLoop { --node-accent: #df6b35; }
  &--http,
  &--code { --node-accent: #3978c5; }
  &--sdAgent,
  &--recognize { --node-accent: #7a5ab8; }

  &--loop,
  &--selectArea {
    height: 100%;
  }

  &:hover .flow-node-card {
    border-color: #cbd3df;
    box-shadow: 0 10px 26px rgba(31, 41, 55, 0.11);
  }

  &--selected .flow-node-card {
    border-color: #2f80ed;
    box-shadow: 0 0 0 2px rgba(47, 128, 237, 0.2), 0 10px 26px rgba(31, 41, 55, 0.12);
  }

  &--selected.flow-node-shell--branch :deep(.node__container),
  &--selected.flow-node-shell--loop :deep(.group__container),
  &--selected.flow-node-shell--selectArea :deep(.group__container) {
    border-color: #2f80ed;
    box-shadow: 0 0 0 2px rgba(47, 128, 237, 0.18);
  }
}

.flow-node-content {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}

.flow-node-card {
  width: 100%;
  height: auto;
  padding: 13px 14px 13px 16px;
  position: relative;
  background: #fff;
  border: 1px solid #dfe4ec;
  border-left: 4px solid var(--node-accent);
  border-radius: 8px;
  box-shadow: 0 7px 20px rgba(31, 41, 55, 0.08);
  box-sizing: border-box;
  transition: border-color 0.16s ease, box-shadow 0.16s ease;

  :deep(.node__container) {
    width: 100%;
    height: auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    box-sizing: border-box;
  }

  :deep(.terminal-node) {
    display: flex;
    min-width: 0;
    align-items: center;
    gap: 11px;
  }

  :deep(.terminal-node__icon) {
    display: grid;
    width: 38px;
    height: 38px;
    flex: 0 0 38px;
    place-items: center;
    border-radius: 50%;
    background: #f1f5f9;
  }

  :deep(.terminal-node__icon img) {
    width: 22px;
    height: 22px;
  }

  :deep(.terminal-node__content) { min-width: 0; }

  :deep(.terminal-node__title) {
    color: #1f2937;
    font-size: 15px;
    font-weight: 650;
    line-height: 21px;
  }

  :deep(.terminal-node__description) {
    overflow: hidden;
    margin-top: 3px;
    color: #7a8494;
    font-size: 11px;
    line-height: 16px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.flow-handle {
  width: 12px;
  height: 12px;
  border: 2px solid #fff;
  background: var(--node-accent);
  box-shadow: 0 0 0 1px var(--node-accent), 0 2px 5px rgba(31, 41, 55, 0.18);
  z-index: 10;
  transition: transform 0.14s ease, box-shadow 0.14s ease;

  &:hover {
    box-shadow: 0 0 0 3px rgba(47, 128, 237, 0.18), 0 2px 6px rgba(31, 41, 55, 0.2);
  }

  &--target {
    left: -6px;
    background: #fff;
    border-color: var(--node-accent);
  }

  &--source { right: -6px; }
  &--branch { transform: translate(50%, -50%); }

  &--branch:hover { transform: translate(50%, -50%) scale(1.18); }
}

</style>
