<template>
  <div :class="containerClass">
    <div class="border border-base-200 rounded-lg bg-base-100 shadow-sm">
      <div class="flex flex-wrap items-start justify-between gap-3 border-b border-base-200 px-4 py-3">
        <div class="flex-1 min-w-[220px] space-y-2">
          <label class="label">
            <span class="label-text text-xs font-semibold">
              {{ isRoot ? 'Composant racine de la catégorie' : 'Famille de composant' }}
            </span>
          </label>
          <template v-if="isRoot">
            <p class="text-[11px] text-gray-500">
              Le composant racine correspond à la catégorie que vous éditez. Sélectionnez uniquement les familles pour les sous-composants.
            </p>
          </template>
          <template v-else-if="!lockType">
            <select
              v-model="node.typeComposantId"
              class="select select-bordered select-sm w-full"
              @change="handleComponentTypeSelect(node)"
            >
              <option value="">
                Sélectionner une famille de composant
              </option>
              <option
                v-for="type in componentTypes"
                :key="type.id"
                :value="type.id"
              >
                {{ formatComponentTypeOption(type) }}
              </option>
            </select>
            <p class="text-[11px] text-gray-500">
              {{ node.typeComposantId ? `Sélection : ${getComponentTypeLabel(node.typeComposantId) || 'Inconnue'}` : 'Aucune famille sélectionnée' }}
            </p>
            <div v-if="!isRoot" class="form-control mt-2">
              <label class="label py-1">
                <span class="label-text text-[11px]">Alias (optionnel)</span>
              </label>
              <input
                v-model="node.alias"
                type="text"
                class="input input-bordered input-xs"
                placeholder="Alias du sous-composant"
              />
            </div>
          </template>
          <template v-else>
            <div class="input input-bordered input-sm bg-base-200 flex items-center">
              {{ lockedTypeDisplay }}
            </div>
          </template>
        </div>
        <button
          v-if="!isRoot"
          type="button"
          class="btn btn-error btn-xs btn-square"
          @click="emit('remove')"
        >
          <IconLucideTrash class="w-4 h-4" aria-hidden="true" />
        </button>
      </div>

      <div class="px-4 py-4 space-y-5">
        <section v-if="isRoot" class="space-y-3">
          <div class="flex items-center justify-between gap-2">
            <h4 :class="headingClass">
              {{ isRoot ? 'Champs personnalisés du composant' : 'Champs personnalisés' }}
            </h4>
            <button type="button" class="btn btn-outline btn-xs" @click="addCustomField">
              <IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
              Ajouter
            </button>
          </div>
          <p v-if="!(node.customFields?.length)" class="text-xs text-gray-500">
            Aucun champ n'a encore été défini.
          </p>
          <div v-else class="space-y-2">
            <div
              v-for="(field, index) in node.customFields"
              :key="`field-${index}`"
              class="border border-base-200 rounded-md p-3 space-y-2 transition-colors"
              :class="customFieldReorderClass(index)"
              draggable="true"
              @dragstart="onCustomFieldDragStart(index, $event)"
              @dragenter="onCustomFieldDragEnter(index)"
              @dragover.prevent
              @drop="onCustomFieldDrop(index)"
              @dragend="onCustomFieldDragEnd"
            >
              <div class="flex items-start justify-between gap-2">
                <div class="flex items-center gap-2">
                  <button
                    type="button"
                    class="btn btn-ghost btn-xs btn-square cursor-grab active:cursor-grabbing"
                    title="Réorganiser"
                    draggable="true"
                    @dragstart.stop="onCustomFieldDragStart(index, $event)"
                  >
                    <IconLucideGripVertical class="w-4 h-4" aria-hidden="true" />
                  </button>
                </div>
                <div class="flex-1 space-y-2">
                  <div class="grid grid-cols-1 md:grid-cols-2 gap-2">
                    <input
                      v-model="field.name"
                      type="text"
                      class="input input-bordered input-xs"
                      placeholder="Nom du champ"
                    />
                    <select v-model="field.type" class="select select-bordered select-xs">
                      <option value="text">Texte</option>
                      <option value="number">Nombre</option>
                      <option value="select">Liste</option>
                      <option value="boolean">Oui/Non</option>
                      <option value="date">Date</option>
                    </select>
                  </div>
                  <div class="flex items-center gap-2 text-xs">
                    <input v-model="field.required" type="checkbox" class="checkbox checkbox-xs" />
                    Obligatoire
                  </div>
                  <textarea
                    v-if="field.type === 'select'"
                    v-model="field.optionsText"
                    class="textarea textarea-bordered textarea-xs h-20"
                    placeholder="Option 1&#10;Option 2"
                  ></textarea>
                </div>
                <button
                  type="button"
                  class="btn btn-error btn-xs btn-square"
                  @click="removeCustomField(index)"
                >
                  <IconLucideTrash class="w-4 h-4" aria-hidden="true" />
                </button>
              </div>
            </div>
          </div>
        </section>

        <section v-if="isRoot" class="space-y-3">
          <div class="flex items-center justify-between gap-2">
            <h4 :class="headingClass">
              {{ isRoot ? 'Produits inclus par défaut' : 'Produits' }}
            </h4>
            <button type="button" class="btn btn-outline btn-xs" @click="addProduct">
              <IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
              Ajouter
            </button>
          </div>
          <p v-if="!(node.products?.length)" class="text-xs text-gray-500">
            Aucun produit défini.
          </p>
          <div v-else class="space-y-2">
            <div
              v-for="(product, index) in node.products"
              :key="`product-${index}`"
              class="relative border border-base-200 rounded-md p-3 pl-10 space-y-3 transition-colors"
              :class="productReorderClass(index)"
              @dragenter="onProductDragEnter(index)"
              @dragover="onProductDragOver"
              @drop="onProductDrop(index)"
            >
              <button
                type="button"
                class="absolute left-2 top-3 btn btn-ghost btn-xs btn-square cursor-grab active:cursor-grabbing"
                draggable="true"
                title="Réorganiser"
                @dragstart="onProductDragStart(index, $event)"
                @dragend="onProductDragEnd"
              >
                <IconLucideGripVertical class="w-4 h-4" aria-hidden="true" />
              </button>
              <div class="flex items-start justify-between gap-2">
                <div class="flex-1 space-y-3">
                  <div class="form-control">
                    <label class="label py-1"><span class="label-text text-xs">Famille de produit</span></label>
                    <select
                      v-model="product.typeProductId"
                      class="select select-bordered select-xs"
                      @change="handleProductTypeSelect(product)"
                    >
                      <option value="">
                        Sélectionner une famille
                      </option>
                      <option
                        v-for="type in productTypes"
                        :key="type.id"
                        :value="type.id"
                      >
                        {{ formatProductTypeOption(type) }}
                      </option>
                    </select>
                  </div>
                </div>
                <button type="button" class="btn btn-error btn-xs btn-square" @click="removeProduct(index)">
                  <IconLucideTrash class="w-4 h-4" aria-hidden="true" />
                </button>
              </div>
            </div>
          </div>
        </section>

        <section v-if="isRoot" class="space-y-3">
          <div class="flex items-center justify-between gap-2">
            <h4 :class="headingClass">
              {{ isRoot ? 'Pièces incluses par défaut' : 'Pièces' }}
            </h4>
            <button type="button" class="btn btn-outline btn-xs" @click="addPiece">
              <IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
              Ajouter
            </button>
          </div>
          <p v-if="!(node.pieces?.length)" class="text-xs text-gray-500">
            Aucune pièce définie.
          </p>
          <div v-else class="space-y-2">
            <div
              v-for="(piece, index) in node.pieces"
              :key="`piece-${index}`"
              class="relative border border-base-200 rounded-md p-3 pl-10 space-y-3 transition-colors"
              :class="pieceReorderClass(index)"
              @dragenter="onPieceDragEnter(index)"
              @dragover="onPieceDragOver"
              @drop="onPieceDrop(index)"
            >
              <button
                type="button"
                class="absolute left-2 top-3 btn btn-ghost btn-xs btn-square cursor-grab active:cursor-grabbing"
                draggable="true"
                title="Réorganiser"
                @dragstart="onPieceDragStart(index, $event)"
                @dragend="onPieceDragEnd"
              >
                <IconLucideGripVertical class="w-4 h-4" aria-hidden="true" />
              </button>
              <div class="flex items-start justify-between gap-2">
                <div class="flex-1 space-y-3">
                  <div class="form-control">
                    <label class="label"><span class="label-text">Famille de pièce</span></label>
                    <div>
                      <select
                        v-model="piece.typePieceId"
                        class="select select-bordered select-xs"
                        @change="handlePieceTypeSelect(piece)"
                      >
                        <option value="">
                          Sélectionner une famille
                        </option>
                        <option
                          v-for="type in pieceTypes"
                          :key="type.id"
                          :value="type.id"
                        >
                          {{ formatPieceTypeOption(type) }}
                        </option>
                      </select>
                    </div>
                    <p class="mt-1 text-[11px] text-gray-500">
                      {{ piece.typePieceId ? `Sélection : ${getPieceTypeLabel(piece.typePieceId) || 'Inconnue'}` : 'Aucune famille sélectionnée' }}
                    </p>
                  </div>
                </div>
                <button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(index)">
                  <IconLucideTrash class="w-4 h-4" aria-hidden="true" />
                </button>
              </div>
            </div>
          </div>
        </section>

        <section v-if="canManageSubcomponents || hasSubcomponents" class="space-y-3">
          <div class="flex items-center justify-between gap-2">
            <h4 :class="headingClass">Sous-composants</h4>
            <button
              v-if="canManageSubcomponents"
              type="button"
              class="btn btn-outline btn-xs"
              @click="addSubComponent"
            >
              <IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
              Ajouter
            </button>
          </div>
          <p v-if="!isRoot && canManageSubcomponents" class="text-[11px] text-gray-500">
            Sélectionnez uniquement la famille de ce sous-composant ; il sera configuré via son propre modèle.
          </p>
          <p v-if="!hasSubcomponents" class="text-xs text-gray-500">
            Aucun sous-composant défini.
          </p>
          <div v-else class="space-y-3">
            <div
              v-for="(subComponent, index) in node.subcomponents"
              :key="`sub-${index}`"
              class="relative pl-8 transition-shadow rounded-lg"
              :class="subcomponentReorderClass(index)"
              @dragenter="onSubcomponentDragEnter(index)"
              @dragover="onSubcomponentDragOver"
              @drop="onSubcomponentDrop(index)"
            >
              <button
                type="button"
                class="absolute left-0 top-4 btn btn-ghost btn-xs btn-square cursor-grab active:cursor-grabbing"
                draggable="true"
                title="Réorganiser"
                @dragstart="onSubcomponentDragStart(index, $event)"
                @dragend="onSubcomponentDragEnd"
              >
                <IconLucideGripVertical class="w-4 h-4" aria-hidden="true" />
              </button>
              <StructureNodeEditor
                :node="subComponent"
                :depth="depth + 1"
                :component-types="componentTypes"
                :piece-types="pieceTypes"
                :product-types="productTypes"
                :allow-subcomponents="childAllowSubcomponents"
                :max-subcomponent-depth="maxSubcomponentDepth"
                @remove="removeSubComponent(index)"
              />
            </div>
          </div>
        </section>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import IconLucidePlus from '~icons/lucide/plus'
