<template>
  <div class="h-screen flex flex-col bg-monokai-bg">
    <!-- Header -->
    <header class="bg-monokai-bg border-b-2 border-monokai-comment p-4">
      <div class="flex items-center justify-between">
        <div class="flex items-center gap-4">
          <h1 class="text-3xl font-bold text-monokai-green">IPWatch</h1>
          <div class="flex flex-col">
            <span class="text-monokai-comment">Architecture réseau</span>
            <span class="text-xs text-monokai-comment/60">bêta</span>
          </div>
        </div>

        <div class="flex items-center gap-6">
          <SystemStats />

          <div class="h-8 w-px bg-monokai-comment"></div>

          <button
            @click="goToDashboard"
            class="px-4 py-2 rounded bg-monokai-yellow text-monokai-bg font-bold hover:bg-monokai-orange transition-colors"
            title="Retour au dashboard"
          >
            ← Dashboard
          </button>

          <button
            @click="openSettings"
            class="px-4 py-2 rounded bg-monokai-purple text-monokai-bg text-sm hover:bg-monokai-pink transition-colors"
            title="Ouvrir les paramètres"
          >
            ⚙ Paramètres
          </button>
        </div>
      </div>
    </header>

    <main class="flex-1 overflow-hidden p-4 flex flex-col gap-3 min-h-0">
      <section class="grid grid-cols-[minmax(0,1fr)_22rem] gap-6">
        <div class="text-sm text-monokai-comment mb-1"></div>
        <div></div>
        <div class="border border-monokai-comment/60 rounded-2xl p-2 flex items-center justify-between gap-2">
          <div class="flex flex-wrap gap-2 items-center">
            <button
              v-for="tool in tools"
              :key="tool.id"
              class="px-3 py-1.5 rounded-xl border border-monokai-comment text-monokai-text text-sm"
              :style="{ borderColor: tool.borderColor, color: tool.color }"
              @mousedown.prevent="startDrag(tool, $event)"
            >
              {{ tool.label }}
            </button>
          </div>
          <button
            class="flex items-center gap-2 px-3 py-1.5 rounded-xl border border-monokai-comment/60 text-monokai-text text-sm hover:border-monokai-cyan/60"
            @click="saveWorld"
          >
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M4 4h12l4 4v12H4z"></path>
              <path d="M8 4v6h8V4"></path>
              <path d="M8 20v-6h8v6"></path>
            </svg>
            Save
          </button>
        </div>
      </section>

      <section class="flex-1 min-h-0 grid grid-cols-[minmax(0,1fr)_22rem] gap-6">
        <div
          ref="worldRef"
          class="border border-monokai-comment/60 rounded-3xl h-full relative overflow-hidden"
          @mousedown="clearSelection"
          @click="handleWorldClick"
          @mousemove="handleWorldMouseMove"
        >
          <svg class="absolute inset-0 w-full h-full">
            <defs>
              <marker id="arrow-end" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                <path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"></path>
              </marker>
              <marker id="circle-end" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="5" markerHeight="5" orient="auto">
                <circle cx="5" cy="5" r="4" fill="currentColor"></circle>
              </marker>
            </defs>
            <template v-for="spline in splines" :key="spline.id">
              <path
                :d="splinePath(spline)"
                fill="none"
                :stroke="spline.style.color"
                :stroke-width="spline.style.width"
                :stroke-dasharray="dashArray(spline.style.dash)"
                :marker-start="markerForCap(spline.style.startCap)"
                :marker-end="markerForCap(spline.style.endCap)"
                class="spline-path"
                :class="spline.id === selectedSplineId ? 'cursor-crosshair' : 'cursor-pointer'"
                @click.stop="selectSpline(spline)"
                @dblclick.stop="exitSplineEdit"
              ></path>
            </template>
            <path
              v-if="drawingSpline"
              :d="previewSplinePath"
              fill="none"
              :stroke="drawingSpline.style.color"
              :stroke-width="drawingSpline.style.width"
              :stroke-dasharray="dashArray(drawingSpline.style.dash)"
              :marker-start="markerForCap(drawingSpline.style.startCap)"
              :marker-end="markerForCap(drawingSpline.style.endCap)"
              class="pointer-events-none"
            ></path>
            <template v-if="editingSpline">
              <circle
                v-for="(point, index) in editingSpline.points"
                :key="`ghost-${editingSpline.id}-${index}`"
                :cx="point.x"
                :cy="point.y"
                r="5"
                class="fill-transparent stroke-monokai-cyan cursor-move spline-point"
                @mousedown.stop="startDragSplinePoint(index, $event)"
              ></circle>
            </template>
          </svg>
          <div
            v-for="item in worldItems"
            :key="item.instanceId"
            class="absolute px-3 py-2 rounded-xl border text-sm select-none world-item"
            :class="item.instanceId === selectedItemId ? 'ring-2 ring-monokai-cyan/70 ring-offset-2 ring-offset-monokai-bg' : ''"
            :style="itemStyle(item)"
            @mousemove="updateHoverMode(item, $event)"
            @mouseleave="clearHoverMode(item)"
            @mousedown.stop="startPointerAction(item, $event)"
            @click.stop="handleObjectClick($event)"
            @dblclick.stop="startEditLabel(item)"
          >
            <div v-if="item.online" class="absolute left-2 bottom-2 flex items-center gap-1 text-[10px] text-monokai-text">
              <span class="w-2.5 h-2.5 rounded-sm bg-green-500 inline-block"></span>
              online
            </div>
            <template v-for="connector in connectorPositions(item)" :key="connector.key">
              <div
                class="absolute w-3 h-3 rounded-full bg-monokai-comment border border-monokai-bg cursor-crosshair"
                :style="{
                  ...connector.style,
                  width: `${item.connectorSize || 12}px`,
                  height: `${item.connectorSize || 12}px`,
                  backgroundColor: item.connectorColor || '#B5B5B5',
                  borderColor: item.connectorStrokeColor || '#1E1F1C'
                }"
                title="connecteur"
                @click.stop="handleConnectorClick(item, connector)"
              ></div>
            </template>
            <template v-if="editingItemId === item.instanceId">
              <input
                class="bg-monokai-bg border border-monokai-comment/60 rounded px-1 py-0.5 text-xs w-full"
                :value="editingLabel"
                @input="editingLabel = $event.target.value"
                @keydown.enter.prevent="commitEditLabel"
                @blur="commitEditLabel"
              />
            </template>
            <template v-else>
              <span v-if="item.icon" class="mr-1 inline-flex items-center">
                <img
                  v-if="isImageIcon(item.icon)"
                  :src="iconUrl(item.icon)"
                  alt=""
                  class="inline mr-1 align-text-bottom"
                  :style="{ width: `${item.iconSize || 16}px`, height: `${item.iconSize || 16}px` }"
                />
                <span v-else :style="{ fontSize: `${item.iconSize || 16}px` }">{{ item.icon }}</span>
              </span>
              <span :style="{ color: item.fontColor || '#E6E6E6', fontSize: `${item.fontSize || 14}px` }">
                {{ item.label }}
              </span>
            </template>
          </div>
        </div>

        <aside
          class="border border-monokai-comment/60 rounded-3xl p-5 overflow-auto overflow-x-hidden h-full"
          @keydown.enter.prevent="confirmField"
        >
          <div class="text-sm text-monokai-text border-b border-monokai-comment/60 pb-1 mb-2">propriétés</div>
          <div v-if="selectedSpline" class="space-y-3 text-sm text-monokai-text">
            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Spline</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Couleur</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="selectedSpline.style.color"
                  @input="updateSplineStyle('color', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Épaisseur</span>
                <input
                  type="number"
                  min="1"
                  max="10"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedSpline.style.width"
                  @input="updateSplineStyle('width', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Style</span>
                <select
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedSpline.style.dash"
                  @change="updateSplineStyle('dash', $event.target.value)"
                >
                  <option value="solid">plein</option>
                  <option value="dashed">pointillé</option>
                  <option value="dotted">points</option>
                </select>
                <span class="text-xs text-[rgb(221_187_22)]">Terminaison 1</span>
                <select
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedSpline.style.startCap"
                  @change="updateSplineStyle('startCap', $event.target.value)"
                >
                  <option value="none">aucune</option>
                  <option value="arrow">flèche</option>
                  <option value="circle">rond</option>
                </select>
                <span class="text-xs text-[rgb(221_187_22)]">Terminaison 2</span>
                <select
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedSpline.style.endCap"
                  @change="updateSplineStyle('endCap', $event.target.value)"
                >
                  <option value="none">aucune</option>
                  <option value="arrow">flèche</option>
                  <option value="circle">rond</option>
                </select>
              </div>
            </details>
          </div>
          <div v-else-if="selectedItem" class="space-y-3 text-sm text-monokai-text">
            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">IP</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Adresse</span>
                <div class="flex gap-2 min-w-0">
                  <input
                    class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 flex-1 min-w-0"
                    :value="selectedItem.ipAddress"
                    @input="updateSelectedField('ipAddress', $event.target.value)"
                    @blur="fetchIpDataForSelected"
                  />
                  <button
                    class="px-2 rounded border border-monokai-comment/60 text-xs text-monokai-comment hover:text-monokai-cyan"
                    @click="fetchIpDataForSelected"
                  >
                    sync
                  </button>
                </div>
                <span class="text-xs text-[rgb(221_187_22)]">IP JSON</span>
                <textarea
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 text-xs font-mono h-28 min-w-0"
                  :value="formatIpData(selectedItem.ipData)"
                  readonly
                ></textarea>
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Identité</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Nom</span>
                <input
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.name"
                  @input="updateSelectedField('name', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">ID</span>
                <input
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.id"
                  @input="updateSelectedField('id', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Label</span>
                <input
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.label"
                  @input="updateSelectedField('label', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Type</span>
                <input
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.type"
                  @input="updateSelectedField('type', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Icon</span>
                <div class="flex gap-2 min-w-0">
                  <input
                    class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 flex-1 min-w-0"
                    :value="selectedItem.icon"
                    @input="updateSelectedField('icon', $event.target.value)"
                  />
                  <button
                    class="px-2 rounded border border-monokai-comment/60 text-xs text-monokai-comment hover:text-monokai-cyan"
                    @click="openIconPicker"
                  >
                    choisir
                  </button>
                </div>
                <span class="text-xs text-[rgb(221_187_22)]">Taille icon</span>
                <input
                  type="range"
                  min="12"
                  max="48"
                  step="2"
                  class="w-full"
                  :value="selectedItem.iconSize"
                  @input="updateSelectedField('iconSize', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Verrouillé</span>
                <label class="flex items-center gap-2">
                  <input
                    type="checkbox"
                    class="accent-monokai-cyan"
                    :checked="selectedItem.locked"
                    @change="updateSelectedField('locked', $event.target.checked)"
                  />
                  <span class="text-xs text-monokai-comment">bloquer</span>
                </label>
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Relations</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Parent</span>
                <input
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="parentDisplay(selectedItem)"
                  @input="parentInput = $event.target.value"
                  @keydown.enter.prevent="applyParentInput"
                  @blur="applyParentInput"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Enfants</span>
                <textarea
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 text-xs font-mono h-20 min-w-0"
                  :value="childrenDisplay(selectedItem)"
                  readonly
                ></textarea>
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Géométrie</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Pos X</span>
                <input
                  type="number"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.x"
                  @input="updateSelectedField('x', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Pos Y</span>
                <input
                  type="number"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.y"
                  @input="updateSelectedField('y', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Pos Z</span>
                <input
                  type="number"
                  min="0"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.zIndex"
                  @input="updateSelectedField('zIndex', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Taille X</span>
                <input
                  type="number"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.width"
                  @input="updateSelectedField('width', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Taille Y</span>
                <input
                  type="number"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1 min-w-0"
                  :value="selectedItem.height"
                  @input="updateSelectedField('height', toNumber($event.target.value))"
                />
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Couleur</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Intérieur</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="selectedItem.fillColor"
                  @input="updateSelectedField('fillColor', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Transparence</span>
                <input
                  type="range"
                  min="0"
                  max="1"
                  step="0.05"
                  class="w-full"
                  :value="selectedItem.opacity"
                  @input="updateSelectedField('opacity', toNumber($event.target.value))"
                />
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Trait</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Couleur</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="selectedItem.borderColor"
                  @input="updateSelectedField('borderColor', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Épaisseur</span>
                <input
                  type="number"
                  min="1"
                  max="12"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.borderWidth"
                  @input="updateSelectedField('borderWidth', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Type</span>
                <select
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.borderStyle"
                  @change="updateSelectedField('borderStyle', $event.target.value)"
                >
                  <option value="solid">plein</option>
                  <option value="dashed">pointillé</option>
                  <option value="dotted">points</option>
                </select>
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Spline</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Couleur</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="splineDefaults.color"
                  @input="splineDefaults.color = $event.target.value"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Épaisseur</span>
                <input
                  type="number"
                  min="1"
                  max="10"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="splineDefaults.width"
                  @input="splineDefaults.width = toNumber($event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Style</span>
                <select
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="splineDefaults.dash"
                  @change="splineDefaults.dash = $event.target.value"
                >
                  <option value="solid">plein</option>
                  <option value="dashed">pointillé</option>
                  <option value="dotted">points</option>
                </select>
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Connecteurs</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Haut</span>
                <input
                  type="number"
                  min="0"
                  max="8"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.connectTop"
                  @input="updateSelectedField('connectTop', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Gauche</span>
                <input
                  type="number"
                  min="0"
                  max="8"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.connectLeft"
                  @input="updateSelectedField('connectLeft', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Droite</span>
                <input
                  type="number"
                  min="0"
                  max="8"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.connectRight"
                  @input="updateSelectedField('connectRight', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Bas</span>
                <input
                  type="number"
                  min="0"
                  max="8"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.connectBottom"
                  @input="updateSelectedField('connectBottom', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Couleur</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="selectedItem.connectorColor"
                  @input="updateSelectedField('connectorColor', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Taille</span>
                <input
                  type="number"
                  min="6"
                  max="24"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.connectorSize"
                  @input="updateSelectedField('connectorSize', toNumber($event.target.value))"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Trait</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="selectedItem.connectorStrokeColor"
                  @input="updateSelectedField('connectorStrokeColor', $event.target.value)"
                />
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Statut</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Online</span>
                <label class="flex items-center gap-2">
                  <input
                    type="checkbox"
                    class="accent-monokai-cyan"
                    :checked="selectedItem.online"
                    @change="updateSelectedField('online', $event.target.checked)"
                  />
                  <span class="text-xs text-monokai-comment">actif</span>
                </label>
              </div>
            </details>

            <details open class="border border-monokai-comment/40 rounded-xl">
              <summary class="px-3 py-2 text-xs text-monokai-comment cursor-pointer">Texte</summary>
              <div class="grid grid-cols-[6rem_1fr] gap-2 items-center px-3 pb-3">
                <span class="text-xs text-[rgb(221_187_22)]">Couleur</span>
                <input
                  type="color"
                  class="h-8 w-full bg-monokai-bg border border-monokai-comment/60 rounded"
                  :value="selectedItem.fontColor"
                  @input="updateSelectedField('fontColor', $event.target.value)"
                />
                <span class="text-xs text-[rgb(221_187_22)]">Taille</span>
                <input
                  type="number"
                  min="8"
                  max="48"
                  class="bg-monokai-bg border border-monokai-comment/60 rounded px-2 py-1"
                  :value="selectedItem.fontSize"
                  @input="updateSelectedField('fontSize', toNumber($event.target.value))"
                />
              </div>
            </details>
          </div>
          <div v-else class="text-sm text-monokai-comment">Aucun objet sélectionné.</div>
        </aside>
      </section>
    </main>

    <div
      v-if="dragging && dragItem"
      class="fixed pointer-events-none z-50 opacity-70"
      :style="ghostStyle"
    >
      <div
        class="px-4 py-2 rounded-xl border text-sm"
        :style="{ borderColor: dragItem.borderColor, color: dragItem.color }"
      >
        {{ dragItem.label }}
      </div>
    </div>

    <div
      v-if="iconPickerOpen"
      class="fixed inset-0 z-50 bg-black/60 flex items-center justify-center"
      @click.self="iconPickerOpen = false"
    >
      <div class="bg-monokai-bg border border-monokai-comment/60 rounded-2xl w-[28rem] max-h-[70vh] overflow-auto p-4">
        <div class="flex items-center justify-between mb-3">
          <div class="text-monokai-text font-semibold">Icônes (data/icons)</div>
          <button class="text-monokai-comment hover:text-monokai-cyan" @click="iconPickerOpen = false">✕</button>
        </div>
        <div v-if="iconList.length" class="grid grid-cols-4 gap-3">
          <button
            v-for="icon in iconList"
            :key="icon"
            class="border border-monokai-comment/60 rounded-xl p-2 text-xs text-monokai-text hover:border-monokai-cyan/60"
            @click="selectIcon(icon)"
          >
            <img :src="iconUrl(icon)" :alt="icon" class="w-full h-12 object-contain mb-1" />
            <div class="truncate">{{ icon }}</div>
          </button>
        </div>
        <div v-else class="text-xs text-monokai-comment">Aucune icône trouvée.</div>
      </div>
    </div>

    <div
      v-if="parentLinkDialog.open"
      class="fixed inset-0 z-50 bg-black/60 flex items-center justify-center"
      @click.self="cancelParentLink"
    >
      <div class="bg-monokai-bg border border-monokai-comment/60 rounded-2xl w-[30rem] max-h-[70vh] overflow-auto p-4">
        <div class="text-monokai-text font-semibold mb-2">Sélection du parent</div>
        <div class="text-xs text-monokai-comment mb-4">
          Plusieurs objets correspondent à <span class="text-monokai-cyan">{{ parentLinkDialog.hostName }}</span>.
          Choisis le parent à lier.
        </div>
        <div class="space-y-2">
          <label
            v-for="option in parentLinkDialog.options"
            :key="option.id"
            class="flex items-center gap-2 text-sm text-monokai-text border border-monokai-comment/40 rounded-lg px-2 py-1"
          >
            <input
              type="radio"
              name="parent-link"
              :value="option.id"
              :checked="parentLinkDialog.selectedId === option.id"
              @change="parentLinkDialog.selectedId = option.id"
            />
            <span>{{ option.label }}</span>
          </label>
        </div>
        <div class="mt-4 flex justify-end gap-2">
          <button
            class="px-3 py-1.5 rounded border border-monokai-comment/60 text-xs text-monokai-comment hover:text-monokai-cyan"
            @click="cancelParentLink"
          >
            Annuler
          </button>
          <button
            class="px-3 py-1.5 rounded border border-monokai-comment/60 text-xs text-monokai-text hover:border-monokai-cyan/60"
            @click="confirmParentLink"
          >
            Valider
          </button>
        </div>
      </div>
    </div>

    <SettingsModal
      :isOpen="showSettings"
      @close="showSettings = false"
      @configReloaded="handleConfigReloaded"
    />
  </div>
