/**
 * Canvas Interactions — click to select, drag to move, right-click context menu,
 * copy/paste, keyboard shortcuts.
 *
 * Requires: renderer.js (getElementBounds, getPositionArgs), parser.js (serializeNode, parseElement)
 */

const CanvasInteractions = {
  /** Initializes all canvas event listeners. Call from Vue mounted(). */
  init(vueApp) {
    this.app = vueApp;
    this.canvas = document.getElementById('gaugeCanvas');
    this.dragging = false;
    this.dragItems = null;  // Array of { el, origArgs }
    this.dragStartX = 0;
    this.dragStartY = 0;
    this.clipboard = null;
    this.ctxMenu = null;

    // Mouse events
    this.canvas.addEventListener('mousedown', (e) => this.onMouseDown(e));
    this.canvas.addEventListener('mousemove', (e) => this.onMouseMove(e));
    this.canvas.addEventListener('mouseup', (e) => this.onMouseUp(e));
    this.canvas.addEventListener('contextmenu', (e) => this.onContextMenu(e));
    this.canvas.addEventListener('dblclick', (e) => this.onDblClick(e));

    // Touch events (mobile)
    this.canvas.addEventListener('touchstart', (e) => this.onTouchStart(e), { passive: false });
    this.canvas.addEventListener('touchmove', (e) => this.onTouchMove(e), { passive: false });
    this.canvas.addEventListener('touchend', (e) => this.onTouchEnd(e));

    // Keyboard
    document.addEventListener('keydown', (e) => this.onKeyDown(e));

    // Dismiss context menu on click anywhere
    document.addEventListener('mousedown', (e) => {
      if (this.ctxMenu && !this.ctxMenu.contains(e.target)) {
        this.hideContextMenu();
      }
    });
  },

  // ── Touch handlers (mobile) ────────────────────────────────────────
  touchToMouse(touch) {
    return { clientX: touch.clientX, clientY: touch.clientY, button: 0 };
  },

  onTouchStart(e) {
    if (e.touches.length !== 1) return;
    e.preventDefault();
    this.onMouseDown(this.touchToMouse(e.touches[0]));
  },

  onTouchMove(e) {
    if (e.touches.length !== 1) return;
    e.preventDefault();
    this.onMouseMove(this.touchToMouse(e.touches[0]));
  },

  onTouchEnd(e) {
    e.preventDefault();
    this.onMouseUp(this.touchToMouse(e.changedTouches[0]));
  },

  // ── Coordinate helpers ──────────────────────────────────────────────
  canvasPos(e) {
    const rect = this.canvas.getBoundingClientRect();
    const scaleX = 400 / rect.width;
    const scaleY = 400 / rect.height;
    return {
      x: (e.clientX - rect.left) * scaleX,
      y: (e.clientY - rect.top) * scaleY
    };
  },

  pixelToPct(px) {
    return (px / 400 * 100);
  },

  // ── Hit testing ─────────────────────────────────────────────────────
  hitTest(mx, my) {
    const app = this.app;
    const ctx = app.renderCtx;
    const s = 400;
    // Walk elements in reverse (top-most first)
    const allEls = this.collectVisibleElements(app.face.elements, ctx);
    for (let i = allEls.length - 1; i >= 0; i--) {
      const el = allEls[i];
      const bounds = getElementBounds(el, ctx, s);
      if (bounds && mx >= bounds.x && mx <= bounds.x + bounds.w &&
          my >= bounds.y && my <= bounds.y + bounds.h) {
        return el;
      }
    }
    return null;
  },

  /** Collect all visible (non-conditional, or within active conditionals) elements */
  collectVisibleElements(elements, ctx) {
    const result = [];
    for (const el of elements) {
      if (el._hidden) continue;
      if (el.isGroup) {
        result.push(...this.collectVisibleElements(el.children || [], ctx));
      } else if (el.isForLoop) {
        result.push(...this.collectVisibleElements(el.children || [], ctx));
      } else if (el.isConditional) {
        if (evalCondition(el.condition, ctx)) {
          result.push(...this.collectVisibleElements(el.children || [], ctx));
        }
      } else {
        result.push(el);
      }
    }
    return result;
  },

  // ── Multi-drag helpers ──────────────────────────────────────────────

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

  /** Check if hit element belongs to the current selection (multi or group). */
  _isHitPartOfSelection(hit) {
    const app = this.app;
    if (app.selectedIds.length > 1) {
      for (const id of app.selectedIds) {
        if (hit._id === id) return true;
        const node = app.findNodeById(app.face.elements, id);
        if (node && (node.isGroup || node.isConditional || node.isForLoop) && this._isDescendant(node, hit._id)) return true;
      }
      return false;
    }
    const sel = app.selectedElement;
    if (sel && (sel.isGroup || sel.isForLoop) && this._isDescendant(sel, hit._id)) return true;
    return false;
  },

  /** Collect all leaf (drawable) elements from a group/conditional. */
  _collectLeaves(container) {
    const result = [];
    for (const child of (container.children || [])) {
      if (child.isGroup || child.isConditional || child.isForLoop) {
        result.push(...this._collectLeaves(child));
      } else if (getPositionArgs(child.type)) {
        result.push(child);
      }
    }
    return result;
  },

  /** Collect all elements that should move based on current selection. */
  _collectDragTargets() {
    const app = this.app;
    if (app.selectedIds.length > 1) {
      const result = [];
      for (const id of app.selectedIds) {
        const node = app.findNodeById(app.face.elements, id);
        if (!node) continue;
        if (node.isGroup || node.isConditional || node.isForLoop) {
          result.push(...this._collectLeaves(node));
        } else if (getPositionArgs(node.type)) {
          result.push(node);
        }
      }
      return result;
    }
    const sel = app.selectedElement;
    if (!sel) return [];
    if (sel.isGroup || sel.isForLoop) return this._collectLeaves(sel);
    if (sel.isConditional) return [];
    if (getPositionArgs(sel.type)) return [sel];
    return [];
  },

  /** Apply a pixel-to-pct move delta to an element (handles shape/line specially). */
  _applyMoveDelta(el, origArgs, dxPct, dyPct) {
    if (el.type === 'shape') {
      let i = 0;
      while (i < el.args.length) {
        if (origArgs[i] === 'M') { i++; continue; }
        if (i + 1 >= el.args.length) break;
        el.args[i] = ((parseFloat(origArgs[i]) || 0) + dxPct).toFixed(1);
        el.args[i + 1] = ((parseFloat(origArgs[i + 1]) || 0) + dyPct).toFixed(1);
        i += 2;
      }
      return;
    }
    if (el.type === 'line' && el.args.length >= 4) {
      for (let i = 0; i < 4; i++) {
        const d = (i % 2 === 0) ? dxPct : dyPct;
        el.args[i] = ((parseFloat(origArgs[i]) || 0) + d).toFixed(1);
      }
      return;
    }
    const posArgs = getPositionArgs(el.type);
    if (!posArgs) return;
    const [xi, yi] = posArgs;
    el.args[xi] = ((parseFloat(origArgs[xi]) || 0) + dxPct).toFixed(1);
    el.args[yi] = ((parseFloat(origArgs[yi]) || 0) + dyPct).toFixed(1);
  },

  /** Apply a discrete nudge (arrow keys) to an element. */
  _applyNudge(el, dx, dy) {
    if (el.type === 'shape') {
      let i = 0;
      while (i < el.args.length) {
        if (el.args[i] === 'M') { i++; continue; }
        if (i + 1 >= el.args.length) break;
        el.args[i] = String((parseFloat(el.args[i]) || 0) + dx);
        el.args[i + 1] = String((parseFloat(el.args[i + 1]) || 0) + dy);
        i += 2;
      }
      return;
    }
    if (el.type === 'line' && el.args.length >= 4) {
      el.args[0] = String((parseFloat(el.args[0]) || 0) + dx);
      el.args[1] = String((parseFloat(el.args[1]) || 0) + dy);
      el.args[2] = String((parseFloat(el.args[2]) || 0) + dx);
      el.args[3] = String((parseFloat(el.args[3]) || 0) + dy);
      return;
    }
    const posArgs = getPositionArgs(el.type);
    if (!posArgs) return;
    const [xi, yi] = posArgs;
    el.args[xi] = String((parseFloat(el.args[xi]) || 0) + dx);
    el.args[yi] = String((parseFloat(el.args[yi]) || 0) + dy);
  },

  // ── Mouse handlers ──────────────────────────────────────────────────
  onMouseDown(e) {
    if (e.button !== 0) return;
    const pos = this.canvasPos(e);
    const hit = this.hitTest(pos.x, pos.y);

    if (hit) {
      const partOfSelection = this._isHitPartOfSelection(hit);
      if (!partOfSelection) {
        this.app.selectedId = hit._id;
      }
      const targets = this._collectDragTargets();
      if (targets.length > 0) {
        this.app.pushUndo();
        this.dragging = true;
        this.dragStartX = pos.x;
        this.dragStartY = pos.y;
        this.dragItems = targets.map(el => ({ el, origArgs: [...el.args] }));
        this.canvas.style.cursor = 'grabbing';
      }
    } else {
      this.app.selectedIds = [];
    }
    this.app.renderCanvas();
  },

  onMouseMove(e) {
    const pos = this.canvasPos(e);

    if (this.dragging && this.dragItems) {
      const dx = pos.x - this.dragStartX;
      const dy = pos.y - this.dragStartY;
      const dxPct = this.pixelToPct(dx);
      const dyPct = this.pixelToPct(dy);

      for (const item of this.dragItems) {
        this._applyMoveDelta(item.el, item.origArgs, dxPct, dyPct);
      }
      this.app.renderCanvas();
      return;
    }

    // Hover cursor
    const hit = this.hitTest(pos.x, pos.y);
    this.canvas.style.cursor = hit ? 'grab' : 'crosshair';
  },

  onMouseUp(e) {
    if (this.dragging) {
      this.dragging = false;
      this.canvas.style.cursor = 'grab';
      this.dragItems = null;
      this.app.syncCodeFromFace();
    }
  },

  onDblClick(e) {
    // Double-click to edit: switch to elements tab and focus property editor
    const pos = this.canvasPos(e);
    const hit = this.hitTest(pos.x, pos.y);
    if (hit) {
      this.app.selectedId = hit._id;
      this.app.tab = 'elements';
      this.app.renderCanvas();
    }
  },

  // ── Context menu ────────────────────────────────────────────────────
  onContextMenu(e) {
    e.preventDefault();
    const pos = this.canvasPos(e);
    const hit = this.hitTest(pos.x, pos.y);

    // Select the element under cursor
    if (hit) {
      this.app.selectedId = hit._id;
      this.app.renderCanvas();
    }

    this.showContextMenu(e.clientX, e.clientY, hit);
  },

  showContextMenu(x, y, hitEl, multiIds, isLayerList) {
    this.hideContextMenu();

    const menu = document.createElement('div');
    menu.className = 'ctx-menu';
    menu.style.left = x + 'px';
    menu.style.top = y + 'px';

    const items = [];

    if (isLayerList) {
      items.push({ label: 'Ajouter un element...', action: () => { this.app.showPalette = true; } });
      items.push({ sep: true });
      items.push({ label: 'Nouveau groupe', action: () => this.app.addGroup() });
      items.push({ label: 'Bloc conditionnel', action: () => this.app.addConditional() });
      items.push({ label: 'Boucle for', action: () => this.app.addForLoop() });
      if (this.clipboard) {
        items.push({ sep: true });
        items.push({ label: 'Coller', shortcut: 'Ctrl+V', action: () => this.pasteElement() });
      }
    } else if (multiIds && multiIds.length > 1) {
      const n = multiIds.length;
      items.push({ label: 'Copier ' + n + ' elements', shortcut: 'Ctrl+C', action: () => this.copyElements(multiIds) });
      items.push({ label: 'Dupliquer ' + n + ' elements', shortcut: 'Ctrl+D', action: () => { this.copyElements(multiIds); this.pasteElement(); } });
      items.push({ sep: true });
      items.push({ label: 'Grouper ' + n + ' elements', action: () => this.app.groupSelectedElements() });
      items.push({ sep: true });
      items.push({ label: 'Supprimer ' + n + ' elements', shortcut: 'Suppr', danger: true, action: () => this.app.deleteSelectedElements() });
    } else if (hitEl) {
      items.push({ label: 'Selectionner', shortcut: 'Clic', action: () => {
        this.app.selectedId = hitEl._id;
        this.app.tab = 'elements';
        this.app.renderCanvas();
      }});
      items.push({ sep: true });
      items.push({ label: 'Copier', shortcut: 'Ctrl+C', action: () => this.copyElement(hitEl) });
      items.push({ label: 'Dupliquer', shortcut: 'Ctrl+D', action: () => this.duplicateElement(hitEl) });
      items.push({ sep: true });
      items.push({ label: 'Monter', shortcut: 'Ctrl+\u2191', action: () => this.app.moveEl(hitEl._id, -1) });
      items.push({ label: 'Descendre', shortcut: 'Ctrl+\u2193', action: () => this.app.moveEl(hitEl._id, 1) });
      items.push({ sep: true });
      if (!hitEl.isGroup) {
        items.push({ label: 'Grouper', action: () => this.app.groupSelectedElement() });
      }
      if (hitEl.isGroup) {
        items.push({ label: 'Degrouper', action: () => this.app.ungroupSelected() });
      }
      items.push({ sep: true });
      items.push({ label: 'Supprimer', shortcut: 'Suppr', danger: true, action: () => this.app.deleteEl(hitEl._id) });
    } else {
      if (this.clipboard) {
        items.push({ label: 'Coller', shortcut: 'Ctrl+V', action: () => this.pasteElement() });
      } else {
        items.push({ label: 'Coller', shortcut: 'Ctrl+V', disabled: true });
      }
    }

    for (const item of items) {
      if (item.sep) {
        const sep = document.createElement('div');
        sep.className = 'ctx-menu-sep';
        menu.appendChild(sep);
        continue;
      }
      const row = document.createElement('div');
      row.className = 'ctx-menu-item' + (item.danger ? ' danger' : '');
      if (item.disabled) {
        row.style.opacity = '0.4';
        row.style.pointerEvents = 'none';
      }

      const labelSpan = document.createElement('span');
      labelSpan.textContent = item.label;
      row.appendChild(labelSpan);

      if (item.shortcut) {
        const sc = document.createElement('span');
        sc.className = 'ctx-menu-shortcut';
        sc.textContent = item.shortcut;
        row.appendChild(sc);
      }

      if (item.action) {
        row.addEventListener('click', () => {
          this.hideContextMenu();
          item.action();
        });
      }
      menu.appendChild(row);
    }

    document.body.appendChild(menu);
    this.ctxMenu = menu;

    // Adjust if off-screen
    requestAnimationFrame(() => {
      const rect = menu.getBoundingClientRect();
      if (rect.right > window.innerWidth) menu.style.left = (x - rect.width) + 'px';
      if (rect.bottom > window.innerHeight) menu.style.top = (y - rect.height) + 'px';
    });
  },

  hideContextMenu() {
    if (this.ctxMenu) {
      this.ctxMenu.remove();
      this.ctxMenu = null;
    }
  },

  // ── Clipboard operations ────────────────────────────────────────────
  copyElement(el) {
    if (!el || el.isConditional) return;
    // Deep clone, stripping internal properties
    this.clipboard = JSON.parse(JSON.stringify(el, (key, val) => key.startsWith('_') ? undefined : val));
    this.app.showToast(el.isGroup ? 'Groupe copie' : el.isForLoop ? 'Boucle copiee' : 'Element copie');
  },

  copyElements(ids) {
    const nodes = [];
    for (const id of ids) {
      const node = this.app.findNodeById(this.app.face.elements, id);
      if (node && !node.isConditional) {
        nodes.push(JSON.parse(JSON.stringify(node, (key, val) => key.startsWith('_') ? undefined : val)));
      }
    }
    if (nodes.length === 0) return;
    this.clipboard = nodes;
    this.app.showToast(nodes.length + ' element(s) copie(s)');
  },

  pasteElement() {
    if (!this.clipboard) return;
    this.app.pushUndo();
    const items = Array.isArray(this.clipboard) ? this.clipboard : [this.clipboard];
    const newIds = [];
    for (const item of items) {
      const el = JSON.parse(JSON.stringify(item));
      el._id = ++_nextId;
      el._depth = 0;
      if (el.isGroup || el.isConditional || el.isForLoop) {
        assignIds(el.children || [], 1);
      }
      if (!el.isGroup && !el.isConditional && !el.isForLoop) {
        const posArgs = getPositionArgs(el.type);
        if (posArgs) {
          const [xi, yi] = posArgs;
          el.args[xi] = String((parseFloat(el.args[xi]) || 50) + 3);
          el.args[yi] = String((parseFloat(el.args[yi]) || 50) + 3);
        }
      }
      this.app.face.elements.push(el);
      newIds.push(el._id);
    }
    if (newIds.length === 1) {
      this.app.selectedId = newIds[0];
    } else {
      this.app.selectedIds = newIds;
    }
    this.app.syncCodeFromFace();
    this.app.renderCanvas();
    this.app.showToast(items.length > 1 ? items.length + ' elements colles' : 'Element colle');
  },

  duplicateElement(el) {
    if (!el || el.isConditional) return;
    this.app.pushUndo();
    const dup = JSON.parse(JSON.stringify(el, (key, val) => key.startsWith('_') ? undefined : val));
    dup._id = ++_nextId;
    dup._depth = el._depth || 0;
    if (dup.isGroup || dup.isForLoop) {
      assignIds(dup.children || [], (el._depth || 0) + 1);
    }
    if (!dup.isGroup && !dup.isForLoop) {
      const posArgs = getPositionArgs(dup.type);
      if (posArgs) {
        const [xi, yi] = posArgs;
        dup.args[xi] = String((parseFloat(dup.args[xi]) || 50) + 3);
        dup.args[yi] = String((parseFloat(dup.args[yi]) || 50) + 3);
      }
    }
    // Insert after the original
    const insertAfter = (arr) => {
      const idx = arr.findIndex(e => e._id === el._id);
      if (idx >= 0) { arr.splice(idx + 1, 0, dup); return true; }
      for (const e of arr) {
        if ((e.isConditional || e.isGroup || e.isForLoop) && e.children) {
          if (insertAfter(e.children)) return true;
        }
      }
      return false;
    };
    if (!insertAfter(this.app.face.elements)) {
      this.app.face.elements.push(dup);
    }
    this.app.selectedId = dup._id;
    this.app.syncCodeFromFace();
    this.app.renderCanvas();
    this.app.showToast(el.isGroup ? 'Groupe duplique' : el.isForLoop ? 'Boucle dupliquee' : 'Element duplique');
  },

  // ── Keyboard shortcuts ──────────────────────────────────────────────
  onKeyDown(e) {
    // Don't capture when typing in inputs
    const tag = e.target.tagName;
    if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;

    const el = this.app.selectedElement;
    const ctrl = e.ctrlKey || e.metaKey;

    // Ctrl+S — Save
    if (ctrl && e.key === 's') {
      e.preventDefault();
      this.app.saveFaceToLibrary();
      return;
    }

    // Delete / Backspace
    if (e.key === 'Delete' || e.key === 'Backspace') {
      if (this.app.selectedIds.length > 1) {
        e.preventDefault();
        this.app.deleteSelectedElements();
        return;
      }
      if (el) {
        e.preventDefault();
        this.app.deleteEl(el._id);
        return;
      }
    }

    // Ctrl+C — Copy
    if (ctrl && e.key === 'c') {
      if (this.app.selectedIds.length > 1) {
        e.preventDefault();
        this.copyElements(this.app.selectedIds);
        return;
      }
      if (el) {
        e.preventDefault();
        this.copyElement(el);
        return;
      }
    }

    // Ctrl+V — Paste
    if (ctrl && e.key === 'v') {
      e.preventDefault();
      this.pasteElement();
      return;
    }

    // Ctrl+D — Duplicate
    if (ctrl && e.key === 'd') {
      if (this.app.selectedIds.length > 1) {
        e.preventDefault();
        this.copyElements(this.app.selectedIds);
        this.pasteElement();
        return;
      }
      if (el) {
        e.preventDefault();
        this.duplicateElement(el);
        return;
      }
    }

    // Ctrl+Shift+Z — Redo
    if (ctrl && e.shiftKey && e.key === 'z') {
      e.preventDefault();
      this.app.redo();
      return;
    }

    // Ctrl+Z — Undo
    if (ctrl && e.key === 'z') {
      e.preventDefault();
      this.app.undo();
      return;
    }

    // Ctrl+Up/Down — reorder
    if (ctrl && e.key === 'ArrowUp' && el) {
      e.preventDefault();
      this.app.moveEl(el._id, -1);
      return;
    }
    if (ctrl && e.key === 'ArrowDown' && el) {
      e.preventDefault();
      this.app.moveEl(el._id, 1);
      return;
    }

    // Arrow keys — nudge selected element(s) / group
    if (!ctrl && (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {
      const targets = this._collectDragTargets();
      if (targets.length === 0) return;

      const nudge = e.shiftKey ? 5 : 1;
      let dx = 0, dy = 0;
      switch (e.key) {
        case 'ArrowUp': dy = -nudge; break;
        case 'ArrowDown': dy = nudge; break;
        case 'ArrowLeft': dx = -nudge; break;
        case 'ArrowRight': dx = nudge; break;
      }

      e.preventDefault();
      for (const t of targets) {
        this._applyNudge(t, dx, dy);
      }
      this.app.syncCodeFromFace();
      this.app.renderCanvas();
    }

    // Escape — close menus / deselect
    if (e.key === 'Escape') {
      if (this.app.activeMenu) {
        this.app.activeMenu = null;
        return;
      }
      this.app.selectedIds = [];
      this.hideContextMenu();
      this.app.renderCanvas();
    }
  }
};