import IconLucideTrash from '~icons/lucide/trash'
import IconLucideGripVertical from '~icons/lucide/grip-vertical'
import type { ComponentModelPiece, ComponentModelProduct, ComponentModelStructureNode } from '~/shared/types/inventory'

defineOptions({ name: 'StructureNodeEditor' })

type ModelTypeOption = {
  id: string
  name: string
  code?: string | null
}

type EditableStructureNode = ComponentModelStructureNode & {
  customFields?: any[]
  pieces?: ComponentModelPiece[]
  products?: ComponentModelProduct[]
}

const props = withDefaults(defineProps<{
  node: EditableStructureNode
  depth?: number
  componentTypes?: ModelTypeOption[]
  pieceTypes?: ModelTypeOption[]
  productTypes?: ModelTypeOption[]
  isRoot?: boolean
  lockType?: boolean
  lockedTypeLabel?: string
  allowSubcomponents?: boolean
  maxSubcomponentDepth?: number
}>(), {
  depth: 0,
  componentTypes: () => [],
  pieceTypes: () => [],
  productTypes: () => [],
  isRoot: false,
  lockType: false,
  lockedTypeLabel: '',
  allowSubcomponents: true,
  maxSubcomponentDepth: Infinity,
})

const emit = defineEmits(['remove'])