</template>

<script setup>
import { ref, computed, onBeforeUnmount, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useIPStore } from '@/stores/ipStore'
import SystemStats from '@/components/SystemStats.vue'
import SettingsModal from '@/components/SettingsModal.vue'

const router = useRouter()
const ipStore = useIPStore()
const showSettings = ref(false)
const worldRef = ref(null)
const dragging = ref(false)
const dragItem = ref(null)
const ghostPos = ref({ x: 0, y: 0 })
const worldItems = ref([])
const selectedItemId = ref(null)
const moveOffset = ref({ x: 0, y: 0 })
const resizeStart = ref({ x: 0, y: 0, width: 0, height: 0 })
const movingItemId = ref(null)
const resizingItemId = ref(null)
const editingItemId = ref(null)
const editingLabel = ref('')
const iconPickerOpen = ref(false)
const iconList = ref([])
const splines = ref([])
const drawingSpline = ref(null)
const worldMouse = ref({ x: 0, y: 0 })
const splineDefaults = ref({ color: '#56A7FF', width: 2, dash: 'solid' })
const parentInput = ref('')
const selectedSplineId = ref(null)
const editingSplineId = ref(null)
const draggingSplinePoint = ref(null)
const parentLinkDialog = ref({
  open: false,
  childId: '',
  hostName: '',
  options: [],
  selectedId: ''
})
const tools = [
  { id: 'world', label: 'world', type: 'container', icon: '', borderColor: '#E6E6E6', color: '#E6E6E6' },
  { id: 'home', label: 'home', type: 'container', icon: '', borderColor: '#8F6FE3', color: '#BDA8F7' },
  { id: 'computer', label: 'computer', type: 'device', icon: '', borderColor: '#B5B5B5', color: '#E6E6E6' },
  { id: 'network', label: 'network', type: 'network', icon: '🌐', borderColor: '#56A7FF', color: '#9BC8FF' },
  { id: 'room', label: 'room', type: 'container', icon: '', borderColor: '#C8A08A', color: '#E6CDBE' },
  { id: 'vm', label: 'vm', type: 'vm', icon: '', borderColor: '#62D36E', color: '#9BE3A4' },
  { id: 'service', label: 'service', type: 'service', icon: '', borderColor: '#F0A33A', color: '#F7C37B' }
]

