/**
 * Element management — CRUD, groups, drag-drop layers, tree manipulation.
 * Merged into Vue app via: methods: { ...ElementMethods, ... }
 */

const ElementMethods = {
  typesInCategory(catKey) {
    const result = {};
    for (const [type, def] of Object.entries(TYPE_DEFS)) {
      if (def.cat === catKey) result[type] = def;
    }
    return result;
  },

  addElementOfType(type) {
    const def = TYPE_DEFS[type];
    if (!def) return;
    this.pushUndo();
    const el = {
      type,
      args: [...(def.defaults || [])],
      props: {},
      _id: _nextId++,
      _depth: 0
    };
    this._insertNearSelection(el);
    this.selectedId = el._id;
    this.showPalette = false;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  addElement() {
    this.addElementOfType(this.newElType || 'circle');
  },

  addConditional() {
    this.pushUndo();
    const node = {
      isConditional: true,
      condition: '$zone==danger',
      children: [],
      _id: _nextId++,
      _depth: 0
    };
    this._insertNearSelection(node);
    this.selectedId = node._id;
    this.showPalette = false;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  addGroup() {
    this.pushUndo();
    const node = {
      isGroup: true,
      name: 'Groupe',
      children: [],
      _id: _nextId++,
      _depth: 0
    };
    this._insertNearSelection(node);
    this.selectedId = node._id;
    this.showPalette = false;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  addForLoop() {
    this.pushUndo();
    const node = {
      isForLoop: true,
      varName: '$i',
      from: '0',
      to: '11',
      step: '1',
      children: [],
      _id: _nextId++,
      _depth: 0
    };
    this._insertNearSelection(node);
    this.selectedId = node._id;
    this.showPalette = false;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  /** Insert a new node near the current selection:
   *  - No selection → append to root
   *  - Group/conditional selected → insert inside it
   *  - Regular element selected → insert after it at same level */
  _insertNearSelection(node) {
    const sel = this.selectedElement;
    if (!sel) {
      this.face.elements.push(node);
      return;
    }
    if (sel.isGroup || sel.isConditional || sel.isForLoop) {
      sel.children = sel.children || [];
      sel.children.push(node);
      node._depth = (sel._depth || 0) + 1;
      return;
    }
    const findParent = (arr) => {
      const idx = arr.findIndex(e => e._id === sel._id);
      if (idx >= 0) return { arr, idx };
      for (const e of arr) {
        if ((e.isConditional || e.isGroup || e.isForLoop) && e.children) {
          const r = findParent(e.children);
          if (r) return r;
        }
      }
      return null;
    };
    const parent = findParent(this.face.elements);
    if (parent) {
      node._depth = sel._depth || 0;
      parent.arr.splice(parent.idx + 1, 0, node);
    } else {
      this.face.elements.push(node);
    }
  },

  // ── Group / Ungroup ──

  groupSelectedElement() {
    const el = this.selectedElement;
    if (!el || el.isGroup) return;
    this.pushUndo();
    const findParent = (arr) => {
      const idx = arr.findIndex(e => e._id === el._id);
      if (idx >= 0) return { arr, idx };
      for (const e of arr) {
        if ((e.isConditional || e.isGroup || e.isForLoop) && e.children) {
          const result = findParent(e.children);
          if (result) return result;
        }
      }
      return null;
    };
    const parent = findParent(this.face.elements);
    if (!parent) return;
    const [removed] = parent.arr.splice(parent.idx, 1);
    const group = {
      isGroup: true,
      name: 'Groupe',
      children: [removed],
      _id: _nextId++,
      _depth: removed._depth || 0
    };
    removed._depth = (group._depth || 0) + 1;
    parent.arr.splice(parent.idx, 0, group);
    this.selectedId = group._id;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  ungroupSelected() {
    const el = this.selectedElement;
    if (!el || !el.isGroup) return;
    this.pushUndo();
    const findParent = (arr) => {
      const idx = arr.findIndex(e => e._id === el._id);
      if (idx >= 0) return { arr, idx };
      for (const e of arr) {
        if ((e.isConditional || e.isGroup || e.isForLoop) && e.children) {
          const result = findParent(e.children);
          if (result) return result;
        }
      }
      return null;
    };
    const parent = findParent(this.face.elements);
    if (!parent) return;
    parent.arr.splice(parent.idx, 1, ...(el.children || []));
    this.recalcDepths(this.face.elements, 0);
    this.selectedId = null;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  // ── Layer click & context menu ──

  onLayerClick(e, el) {
    e.stopPropagation();
    if (e.ctrlKey || e.metaKey) {
      const idx = this.selectedIds.indexOf(el.id);
      if (idx >= 0) this.selectedIds.splice(idx, 1);
      else this.selectedIds.push(el.id);
      this._selectionAnchor = el.id;
    } else if (e.shiftKey && this._selectionAnchor != null) {
      const flat = this.flatElements;
      const anchorIdx = flat.findIndex(f => f.id === this._selectionAnchor);
      const clickIdx = flat.findIndex(f => f.id === el.id);
      if (anchorIdx >= 0 && clickIdx >= 0) {
        const start = Math.min(anchorIdx, clickIdx);
        const end = Math.max(anchorIdx, clickIdx);
        this.selectedIds = flat.slice(start, end + 1).map(f => f.id);
      }
    } else {
      // Force exactly this one element — prevents any stale multi-selection
      this.selectedIds = [el.id];
      this._selectionAnchor = el.id;
    }
    // Defer render to next tick to let Vue stabilize selectedIds first
    this.$nextTick(() => this.renderCanvas());
  },

  onLayerListContextMenu(e) {
    e.preventDefault();
    this.selectedIds = [];
    CanvasInteractions.showContextMenu(e.clientX, e.clientY, null, null, true);
  },

  onLayerContextMenu(e, el) {
    e.preventDefault();
    if (!this.selectedIds.includes(el.id)) {
      this.selectedIds = [el.id];
      this._selectionAnchor = el.id;
    }
    if (this.selectedIds.length > 1) {
      CanvasInteractions.showContextMenu(e.clientX, e.clientY, null, this.selectedIds);
    } else {
      CanvasInteractions.showContextMenu(e.clientX, e.clientY, el.node);
    }
    this.renderCanvas();
  },

  // ── Multi-selection batch operations ──

  deleteSelectedElements() {
    if (this.selectedIds.length === 0) return;
    this.pushUndo();
    for (const id of [...this.selectedIds]) {
      const removeFrom = (arr) => {
        const idx = arr.findIndex(e => e._id === id);
        if (idx >= 0) { arr.splice(idx, 1); return true; }
        for (const el of arr) {
          if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
            if (removeFrom(el.children)) return true;
          }
        }
        return false;
      };
      removeFrom(this.face.elements);
    }
    this.selectedIds = [];
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  groupSelectedElements() {
    if (this.selectedIds.length < 2) return;
    this.pushUndo();
    // Find position of first selected element (in flat order) before removing
    const flat = this.flatElements;
    const firstSelIdx = flat.findIndex(f => this.selectedIds.includes(f.id));
    const firstId = firstSelIdx >= 0 ? flat[firstSelIdx].id : null;
    const firstNode = firstId ? this.findNodeById(this.face.elements, firstId) : null;
    // Find parent array and index of first selected element
    let insertArr = this.face.elements;
    let insertIdx = insertArr.length;
    if (firstNode) {
      const findParent = (arr) => {
        const idx = arr.findIndex(e => e._id === firstNode._id);
        if (idx >= 0) return { arr, idx };
        for (const e of arr) {
          if ((e.isConditional || e.isGroup || e.isForLoop) && e.children) {
            const r = findParent(e.children);
            if (r) return r;
          }
        }
        return null;
      };
      const parent = findParent(this.face.elements);
      if (parent) { insertArr = parent.arr; insertIdx = parent.idx; }
    }
    const nodes = [];
    for (const id of [...this.selectedIds]) {
      const removed = this.removeFromTree(id);
      if (removed) nodes.push(removed);
    }
    if (nodes.length === 0) return;
    const group = {
      isGroup: true,
      name: 'Groupe',
      children: nodes,
      _id: _nextId++,
      _depth: 0
    };
    // Recalculate insert index (removals may have shifted it)
    const actualIdx = Math.min(insertIdx, insertArr.length);
    insertArr.splice(actualIdx, 0, group);
    this.recalcDepths(this.face.elements, 0);
    this.selectedIds = [group._id];
    this._selectionAnchor = group._id;
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  // ── Unified selection operations (single + multi) ──

  deleteSelection() {
    if (this.selectedIds.length > 1) this.deleteSelectedElements();
    else if (this.selectedIds.length === 1) this.deleteEl(this.selectedIds[0]);
  },

  copySelection() {
    if (this.selectedIds.length > 1) CanvasInteractions.copyElements(this.selectedIds);
    else if (this.selectedElement) CanvasInteractions.copyElement(this.selectedElement);
  },

  duplicateSelection() {
    if (this.selectedIds.length > 1) {
      CanvasInteractions.copyElements(this.selectedIds);
      CanvasInteractions.pasteElement();
    } else if (this.selectedElement) {
      CanvasInteractions.duplicateElement(this.selectedElement);
    }
  },

  groupSelection() {
    if (this.selectedIds.length > 1) this.groupSelectedElements();
    else if (this.selectedElement && !this.selectedElement.isGroup) this.groupSelectedElement();
  },

  // ── Collapse / Expand ──

  toggleCollapse(id) {
    const idx = this.collapsedIds.indexOf(id);
    if (idx >= 0) this.collapsedIds.splice(idx, 1);
    else this.collapsedIds.push(id);
  },

  // ── Move / Delete / Visibility ──

  moveEl(id, dir) {
    this.pushUndo();
    const moveIn = (arr) => {
      const idx = arr.findIndex(e => e._id === id);
      if (idx >= 0) {
        const newIdx = idx + dir;
        if (newIdx >= 0 && newIdx < arr.length) {
          const tmp = arr[idx];
          arr[idx] = arr[newIdx];
          arr[newIdx] = tmp;
          return true;
        }
        return false;
      }
      for (const el of arr) {
        if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
          if (moveIn(el.children)) return true;
        }
      }
      return false;
    };
    moveIn(this.face.elements);
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  deleteEl(id) {
    this.pushUndo();
    const removeFrom = (arr) => {
      const idx = arr.findIndex(e => e._id === id);
      if (idx >= 0) { arr.splice(idx, 1); return true; }
      for (const el of arr) {
        if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
          if (removeFrom(el.children)) return true;
        }
      }
      return false;
    };
    removeFrom(this.face.elements);
    const selIdx = this.selectedIds.indexOf(id);
    if (selIdx >= 0) this.selectedIds.splice(selIdx, 1);
    this.syncCodeFromFace();
    this.renderCanvas();
  },

  toggleVisibility(id) {
    const find = (els) => {
      for (const el of els) {
        if (el._id === id) return el;
        if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
          const found = find(el.children);
          if (found) return found;
        }
      }
      return null;
    };
    const el = find(this.face.elements);
    if (el) {
      el._hidden = !el._hidden;
      this.renderCanvas();
    }
  },

  // ── Drag and drop layers ──

  dragLayerStart(e, el) {
    this.dragSource = el.id;
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', String(el.id));
  },

  dragLayerOver(e, el) {
    if (this.dragSource === el.id) { this.dragTarget = null; return; }
    e.preventDefault();
    const rect = e.currentTarget.getBoundingClientRect();
    const y = e.clientY - rect.top;
    const h = rect.height;
    if ((el.isConditional || el.isGroup || el.isForLoop) && y > h * 0.25 && y < h * 0.75) {
      this.dragTarget = el.id;
      this.dragPos = 'into';
    } else if (y < h / 2) {
      this.dragTarget = el.id;
      this.dragPos = 'before';
    } else {
      this.dragTarget = el.id;
      this.dragPos = 'after';
    }
  },

  dropLayer(e, el) {
    e.preventDefault();
    if (!this.dragSource || !this.dragTarget || this.dragSource === this.dragTarget) {
      this.dragLayerEnd();
      return;
    }
    const sourceNode = this.findNodeById(this.face.elements, this.dragSource);
    if (!sourceNode) { this.dragLayerEnd(); return; }
    if ((sourceNode.isGroup || sourceNode.isConditional || sourceNode.isForLoop) && this.isDescendant(sourceNode, this.dragTarget)) {
      this.dragLayerEnd();
      return;
    }
    this.pushUndo();
    const removed = this.removeFromTree(this.dragSource);
    if (!removed) { this.dragLayerEnd(); return; }
    this.insertInTree(removed, this.dragTarget, this.dragPos);
    this.recalcDepths(this.face.elements, 0);
    this.syncCodeFromFace();
    this.renderCanvas();
    this.dragLayerEnd();
  },

  dragLayerEnd() {
    this.dragSource = null;
    this.dragTarget = null;
    this.dragPos = null;
  },

  // ── Tree helpers ──

  findNodeById(elements, id) {
    for (const el of elements) {
      if (el._id === id) return el;
      if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
        const found = this.findNodeById(el.children, id);
        if (found) return found;
      }
    }
    return null;
  },

  removeFromTree(id) {
    const remove = (arr) => {
      const idx = arr.findIndex(e => e._id === id);
      if (idx >= 0) return arr.splice(idx, 1)[0];
      for (const el of arr) {
        if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
          const found = remove(el.children);
          if (found) return found;
        }
      }
      return null;
    };
    return remove(this.face.elements);
  },

  insertInTree(node, targetId, pos) {
    const insert = (arr) => {
      const idx = arr.findIndex(e => e._id === targetId);
      if (idx >= 0) {
        if (pos === 'into' && (arr[idx].isConditional || arr[idx].isGroup || arr[idx].isForLoop)) {
          arr[idx].children = arr[idx].children || [];
          arr[idx].children.push(node);
        } else if (pos === 'before') {
          arr.splice(idx, 0, node);
        } else {
          arr.splice(idx + 1, 0, node);
        }
        return true;
      }
      for (const el of arr) {
        if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
          if (insert(el.children)) return true;
        }
      }
      return false;
    };
    if (!insert(this.face.elements)) {
      this.face.elements.push(node);
    }
  },

  isDescendant(parent, childId) {
    if (!parent.children) return false;
    for (const child of parent.children) {
      if (child._id === childId) return true;
      if ((child.isConditional || child.isGroup || child.isForLoop) && this.isDescendant(child, childId)) return true;
    }
    return false;
  },

  recalcDepths(elements, depth) {
    for (const el of elements) {
      el._depth = depth;
      if ((el.isConditional || el.isGroup || el.isForLoop) && el.children) {
        this.recalcDepths(el.children, depth + 1);
      }
    }
  },

  // ── SVG Import ──

  importSVGFile() {
    this.showPalette = false;
    this.$refs.svgFileInput.click();
  },

  onSVGFileSelected(e) {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      const result = SVGImport.parse(ev.target.result);
      if (!result.elements.length) {
        this.showToast('Aucun element SVG reconnu');
        return;
      }
      this.pushUndo();
      const group = {
        isGroup: true,
        name: file.name.replace(/\.svg$/i, ''),
        children: result.elements,
        _id: _nextId++,
        _depth: 0
      };
      assignIds(group.children, 1);
      this.face.elements.push(group);
      this.selectedId = group._id;
      this.tab = 'elements';
      this.syncCodeFromFace();
      this.renderCanvas();
      this.showToast(result.elements.length + ' element(s) importes');
    };
    reader.readAsText(file);
    e.target.value = '';
  },

  // ── Clipboard proxies (delegate to CanvasInteractions) ──

  copySelectedElement() {
    if (this.selectedElement) CanvasInteractions.copyElement(this.selectedElement);
  },
  pasteClipboard() {
    CanvasInteractions.pasteElement();
  },
  duplicateSelectedElement() {
    if (this.selectedElement) CanvasInteractions.duplicateElement(this.selectedElement);
  }
};