const componentTypes = computed(() => props.componentTypes ?? [])
const pieceTypes = computed(() => props.pieceTypes ?? [])
const productTypes = computed(() => props.productTypes ?? [])
const allowSubcomponents = computed(() => props.allowSubcomponents !== false)
const maxSubcomponentDepth = computed(() =>
  typeof props.maxSubcomponentDepth === 'number' ? props.maxSubcomponentDepth : Infinity,
)
const currentDepth = computed(() => Math.max(0, props.depth ?? 0))
const canManageSubcomponents = computed(
  () => allowSubcomponents.value && currentDepth.value < maxSubcomponentDepth.value,
)
const childAllowSubcomponents = computed(
  () => allowSubcomponents.value && currentDepth.value + 1 < maxSubcomponentDepth.value,
)
const hasSubcomponents = computed(
  () => Array.isArray(props.node?.subcomponents) && props.node.subcomponents.length > 0,
)

const depthClasses = ['', 'ml-4', 'ml-8', 'ml-12', 'ml-16', 'ml-20']
const containerClass = computed(() => {
  const level = currentDepth.value
  const index = Math.min(level, depthClasses.length - 1)
  return level === 0 ? 'space-y-4' : `${depthClasses[index]} space-y-4`
})

const headingClass = computed(() => (props.isRoot ? 'text-sm font-semibold' : 'text-xs font-semibold'))
const lockedTypeDisplay = computed(() => {
  if (props.lockedTypeLabel) {
    return props.lockedTypeLabel
  }
  return getComponentTypeLabel(props.node?.typeComposantId) || 'Famille non définie'
})