const ghostStyle = computed(() => ({
  left: `${ghostPos.value.x}px`,
  top: `${ghostPos.value.y}px`,
  transform: 'translate(-50%, -50%)'
}))
const selectedItem = computed(() =>
  worldItems.value.find(item => item.instanceId === selectedItemId.value) || null
)
const selectedSpline = computed(() =>
  splines.value.find(spline => spline.id === selectedSplineId.value) || null
)
const editingSpline = computed(() =>
  splines.value.find(spline => spline.id === editingSplineId.value) || null
)
const defaultSize = { width: 120, height: 44 }
const defaultStyle = {
  borderColor: '#B5B5B5',
  fillColor: 'transparent',
  opacity: 1,
  zIndex: 1
}

function openSettings() {
  showSettings.value = true
}

function goToDashboard() {
  router.push('/')
}

async function handleConfigReloaded() {
  await ipStore.fetchIPs()
  ipStore.bumpConfigReload()
}

onMounted(async () => {
  await loadWorld()
  window.addEventListener('keydown', handleKeyDown)
})

watch(selectedItemId, () => {
  if (!selectedItem.value) {
    parentInput.value = ''
    return
  }
  parentInput.value = parentDisplay(selectedItem.value)
})

function startDrag(tool, event) {
  dragItem.value = tool
  dragging.value = true
  ghostPos.value = { x: event.clientX, y: event.clientY }
  window.addEventListener('mousemove', handleDragMove)
  window.addEventListener('mouseup', handleDragEnd)
}