const formatModelTypeOption = (type: ModelTypeOption | undefined | null) =>
  type?.name ?? ''

const componentTypeMap = computed(() => {
  const map = new Map<string, ModelTypeOption>()
  componentTypes.value.forEach((type) => {
    if (type && typeof type.id === 'string') {
      map.set(type.id, type)
    }
  })
  return map
})

const componentTypeCodeMap = computed(() => {
  const map = new Map<string, ModelTypeOption>()
  componentTypes.value.forEach((type) => {
    const code = typeof type?.code === 'string' ? type.code.trim() : ''
    if (code) {
      map.set(code, type)
    }
  })
  return map
})

const pieceTypeMap = computed(() => {
  const map = new Map<string, ModelTypeOption>()
  pieceTypes.value.forEach((type) => {
    if (type && typeof type.id === 'string') {
      map.set(type.id, type)
    }
  })
  return map
})

const productTypeMap = computed(() => {
  const map = new Map<string, ModelTypeOption>()
  productTypes.value.forEach((type) => {
    if (type && typeof type.id === 'string') {
      map.set(type.id, type)
    }
  })
  return map
})

const getComponentTypeLabel = (id?: string) => {
  if (!id) return ''
  return formatModelTypeOption(componentTypeMap.value.get(id))
}

const getPieceTypeLabel = (id?: string) => {
  if (!id) return ''
  return formatModelTypeOption(pieceTypeMap.value.get(id))
}

const getProductTypeLabel = (id?: string) => {
  if (!id) return ''
  return formatModelTypeOption(productTypeMap.value.get(id))
}

const formatComponentTypeOption = (type: ModelTypeOption | undefined | null) =>
  formatModelTypeOption(type)

const formatPieceTypeOption = (type: ModelTypeOption | undefined | null) =>
  formatModelTypeOption(type)

const formatProductTypeOption = (type: ModelTypeOption | undefined | null) =>
  formatModelTypeOption(type)

const ensureArray = (key: 'customFields' | 'pieces' | 'products' | 'subcomponents') => {
  if (!Array.isArray((props.node as any)[key])) {
    if (key === 'subcomponents') {
      props.node.subcomponents = []
    } else if (key === 'products') {
      props.node.products = []
    } else {
      (props.node as any)[key] = []
    }
  }
}

const syncComponentType = (component: EditableStructureNode) => {
  if (!component) {
    return
  }
  if (props.isRoot) {
    component.typeComposantId = ''
    component.typeComposantLabel = ''
    component.familyCode = ''
    if (component.alias) {
      component.alias = ''
    }
    return
  }
  if (props.lockType && props.isRoot) {
    if (props.lockedTypeLabel) {
      component.typeComposantLabel = props.lockedTypeLabel
      if (!component.alias || component.alias === component.typeComposantLabel) {
        component.alias = props.lockedTypeLabel
      }
    }
    if (component.typeComposantId) {
      const option = componentTypeMap.value.get(component.typeComposantId)
      component.familyCode = option?.code ?? component.familyCode
    }
    return
  }
  const id = typeof component.typeComposantId === 'string'
    ? component.typeComposantId
    : ''

  if (!id) {
    const code =
      typeof component.familyCode === 'string' && component.familyCode
        ? component.familyCode
        : ''
    if (code) {
      const codeMatch = componentTypeCodeMap.value.get(code)
      if (codeMatch?.id) {
        component.typeComposantId = codeMatch.id
        component.typeComposantLabel = formatModelTypeOption(codeMatch)
        component.familyCode = codeMatch.code ?? component.familyCode
        if (!component.alias || component.alias === '' || component.alias === lockedTypeDisplay.value) {
          component.alias = codeMatch.name || component.typeComposantLabel
        }
        return
      }
    }
    component.typeComposantLabel = ''
    component.familyCode = ''
    return
  }

  const option = componentTypeMap.value.get(id)
  if (!option) {
    component.typeComposantLabel = ''
    component.familyCode = ''
    return
  }

  component.typeComposantLabel = formatModelTypeOption(option)
  component.familyCode = option.code ?? component.familyCode
  if (!component.alias || component.alias === '' || component.alias === lockedTypeDisplay.value) {
    component.alias = option.name || component.typeComposantLabel
  }
}

const updatePieceTypeLabel = (piece: ComponentModelPiece & Record<string, any>) => {
  if (!piece) return

  if (piece.typePieceId) {
    const option = pieceTypeMap.value.get(piece.typePieceId)
    if (option) {
      piece.typePieceLabel = formatPieceTypeOption(option)
      return
    }
  }

  if (piece.typePieceLabel) {
    const normalized = piece.typePieceLabel.trim().toLowerCase()
    if (normalized) {
      const match = pieceTypes.value.find((type) => {
        const formatted = formatPieceTypeOption(type).toLowerCase()
        const name = (type?.name ?? '').toLowerCase()
        const code = (type?.code ?? '').toLowerCase()
        return formatted === normalized || name === normalized || (!!code && code === normalized)
      })
      if (match) {
        piece.typePieceId = match.id
        piece.typePieceLabel = formatPieceTypeOption(match)
        return
      }
    }
  }
}

const updateProductTypeLabel = (product: ComponentModelProduct & Record<string, any>) => {
  if (!product) return

  if (product.typeProductId) {
    const option = productTypeMap.value.get(product.typeProductId)
    if (option) {
      product.typeProductLabel = formatProductTypeOption(option)
      product.familyCode = option.code ?? product.familyCode ?? ''
      return
    }
  }

  if (product.typeProductLabel) {
    const normalized = product.typeProductLabel.trim().toLowerCase()
    if (normalized) {
      const match = productTypes.value.find((type) => {
        const formatted = formatProductTypeOption(type).toLowerCase()
        const name = (type?.name ?? '').toLowerCase()
        const code = (type?.code ?? '').toLowerCase()
        return formatted === normalized || name === normalized || (!!code && code === normalized)
      })
      if (match) {
        product.typeProductId = match.id
        product.typeProductLabel = formatProductTypeOption(match)
        product.familyCode = match.code ?? product.familyCode ?? ''
        return
      }
    }
  }
}