function handleDragMove(event) {
  if (!dragging.value) return
  ghostPos.value = { x: event.clientX, y: event.clientY }
}

function handleDragEnd(event) {
  if (!dragging.value || !dragItem.value) return
  dragging.value = false
  window.removeEventListener('mousemove', handleDragMove)
  window.removeEventListener('mouseup', handleDragEnd)

  if (!worldRef.value) {
    dragItem.value = null
    return
  }

  const rect = worldRef.value.getBoundingClientRect()
  const inside =
    event.clientX >= rect.left &&
    event.clientX <= rect.right &&
    event.clientY >= rect.top &&
    event.clientY <= rect.bottom

  if (inside) {
    const x = Math.max(0, Math.round(event.clientX - rect.left))
    const y = Math.max(0, Math.round(event.clientY - rect.top))
    worldItems.value.push({
      instanceId: `${dragItem.value.id}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
      id: dragItem.value.id,
      name: dragItem.value.label,
      label: dragItem.value.label,
      type: dragItem.value.type,
      icon: dragItem.value.icon || '',
      iconSize: 16,
      fontColor: '#E6E6E6',
      fontSize: 14,
      ipAddress: '',
      ipData: null,
      room: '',
      parent: '',
      parentId: '',
      childrenIds: [],
      connectTop: 0,
      connectLeft: 0,
      connectRight: 0,
      connectBottom: 0,
      online: false,
      connectorColor: '#B5B5B5',
      connectorSize: 12,
      connectorStrokeColor: '#1E1F1C',
      borderColor: dragItem.value.borderColor || defaultStyle.borderColor,
      borderWidth: 2,
      borderStyle: 'solid',
      fillColor: defaultStyle.fillColor,
      opacity: defaultStyle.opacity,
      zIndex: defaultStyle.zIndex,
      locked: false,
      color: dragItem.value.color,
      x,
      y,
      width: defaultSize.width,
      height: defaultSize.height,
      hoverMode: 'move'
    })
    selectedItemId.value = worldItems.value[worldItems.value.length - 1].instanceId
    selectedSplineId.value = null
  }

  dragItem.value = null
}

function startPointerAction(item, event) {
  const mode = getPointerMode(item, event)
  selectedItemId.value = item.instanceId
  if (item.locked) return
  const rect = worldRef.value?.getBoundingClientRect()
  if (!rect) return
  if (mode === 'resize') {
    resizingItemId.value = item.instanceId
    resizeStart.value = {
      x: event.clientX,
      y: event.clientY,
      width: item.width || defaultSize.width,
      height: item.height || defaultSize.height
    }
    window.addEventListener('mousemove', handleResizeDrag)
    window.addEventListener('mouseup', handleResizeEnd)
  } else {
    movingItemId.value = item.instanceId
    moveOffset.value = {
      x: event.clientX - rect.left - item.x,
      y: event.clientY - rect.top - item.y
    }
    window.addEventListener('mousemove', handleMoveDrag)
    window.addEventListener('mouseup', handleMoveEnd)
  }
}

function handleMoveDrag(event) {
  if (!movingItemId.value || !worldRef.value) return
  const rect = worldRef.value.getBoundingClientRect()
  const x = Math.max(0, Math.round(event.clientX - rect.left - moveOffset.value.x))
  const y = Math.max(0, Math.round(event.clientY - rect.top - moveOffset.value.y))
  const index = worldItems.value.findIndex(item => item.instanceId === movingItemId.value)
  if (index !== -1) {
    worldItems.value[index] = { ...worldItems.value[index], x, y }
  }
}

function handleMoveEnd() {
  movingItemId.value = null
  window.removeEventListener('mousemove', handleMoveDrag)
  window.removeEventListener('mouseup', handleMoveEnd)
}

function handleResizeDrag(event) {
  if (!resizingItemId.value) return
  const index = worldItems.value.findIndex(item => item.instanceId === resizingItemId.value)
  if (index === -1) return
  const deltaX = event.clientX - resizeStart.value.x
  const deltaY = event.clientY - resizeStart.value.y
  const nextWidth = Math.max(40, Math.round(resizeStart.value.width + deltaX))
  const nextHeight = Math.max(28, Math.round(resizeStart.value.height + deltaY))
  worldItems.value[index] = {
    ...worldItems.value[index],
    width: nextWidth,
    height: nextHeight
  }
}

function handleResizeEnd() {
  resizingItemId.value = null
  window.removeEventListener('mousemove', handleResizeDrag)
  window.removeEventListener('mouseup', handleResizeEnd)
}

function clearSelection(event) {
  const target = event.target
  if (!target || !worldRef.value) return
  if (target.closest('.world-item')) return
  if (target.closest('.spline-path') || target.closest('.spline-point')) return
  selectedItemId.value = null
  selectedSplineId.value = null
}

function handleWorldMouseMove(event) {
  if (!worldRef.value) return
  const rect = worldRef.value.getBoundingClientRect()
  worldMouse.value = {
    x: Math.round(event.clientX - rect.left),
    y: Math.round(event.clientY - rect.top)
  }
}

function handleWorldClick(event) {
  if (!drawingSpline.value || !worldRef.value) return
  if (editingSplineId.value) return
  if (event.target !== worldRef.value) return
  drawingSpline.value.points.push({ ...worldMouse.value })
}

function handleObjectClick(event) {
  if (!drawingSpline.value || !worldRef.value) return
  if (editingSplineId.value) return
  addSplinePointFromEvent(event)
}

function addSplinePointFromEvent(event) {
  if (!worldRef.value || !drawingSpline.value) return
  const rect = worldRef.value.getBoundingClientRect()
  const x = Math.round(event.clientX - rect.left)
  const y = Math.round(event.clientY - rect.top)
  drawingSpline.value.points.push({ x, y })
}

function itemStyle(item) {
  return {
    left: `${item.x}px`,
    top: `${item.y}px`,
    width: item.width ? `${item.width}px` : `${defaultSize.width}px`,
    height: item.height ? `${item.height}px` : `${defaultSize.height}px`,
    borderColor: item.borderColor || defaultStyle.borderColor,
    borderWidth: `${item.borderWidth || 2}px`,
    borderStyle: item.borderStyle || 'solid',
    color: item.color,
    backgroundColor: applyFillOpacity(item.fillColor || defaultStyle.fillColor, item.opacity),
    zIndex: item.zIndex ?? defaultStyle.zIndex,
    cursor: item.hoverMode === 'resize' ? 'nwse-resize' : 'move'
  }
}

function getPointerMode(item, event) {
  const rect = event.currentTarget.getBoundingClientRect()
  const offsetX = event.clientX - rect.left
  const offsetY = event.clientY - rect.top
  const edgeSize = 10
  const nearRight = rect.width - offsetX <= edgeSize
  const nearBottom = rect.height - offsetY <= edgeSize
  return nearRight || nearBottom ? 'resize' : 'move'
}

function updateHoverMode(item, event) {
  const mode = getPointerMode(item, event)
  if (item.hoverMode !== mode) {
    item.hoverMode = mode
  }
}

function clearHoverMode(item) {
  item.hoverMode = 'move'
}

async function loadWorld() {
  try {
    const response = await fetch('/api/architecture/world')
    if (!response.ok) return
    const data = await response.json()
    worldItems.value = Array.isArray(data?.items)
      ? data.items.map(item => ({
          ...item,
          width: item.width || defaultSize.width,
          height: item.height || defaultSize.height,
          borderColor: item.borderColor || defaultStyle.borderColor,
          fillColor: item.fillColor ?? defaultStyle.fillColor,
          opacity: typeof item.opacity === 'number' ? item.opacity : defaultStyle.opacity,
          zIndex: typeof item.zIndex === 'number' ? item.zIndex : defaultStyle.zIndex,
          ipAddress: item.ipAddress || '',
          ipData: item.ipData || null,
          room: item.room || '',
          parent: item.parent || '',
          parentId: item.parentId || '',
          childrenIds: Array.isArray(item.childrenIds) ? item.childrenIds : [],
          iconSize: typeof item.iconSize === 'number' ? item.iconSize : 16,
          fontColor: item.fontColor || '#E6E6E6',
          fontSize: typeof item.fontSize === 'number' ? item.fontSize : 14,
          connectTop: toNumber(item.connectTop || 0),
          connectLeft: toNumber(item.connectLeft || 0),
          connectRight: toNumber(item.connectRight || 0),
          connectBottom: toNumber(item.connectBottom || 0),
          online: Boolean(item.online),
          connectorColor: item.connectorColor || '#B5B5B5',
          connectorSize: typeof item.connectorSize === 'number' ? item.connectorSize : 12,
          connectorStrokeColor: item.connectorStrokeColor || '#1E1F1C',
          borderWidth: typeof item.borderWidth === 'number' ? item.borderWidth : 2,
          borderStyle: item.borderStyle || 'solid',
          locked: Boolean(item.locked),
          hoverMode: 'move'
        }))
      : []
    splines.value = Array.isArray(data?.splines)
      ? data.splines.map(spline => ({
          ...spline,
          style: {
            color: spline?.style?.color || splineDefaults.value.color,
            width: spline?.style?.width || splineDefaults.value.width,
            dash: spline?.style?.dash || splineDefaults.value.dash,
            startCap: spline?.style?.startCap || 'none',
            endCap: spline?.style?.endCap || 'none'
          },
          points: Array.isArray(spline?.points) ? spline.points : []
        }))
      : []
  } catch (error) {
    console.error('Erreur chargement architecture.json:', error)
  }
}

async function saveWorld() {
  try {
    const response = await fetch('/api/architecture/world', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ items: worldItems.value, splines: splines.value })
    })
    if (!response.ok) {
      console.error('Erreur sauvegarde architecture.json:', response.status)
    }
  } catch (error) {
    console.error('Erreur sauvegarde architecture.json:', error)
  }
}

function updateSelectedField(field, value) {
  const index = worldItems.value.findIndex(item => item.instanceId === selectedItemId.value)
  if (index === -1) return
  if (field !== 'locked' && worldItems.value[index].locked) return
  worldItems.value[index] = { ...worldItems.value[index], [field]: value }
}

function toNumber(value) {
  const parsed = Number(value)
  return Number.isFinite(parsed) ? parsed : 0
}

function formatIpData(data) {
  if (!data) return ''
  try {
    return JSON.stringify(data, null, 2)
  } catch (error) {
    return ''
  }
}

async function fetchIpDataForSelected() {
  if (!selectedItem.value) return
  if (selectedItem.value.locked) return
  const ipAddress = (selectedItem.value.ipAddress || '').trim()
  if (!ipAddress) {
    updateSelectedField('ipData', null)
    return
  }
  try {
    const response = await fetch(`/api/ips/${encodeURIComponent(ipAddress)}`)
    if (!response.ok) {
      updateSelectedField('ipData', null)
      return
    }
    const data = await response.json()
    const updates = {
      ipData: data,
      name: data?.name || selectedItem.value.name || '',
      label: data?.name || selectedItem.value.label || '',
      id: data?.ip || selectedItem.value.id || '',
      type: data?.vm ? 'vm' : selectedItem.value.type,
      room: data?.location || selectedItem.value.room || '',
      parent: data?.host || selectedItem.value.parent || '',
      icon: data?.icon_filename || selectedItem.value.icon || ''
    }
    const index = worldItems.value.findIndex(item => item.instanceId === selectedItemId.value)
    if (index !== -1) {
      worldItems.value[index] = { ...worldItems.value[index], ...updates }
    }
    if (data?.host) {
      handleParentCandidates(selectedItem.value.instanceId, data.host)
    }
  } catch (error) {
    console.error('Erreur chargement IP:', error)
    updateSelectedField('ipData', null)
  }
}

function startEditLabel(item) {
  if (item.locked) return
  editingItemId.value = item.instanceId
  editingLabel.value = item.label
}

function commitEditLabel() {
  if (!editingItemId.value) return
  const index = worldItems.value.findIndex(item => item.instanceId === editingItemId.value)
  if (index !== -1) {
    worldItems.value[index] = { ...worldItems.value[index], label: editingLabel.value }
  }
  editingItemId.value = null
  editingLabel.value = ''
}

function handleKeyDown(event) {
  const tag = event.target?.tagName?.toLowerCase()
  if (tag === 'input' || tag === 'textarea') return
  if (event.key === 'Escape' && drawingSpline.value) {
    drawingSpline.value = null
    return
  }
  if (event.key === 'Enter' && editingSplineId.value) {
    editingSplineId.value = null
    return
  }
  if ((event.key === 'Delete' || event.key === 'Backspace') && selectedItemId.value) {
    const selected = worldItems.value.find(item => item.instanceId === selectedItemId.value)
    if (selected?.locked) return
    removeItemRelations(selectedItemId.value)
    worldItems.value = worldItems.value.filter(item => item.instanceId !== selectedItemId.value)
    selectedItemId.value = null
  }
  if ((event.key === 'Delete' || event.key === 'Backspace') && selectedSplineId.value) {
    splines.value = splines.value.filter(spline => spline.id !== selectedSplineId.value)
    selectedSplineId.value = null
    editingSplineId.value = null
  }
}

async function openIconPicker() {
  iconPickerOpen.value = true
  if (iconList.value.length) return
  try {
    const response = await fetch('/api/ips/icons')
    if (!response.ok) return
    const data = await response.json()
    iconList.value = Array.isArray(data?.icons) ? data.icons : []
  } catch (error) {
    console.error('Erreur chargement icônes:', error)
  }
}

function iconUrl(name) {
  return `/icons/${name}`
}

function selectIcon(name) {
  updateSelectedField('icon', name)
  iconPickerOpen.value = false
}

function isImageIcon(value) {
  return typeof value === 'string' && value.includes('.')
}

function applyFillOpacity(color, opacity) {
  if (!color || color === 'transparent') return 'transparent'
  const alpha = typeof opacity === 'number' ? opacity : defaultStyle.opacity
  const hexMatch = color.startsWith('#') ? color.slice(1) : null
  if (hexMatch) {
    const hex = hexMatch.length === 3
      ? hexMatch.split('').map(ch => ch + ch).join('')
      : hexMatch.padEnd(6, '0')
    const r = parseInt(hex.slice(0, 2), 16)
    const g = parseInt(hex.slice(2, 4), 16)
    const b = parseInt(hex.slice(4, 6), 16)
    return `rgba(${r}, ${g}, ${b}, ${alpha})`
  }
  const rgbMatch = color.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/i)
  if (rgbMatch) {
    const [r, g, b] = rgbMatch.slice(1, 4)
    return `rgba(${r}, ${g}, ${b}, ${alpha})`
  }
  const rgbaMatch = color.match(/rgba\\((\\d+),\\s*(\\d+),\\s*(\\d+),\\s*([\\d.]+)\\)/i)
  if (rgbaMatch) {
    const [r, g, b] = rgbaMatch.slice(1, 4)
    return `rgba(${r}, ${g}, ${b}, ${alpha})`
  }
  return color
}

function connectorPositions(item) {
  const positions = []
  const w = item.width || defaultSize.width
  const h = item.height || defaultSize.height
  const pushSide = (count, side) => {
    if (!count) return
    const total = Math.max(1, count)
    for (let i = 0; i < total; i += 1) {
      const t = (i + 1) / (total + 1)
      if (side === 'top') {
        positions.push({
          key: `${item.instanceId}-t-${i}`,
          side,
          index: i,
          x: Math.round(w * t),
          y: 0,
          style: { left: `${Math.round(w * t) - 6}px`, top: '-6px' }
        })
      } else if (side === 'bottom') {
        positions.push({
          key: `${item.instanceId}-b-${i}`,
          side,
          index: i,
          x: Math.round(w * t),
          y: h,
          style: { left: `${Math.round(w * t) - 6}px`, bottom: '-6px' }
        })
      } else if (side === 'left') {
        positions.push({
          key: `${item.instanceId}-l-${i}`,
          side,
          index: i,
          x: 0,
          y: Math.round(h * t),
          style: { left: '-6px', top: `${Math.round(h * t) - 6}px` }
        })
      } else if (side === 'right') {
        positions.push({
          key: `${item.instanceId}-r-${i}`,
          side,
          index: i,
          x: w,
          y: Math.round(h * t),
          style: { right: '-6px', top: `${Math.round(h * t) - 6}px` }
        })
      }
    }
  }
  pushSide(item.connectTop, 'top')
  pushSide(item.connectBottom, 'bottom')
  pushSide(item.connectLeft, 'left')
  pushSide(item.connectRight, 'right')
  return positions
}

function parentDisplay(item) {
  if (!item?.parentId) return ''
  const parent = worldItems.value.find(entry => entry.instanceId === item.parentId)
  return parent?.label || parent?.id || item.parentId
}

function childrenDisplay(item) {
  if (!item?.childrenIds?.length) return ''
  return item.childrenIds
    .map(childId => {
      const child = worldItems.value.find(entry => entry.instanceId === childId)
      return child?.label || child?.id || childId
    })
    .join(', ')
}

function applyParentInput() {
  if (!selectedItem.value) return
  if (selectedItem.value.locked) return
  const value = parentInput.value.trim()
  setParentForItem(selectedItem.value.instanceId, value)
}

function setParentForItem(childId, parentRef) {
  const childIndex = worldItems.value.findIndex(item => item.instanceId === childId)
  if (childIndex === -1) return
  const child = worldItems.value[childIndex]
  const previousParentId = child.parentId || ''
  if (!parentRef) {
    if (previousParentId) {
      removeChildFromParent(previousParentId, childId)
    }
    worldItems.value[childIndex] = { ...child, parentId: '' }
    return
  }
  const parent = resolveItemByRef(parentRef)
  if (!parent || parent.instanceId === childId) return
  if (previousParentId && previousParentId !== parent.instanceId) {
    removeChildFromParent(previousParentId, childId)
  }
  const parentChildren = new Set(parent.childrenIds || [])
  parentChildren.add(childId)
  updateItemById(parent.instanceId, { childrenIds: Array.from(parentChildren) })
  worldItems.value[childIndex] = { ...child, parentId: parent.instanceId }
}

function resolveItemByRef(refValue) {
  const value = refValue.toLowerCase()
  return (
    worldItems.value.find(item => item.instanceId.toLowerCase() === value) ||
    worldItems.value.find(item => (item.id || '').toLowerCase() === value) ||
    worldItems.value.find(item => (item.label || '').toLowerCase() === value)
  )
}

function removeChildFromParent(parentId, childId) {
  const parentIndex = worldItems.value.findIndex(item => item.instanceId === parentId)
  if (parentIndex === -1) return
  const children = (worldItems.value[parentIndex].childrenIds || []).filter(id => id !== childId)
  updateItemById(parentId, { childrenIds: children })
}

function updateItemById(itemId, patch) {
  const index = worldItems.value.findIndex(item => item.instanceId === itemId)
  if (index === -1) return
  worldItems.value[index] = { ...worldItems.value[index], ...patch }
}

function removeItemRelations(itemId) {
  worldItems.value.forEach(item => {
    if (item.parentId === itemId) {
      item.parentId = ''
    }
    if (Array.isArray(item.childrenIds) && item.childrenIds.includes(itemId)) {
      item.childrenIds = item.childrenIds.filter(childId => childId !== itemId)
    }
  })
}

function handleParentCandidates(childId, hostName) {
  const matches = worldItems.value.filter(item => {
    const label = (item.label || '').toLowerCase()
    const name = (item.name || '').toLowerCase()
    const id = (item.id || '').toLowerCase()
    const host = hostName.toLowerCase()
    return label === host || name === host || id === host
  })
  if (matches.length === 1) {
    setParentForItem(childId, matches[0].instanceId)
    return
  }
  if (matches.length > 1) {
    parentLinkDialog.value = {
      open: true,
      childId,
      hostName,
      options: matches.map(item => ({
        id: item.instanceId,
        label: item.label || item.name || item.id || item.instanceId
      })),
      selectedId: matches[0].instanceId
    }
  }
}

function confirmParentLink() {
  if (!parentLinkDialog.value.selectedId) return
  setParentForItem(parentLinkDialog.value.childId, parentLinkDialog.value.selectedId)
  parentLinkDialog.value.open = false
}

function cancelParentLink() {
  parentLinkDialog.value.open = false
}

function handleConnectorClick(item, connector) {
  const target = {
    nodeId: item.instanceId,
    side: connector.side,
    index: connector.index
  }
  if (!drawingSpline.value) {
    drawingSpline.value = {
      from: target,
      points: [],
      style: { ...splineDefaults.value, startCap: 'none', endCap: 'none' }
    }
    return
  }
  const spline = {
    id: `spline-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
    from: drawingSpline.value.from,
    to: target,
    points: drawingSpline.value.points,
    style: drawingSpline.value.style
  }
  splines.value.push(spline)
  drawingSpline.value = null
}

function selectSpline(spline) {
  selectedSplineId.value = spline.id
  selectedItemId.value = null
  editingSplineId.value = spline.id
}

function exitSplineEdit() {
  editingSplineId.value = null
}

function updateSplineStyle(field, value) {
  if (!selectedSpline.value) return
  const index = splines.value.findIndex(spline => spline.id === selectedSpline.value.id)
  if (index === -1) return
  splines.value[index] = {
    ...splines.value[index],
    style: { ...splines.value[index].style, [field]: value }
  }
}

function markerForCap(cap) {
  if (cap === 'arrow') return 'url(#arrow-end)'
  if (cap === 'circle') return 'url(#circle-end)'
  return undefined
}

function startDragSplinePoint(index, event) {
  if (!editingSplineId.value) return
  draggingSplinePoint.value = { splineId: editingSplineId.value, index }
  window.addEventListener('mousemove', handleSplinePointDrag)
  window.addEventListener('mouseup', stopDragSplinePoint)
}

function handleSplinePointDrag(event) {
  if (!draggingSplinePoint.value || !worldRef.value) return
  const rect = worldRef.value.getBoundingClientRect()
  const x = Math.round(event.clientX - rect.left)
  const y = Math.round(event.clientY - rect.top)
  const { splineId, index } = draggingSplinePoint.value
  const splineIndex = splines.value.findIndex(spline => spline.id === splineId)
  if (splineIndex === -1) return
  const points = [...(splines.value[splineIndex].points || [])]
  if (!points[index]) return
  points[index] = { x, y }
  splines.value[splineIndex] = { ...splines.value[splineIndex], points }
}

function stopDragSplinePoint() {
  draggingSplinePoint.value = null
  window.removeEventListener('mousemove', handleSplinePointDrag)
  window.removeEventListener('mouseup', stopDragSplinePoint)
}

function confirmField(event) {
  const target = event.target
  if (target && typeof target.blur === 'function') {
    target.blur()
  }
}

function connectorWorldPoint(nodeId, side, index) {
  const item = worldItems.value.find(entry => entry.instanceId === nodeId)
  if (!item) return null
  const connectors = connectorPositions(item).filter(c => c.side === side)
  const target = connectors[index]
  if (!target) return null
  return {
    x: item.x + target.x,
    y: item.y + target.y
  }
}

function splinePath(spline) {
  const start = connectorWorldPoint(spline.from.nodeId, spline.from.side, spline.from.index)
  const end = connectorWorldPoint(spline.to.nodeId, spline.to.side, spline.to.index)
  if (!start || !end) return ''
  const points = [start, ...(spline.points || []), end]
  return curvedPath(points)
}

const previewSplinePath = computed(() => {
  if (!drawingSpline.value) return ''
  const start = connectorWorldPoint(
    drawingSpline.value.from.nodeId,
    drawingSpline.value.from.side,
    drawingSpline.value.from.index
  )
  if (!start) return ''
  const points = [start, ...drawingSpline.value.points, worldMouse.value]
  return curvedPath(points)
})

function dashArray(style) {
  if (style === 'dashed') return '6 4'
  if (style === 'dotted') return '2 4'
  return '0'
}

function curvedPath(points) {
  if (!points.length) return ''
  if (points.length === 1) return `M ${points[0].x} ${points[0].y}`
  if (points.length === 2) {
    const [p0, p1] = points
    const cx = (p0.x + p1.x) / 2
    const cy = (p0.y + p1.y) / 2
    return `M ${p0.x} ${p0.y} Q ${cx} ${cy} ${p1.x} ${p1.y}`
  }
  const path = [`M ${points[0].x} ${points[0].y}`]
  for (let i = 1; i < points.length - 1; i += 1) {
    const current = points[i]
    const next = points[i + 1]
    const midX = (current.x + next.x) / 2
    const midY = (current.y + next.y) / 2
    path.push(`Q ${current.x} ${current.y} ${midX} ${midY}`)
  }
  const last = points[points.length - 1]
  path.push(`T ${last.x} ${last.y}`)
  return path.join(' ')
}

onBeforeUnmount(() => {
  window.removeEventListener('mousemove', handleDragMove)
  window.removeEventListener('mouseup', handleDragEnd)
  window.removeEventListener('mousemove', handleMoveDrag)
  window.removeEventListener('mouseup', handleMoveEnd)
  window.removeEventListener('mousemove', handleResizeDrag)
  window.removeEventListener('mouseup', handleResizeEnd)
  window.removeEventListener('keydown', handleKeyDown)
  window.removeEventListener('mousemove', handleSplinePointDrag)
  window.removeEventListener('mouseup', stopDragSplinePoint)
})
</script>