const syncPieceLabels = (pieces?: any[]) => {
  if (!Array.isArray(pieces)) {
    return
  }
  pieces.forEach((piece) => {
    updatePieceTypeLabel(piece)
  })
}

const syncProductLabels = (products?: any[]) => {
  if (!Array.isArray(products)) {
    return
  }
  products.forEach((product) => {
    updateProductTypeLabel(product)
  })
}

const handleComponentTypeSelect = (component: any) => {
  syncComponentType(component)
}

const handlePieceTypeSelect = (piece: ComponentModelPiece & Record<string, any>) => {
  if (!piece) {
    return
  }
  const id = typeof piece.typePieceId === 'string' ? piece.typePieceId : ''
  if (!id) {
    piece.typePieceLabel = ''
    return
  }
  const option = pieceTypeMap.value.get(id)
  if (!option) {
    piece.typePieceId = ''
    piece.typePieceLabel = ''
    return
  }
  piece.typePieceLabel = formatPieceTypeOption(option)
}

const handleProductTypeSelect = (product: ComponentModelProduct & Record<string, any>) => {
  if (!product) {
    return
  }
  const id = typeof product.typeProductId === 'string' ? product.typeProductId : ''
  if (!id) {
    product.typeProductLabel = ''
    return
  }
  const option = productTypeMap.value.get(id)
  if (!option) {
    product.typeProductId = ''
    product.typeProductLabel = ''
    return
  }
  product.typeProductLabel = formatProductTypeOption(option)
  product.familyCode = option.code ?? product.familyCode ?? ''
}

const customFieldDragState = ref({
  draggingIndex: null as number | null,
  dropTargetIndex: null as number | null,
})

const reindexCustomFields = () => {
  if (!Array.isArray(props.node.customFields)) {
    return
  }
  props.node.customFields.forEach((field: any, index: number) => {
    if (!field || typeof field !== 'object') {
      return
    }
    field.orderIndex = index
  })
}

const resetCustomFieldDragState = () => {
  customFieldDragState.value.draggingIndex = null
  customFieldDragState.value.dropTargetIndex = null
}

const onCustomFieldDragStart = (index: number, event: DragEvent) => {
  customFieldDragState.value.draggingIndex = index
  customFieldDragState.value.dropTargetIndex = index
  if (event.dataTransfer) {
    event.dataTransfer.effectAllowed = 'move'
  }
}

const onCustomFieldDragEnter = (index: number) => {
  if (customFieldDragState.value.draggingIndex === null) {
    return
  }
  customFieldDragState.value.dropTargetIndex = index
}

const onCustomFieldDrop = (index: number) => {
  if (!Array.isArray(props.node.customFields)) {
    resetCustomFieldDragState()
    return
  }
  const from = customFieldDragState.value.draggingIndex
  const to = index
  if (from === null || to === null) {
    resetCustomFieldDragState()
    return
  }
  moveItemInPlace(props.node.customFields, from, to)
  reindexCustomFields()
  resetCustomFieldDragState()
}

const onCustomFieldDragEnd = () => {
  resetCustomFieldDragState()
}

const customFieldReorderClass = (index: number) => {
  if (customFieldDragState.value.draggingIndex === index) {
    return 'border-dashed border-primary'
  }
  if (
    customFieldDragState.value.draggingIndex !== null &&
    customFieldDragState.value.dropTargetIndex === index &&
    customFieldDragState.value.draggingIndex !== index
  ) {
    return 'border-primary border-dashed bg-primary/5'
  }
  return ''
}

const addCustomField = () => {
  ensureArray('customFields')
  const nextIndex = Array.isArray(props.node.customFields)
    ? props.node.customFields.length
    : 0
  props.node.customFields.push({
    name: '',
    type: 'text',
    required: false,
    optionsText: '',
    options: [],
    orderIndex: nextIndex,
  })
  reindexCustomFields()
}

const removeCustomField = (index: number) => {
  if (!Array.isArray(props.node.customFields)) return
  props.node.customFields.splice(index, 1)
  reindexCustomFields()
}

const addPiece = () => {
  ensureArray('pieces')
  props.node.pieces.push({
    typePieceId: '',
    typePieceLabel: '',
    reference: '',
    familyCode: '',
    role: '',
  })
}

const removePiece = (index: number) => {
  if (!Array.isArray(props.node.pieces)) return
  props.node.pieces.splice(index, 1)
}

const addProduct = () => {
  ensureArray('products')
  props.node.products.push({
    typeProductId: '',
    typeProductLabel: '',
    familyCode: '',
  })
}

const removeProduct = (index: number) => {
  if (!Array.isArray(props.node.products)) return
  props.node.products.splice(index, 1)
}

const addSubComponent = () => {
  if (!canManageSubcomponents.value) {
    return
  }
  ensureArray('subcomponents')
  props.node.subcomponents.push({
    typeComposantId: '',
    typeComposantLabel: '',
    modelId: '',
    familyCode: '',
    alias: '',
    subcomponents: [],
  })
}

const removeSubComponent = (index: number) => {
  if (!Array.isArray(props.node.subcomponents)) return
  props.node.subcomponents.splice(index, 1)
}

const draggingPieceIndex = ref<number | null>(null)
const pieceDropTargetIndex = ref<number | null>(null)
const draggingProductIndex = ref<number | null>(null)
const productDropTargetIndex = ref<number | null>(null)
const draggingSubcomponentIndex = ref<number | null>(null)
const subcomponentDropTargetIndex = ref<number | null>(null)

const moveItemInPlace = <T,>(list: T[], from: number, to: number) => {
  if (from === to) {
    return
  }
  if (from < 0 || to < 0 || from >= list.length || to >= list.length) {
    return
  }
  const updated = list.slice()
  const [item] = updated.splice(from, 1)
  updated.splice(to, 0, item)
  list.splice(0, list.length, ...updated)
}

const resetPieceDragState = () => {
  draggingPieceIndex.value = null
  pieceDropTargetIndex.value = null
}

const resetProductDragState = () => {
  draggingProductIndex.value = null
  productDropTargetIndex.value = null
}

const onPieceDragStart = (index: number, event: DragEvent) => {
  draggingPieceIndex.value = index
  pieceDropTargetIndex.value = index
  if (event.dataTransfer) {
    event.dataTransfer.effectAllowed = 'move'
  }
}

const onPieceDragEnter = (index: number) => {
  if (draggingPieceIndex.value === null) {
    return
  }
  pieceDropTargetIndex.value = index
}

const onPieceDragOver = (event: DragEvent) => {
  event.preventDefault()
}

const onPieceDrop = (index: number) => {
  if (!Array.isArray(props.node.pieces)) {
    resetPieceDragState()
    return
  }
  const from = draggingPieceIndex.value
  const to = index
  if (from === null || to === null) {
    resetPieceDragState()
    return
  }
  moveItemInPlace(props.node.pieces, from, to)
  resetPieceDragState()
}

const onPieceDragEnd = () => {
  resetPieceDragState()
}

const pieceReorderClass = (index: number) => {
  if (draggingPieceIndex.value === index) {
    return 'border-dashed border-primary'
  }
  if (
    draggingPieceIndex.value !== null &&
    pieceDropTargetIndex.value === index &&
    draggingPieceIndex.value !== index
  ) {
    return 'border-primary border-dashed bg-primary/5'
  }
  return ''
}

const onProductDragStart = (index: number, event: DragEvent) => {
  draggingProductIndex.value = index
  productDropTargetIndex.value = index
  if (event.dataTransfer) {
    event.dataTransfer.effectAllowed = 'move'
  }
}

const onProductDragEnter = (index: number) => {
  if (draggingProductIndex.value === null) {
    return
  }
  productDropTargetIndex.value = index
}

const onProductDragOver = (event: DragEvent) => {
  event.preventDefault()
}

const onProductDrop = (index: number) => {
  if (!Array.isArray(props.node.products)) {
    resetProductDragState()
    return
  }
  const from = draggingProductIndex.value
  const to = index
  if (from === null || to === null) {
    resetProductDragState()
    return
  }
  moveItemInPlace(props.node.products, from, to)
  resetProductDragState()
}

const onProductDragEnd = () => {
  resetProductDragState()
}

const productReorderClass = (index: number) => {
  if (draggingProductIndex.value === index) {
    return 'border-dashed border-primary'
  }
  if (
    draggingProductIndex.value !== null &&
    productDropTargetIndex.value === index &&
    draggingProductIndex.value !== index
  ) {
    return 'border-primary border-dashed bg-primary/5'
  }
  return ''
}

const resetSubcomponentDragState = () => {
  draggingSubcomponentIndex.value = null
  subcomponentDropTargetIndex.value = null
}

const onSubcomponentDragStart = (index: number, event: DragEvent) => {
  draggingSubcomponentIndex.value = index
  subcomponentDropTargetIndex.value = index
  if (event.dataTransfer) {
    event.dataTransfer.effectAllowed = 'move'
  }
}

const onSubcomponentDragEnter = (index: number) => {
  if (draggingSubcomponentIndex.value === null) {
    return
  }
  subcomponentDropTargetIndex.value = index
}

const onSubcomponentDragOver = (event: DragEvent) => {
  event.preventDefault()
}

const onSubcomponentDrop = (index: number) => {
  if (!Array.isArray(props.node.subcomponents)) {
    resetSubcomponentDragState()
    return
  }
  const from = draggingSubcomponentIndex.value
  const to = index
  if (from === null || to === null) {
    resetSubcomponentDragState()
    return
  }
  moveItemInPlace(props.node.subcomponents, from, to)
  resetSubcomponentDragState()
}

const onSubcomponentDragEnd = () => {
  resetSubcomponentDragState()
}

const subcomponentReorderClass = (index: number) => {
  if (draggingSubcomponentIndex.value === index) {
    return 'ring-2 ring-primary'
  }
  if (
    draggingSubcomponentIndex.value !== null &&
    subcomponentDropTargetIndex.value === index &&
    draggingSubcomponentIndex.value !== index
  ) {
    return 'ring-2 ring-primary/70'
  }
  return ''
}

watch(
  canManageSubcomponents,
  (allowed) => {
    if (!allowed && Array.isArray(props.node.subcomponents) && props.node.subcomponents.length) {
      props.node.subcomponents.splice(0, props.node.subcomponents.length)
    }
  },
  { immediate: true }
)

watch(componentTypes, () => {
  syncComponentType(props.node)
}, { deep: true, immediate: true })

watch(
  () => props.node.typeComposantId,
  () => {
    syncComponentType(props.node)
  },
)

watch(pieceTypes, () => {
  syncPieceLabels(props.node?.pieces)
}, { deep: true, immediate: true })

watch(
  () => props.node.pieces,
  (value) => {
    syncPieceLabels(value)
  },
  { deep: true }
)

watch(productTypes, () => {
  syncProductLabels(props.node?.products)
}, { deep: true, immediate: true })

watch(
  () => props.node.products,
  (value) => {
    syncProductLabels(value)
  },
  { deep: true }
)

watch(
  () => props.node.customFields,
  (value) => {
    if (!Array.isArray(value)) {
      return
    }
    value.sort((a: any, b: any) => {
      const left = typeof a?.orderIndex === 'number' ? a.orderIndex : 0
      const right = typeof b?.orderIndex === 'number' ? b.orderIndex : 0
      return left - right
    })
    reindexCustomFields()
  },
  { deep: true }
)

watch(
  () => [props.lockedTypeLabel, props.lockType],
  () => {
    if (props.lockType && props.isRoot) {
      const label = props.lockedTypeLabel || lockedTypeDisplay.value
      props.node.typeComposantLabel = label
      if (label && (!props.node.alias || props.node.alias === lockedTypeDisplay.value)) {
        props.node.alias = label
      }
      if (props.node.typeComposantId) {
        const option = componentTypeMap.value.get(props.node.typeComposantId)
        props.node.familyCode = option?.code ?? props.node.familyCode
      }
    }
  },
  { immediate: true }
)
</script>
