/**
 * Undo/redo, code sync, import/export, QR, localStorage persistence.
 * Merged into Vue app via: methods: { ...SyncMethods, ... }
 */

const SyncMethods = {
  // ── Undo / Redo ──

  pushUndo() {
    const snapshot = serializeFace(this.face);
    if (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1] === snapshot) return;
    this._undoStack.push(snapshot);
    if (this._undoStack.length > 50) this._undoStack.shift();
    this._redoStack = [];
  },

  pushUndoDebounced() {
    if (!this._undoPending) {
      this._undoPending = serializeFace(this.face);
    }
    clearTimeout(this._undoTimer);
    this._undoTimer = setTimeout(() => {
      if (this._undoPending) {
        const snap = this._undoPending;
        if (this._undoStack.length === 0 || this._undoStack[this._undoStack.length - 1] !== snap) {
          this._undoStack.push(snap);
          if (this._undoStack.length > 50) this._undoStack.shift();
        }
        this._undoPending = null;
        this._redoStack = [];
      }
    }, 500);
  },

  undo() {
    if (this._undoStack.length === 0) return;
    this._redoStack.push(serializeFace(this.face));
    const prev = this._undoStack.pop();
    this._syncing = true;
    this.codeText = prev;
    this.applyCode(prev);
    this.$nextTick(() => { this._syncing = false; });
    this.showToast('Annule');
  },

  redo() {
    if (this._redoStack.length === 0) return;
    this._undoStack.push(serializeFace(this.face));
    const next = this._redoStack.pop();
    this._syncing = true;
    this.codeText = next;
    this.applyCode(next);
    this.$nextTick(() => { this._syncing = false; });
    this.showToast('Retabli');
  },

  applyCode(code) {
    try {
      const parsed = parseDSL(code);
      this.face.name = parsed.name;
      this.face.author = parsed.author;
      this.face.bg = parsed.bg;
      this.face.vars = parsed.vars || {};
      assignIds(parsed.elements, 0);
      this.face.elements = parsed.elements;
      this.renderCanvas();
      this.autoSaveSession();
    } catch(e) { /* ignore parse errors */ }
  },

  // ── Bidirectional sync ──

  syncCodeFromFace() {
    if (this._syncing) return;
    this._syncing = true;
    this.codeText = serializeFace(this.face);
    // Keep active cell code in sync
    this.cells[this.activeCell].code = this.codeText;
    this._cellFaces[this.activeCell] = null; // invalidate cache
    this.$nextTick(() => { this._syncing = false; });
  },

  onCodeInput() {
    if (this._syncing) return;
    clearTimeout(this._codeInputTimer);
    this._codeInputTimer = setTimeout(() => {
      this.pushUndo();
      this.syncFaceFromCode();
    }, 300);
  },

  syncFaceFromCode() {
    if (this._syncing) return;
    this._syncing = true;
    try {
      const parsed = parseDSL(this.codeText);
      this.face.name = parsed.name;
      this.face.author = parsed.author;
      this.face.bg = parsed.bg;
      this.face.vars = parsed.vars || {};
      assignIds(parsed.elements, 0);
      this.face.elements = parsed.elements;
      this.renderCanvas();
      this.autoSaveSession();
    } catch(e) { /* ignore parse errors during typing */ }
    this.$nextTick(() => { this._syncing = false; });
  },

  formatCode() {
    this.syncFaceFromCode();
    this.codeText = serializeFace(this.face);
  },

  // ── Presets ──

  loadPreset(preset) {
    this.pushUndo();
    this.codeText = preset.code;
    this.syncFaceFromCode();

    // Auto-add data bindings for multi-value presets
    if (preset.tags && preset.tags.includes('multi-value')) {
      // Detect how many indexed bindings the code references ($value-1, $value-2, etc.)
      let maxIdx = 0;
      for (let i = 1; i <= 9; i++) {
        if (preset.code.includes('$value-' + i) || preset.code.includes('$progress-' + i)) {
          maxIdx = i;
        }
      }
      if (maxIdx > 0) {
        // Pick binding types that are not the current previewType
        const currentType = this.previewType;
        const available = PREVIEW_TYPES.filter(t => t.key !== currentType);
        this.dataBindings = [];
        for (let i = 0; i < maxIdx && i < available.length; i++) {
          const t = available[i];
          this.dataBindings.push({ type: t.key, value: t.min + (t.max - t.min) * 0.5 });
        }
      }
    }

    this.renderCanvas();
    this.showToast('Preset "' + preset.name + '" charge');
  },

  // ── Import / Export ──

  importFile() {
    this.$refs.fileInput.click();
  },

  onFileImport(e) {
    const file = e.target.files[0];
    if (!file) return;
    this.pushUndo();
    const reader = new FileReader();
    reader.onload = (ev) => {
      this.codeText = ev.target.result;
      this.syncFaceFromCode();
      this.showToast('Fichier importe');
    };
    reader.readAsText(file);
    e.target.value = '';
  },

  exportFile() {
    const blob = new Blob([serializeFace(this.face)], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = (this.face.name || 'face').replace(/\s+/g, '_') + '.face';
    a.click();
    URL.revokeObjectURL(url);
    this.showToast('Fichier exporte');
  },

  async copyCode() {
    try {
      await navigator.clipboard.writeText(serializeFace(this.face));
      this.showToast('Code copie dans le presse-papiers');
    } catch(e) {
      this.showToast('Erreur de copie');
    }
  },

  // ── QR ──

  async showQrExport() {
    this.qrModal = true;
    await this.$nextTick();
    setTimeout(() => {
      const canvas = document.getElementById('qrCanvas');
      if (!canvas || typeof qrcode !== 'function') return;
      try {
        const text = serializeFace(this.face);
        const qr = qrcode(0, 'L');
        qr.addData(text);
        qr.make();
        const count = qr.getModuleCount();
        const margin = 2;
        const maxPx = this.qrFullscreen ? Math.min(window.innerWidth, window.innerHeight) * 0.85 : 280;
        const cell = Math.max(2, Math.floor(maxPx / (count + margin * 2)));
        const size = cell * (count + margin * 2);
        canvas.width = size;
        canvas.height = size;
        const ctx = canvas.getContext('2d');
        ctx.fillStyle = '#1f2937';
        ctx.fillRect(0, 0, size, size);
        ctx.fillStyle = '#FFFFFF';
        const off = cell * margin;
        for (let r = 0; r < count; r++) {
          for (let c = 0; c < count; c++) {
            if (qr.isDark(r, c)) {
              ctx.fillRect(off + c * cell, off + r * cell, cell, cell);
            }
          }
        }
      } catch (e) {
        this.showToast('Cadran trop volumineux pour un QR code');
      }
    }, 100);
  },

  startQrScan() {
    this.qrScanModal = true;
    this.$nextTick(() => {
      if (typeof Html5Qrcode !== 'undefined') {
        this._scanner = new Html5Qrcode('qr-reader');
        this._scanner.start(
          { facingMode: 'environment' },
          { fps: 10, qrbox: 250 },
          (text) => {
            this.pushUndo();
            this.codeText = text;
            this.syncFaceFromCode();
            this.stopQrScan();
            this.showToast('QR code importe');
          },
          () => {}
        ).catch(() => {});
      }
    });
  },

  stopQrScan() {
    if (this._scanner) {
      this._scanner.stop().catch(() => {});
      this._scanner = null;
    }
    this.qrScanModal = false;
  },

  onQrFileScan(e) {
    const file = e.target.files[0];
    if (!file) return;
    if (typeof Html5Qrcode === 'undefined') return;
    const scanner = new Html5Qrcode('qr-reader');
    scanner.scanFile(file, true).then((text) => {
      this.pushUndo();
      this.codeText = text;
      this.syncFaceFromCode();
      this.stopQrScan();
      this.showToast('QR code importe');
    }).catch(() => {
      this.showToast('QR code non detecte dans l\'image');
    });
    e.target.value = '';
  },

  // ── Preset thumbnails ──

  renderPresetThumbnails() {
    const previewCtx = {
      progress: 0.65, percent: 65, rawValue: 95, formattedValue: '95',
      unit: '\u00b0C', label: 'Huile', minValue: 0, maxValue: 150,
      accentColor: '#4FC3F7', zone: 'normal',
      warnLowProgress: null, warnHighProgress: 0.8,
      dangerLowProgress: null, dangerHighProgress: 0.9
    };
    for (const p of PRESETS) {
      const id = 'preset-' + p.name.replace(/\s+/g, '_');
      const canvas = document.getElementById(id);
      if (!canvas) continue;
      const c = canvas.getContext('2d');
      try {
        const face = parseDSL(p.code);
        renderFace(c, face, previewCtx, 80, null);
      } catch(_) {
        c.fillStyle = '#111';
        c.fillRect(0, 0, 80, 80);
      }
    }
  },

  // ── localStorage face library ──

  loadLibrary() {
    try {
      const raw = localStorage.getItem('obd_face_library');
      this.savedFaces = raw ? JSON.parse(raw) : [];
    } catch(_) { this.savedFaces = []; }
  },

  saveLibrary() {
    try { localStorage.setItem('obd_face_library', JSON.stringify(this.savedFaces)); } catch(_) {}
  },

  saveFaceToLibrary() {
    const code = serializeFace(this.face);
    const name = this.face.name || 'Sans nom';
    const idx = this.savedFaces.findIndex(f => f.name === name);
    if (idx >= 0) {
      this.savedFaces[idx].code = code;
      this.savedFaces[idx].date = Date.now();
    } else {
      this.savedFaces.push({ name, code, date: Date.now() });
    }
    this.saveLibrary();
    this.showToast('Cadran "' + name + '" sauvegarde');
  },

  loadFaceFromLibrary(idx) {
    const entry = this.savedFaces[idx];
    if (!entry) return;
    this.pushUndo();
    this.codeText = entry.code;
    this.syncFaceFromCode();
    this.showToast('Cadran "' + entry.name + '" charge');
  },

  deleteFaceFromLibrary(idx) {
    const entry = this.savedFaces[idx];
    if (!entry) return;
    this.savedFaces.splice(idx, 1);
    this.saveLibrary();
    this.showToast('Cadran "' + entry.name + '" supprime');
  },

  autoSaveSession() {
    try {
      // Keep active cell in sync
      if (this.activeCell >= 0) {
        this.cells[this.activeCell].code = serializeFace(this.face);
        this.cells[this.activeCell].previewType = this.previewType;
      }
      // Save full grid state
      const state = {
        layout: this.previewLayout,
        activeCell: this.activeCell,
        cells: this.cells.map(c => ({ code: c.code, previewType: c.previewType }))
      };
      localStorage.setItem('obd_face_grid', JSON.stringify(state));
      // Also save legacy key for backwards compat
      localStorage.setItem('obd_face_session', serializeFace(this.face));
    } catch(_) {}
  },

  loadSession() {
    try {
      // Try loading grid state first
      const raw = localStorage.getItem('obd_face_grid');
      if (raw) {
        const state = JSON.parse(raw);
        if (state.layout) this.previewLayout = state.layout;
        if (Array.isArray(state.cells)) {
          for (let i = 0; i < Math.min(state.cells.length, 4); i++) {
            this.cells[i].code = state.cells[i].code || '';
            this.cells[i].previewType = state.cells[i].previewType || PREVIEW_TYPES[i % PREVIEW_TYPES.length].key;
          }
        }
        this._cellFaces = [null, null, null, null];
        // Load active cell
        const active = state.activeCell != null && state.activeCell >= 0 ? state.activeCell : 0;
        this.activeCell = active;
        const cell = this.cells[active];
        if (cell && cell.code) {
          this.codeText = cell.code;
          this.syncFaceFromCode();
          this.previewType = cell.previewType;
          return true;
        }
      }
      // Fallback: legacy single-face session
      const code = localStorage.getItem('obd_face_session');
      if (code) {
        this.codeText = code;
        this.syncFaceFromCode();
        this.cells[0].code = code;
        return true;
      }
    } catch(_) {}
    return false;
  },

  // ── Preview background persistence ──

  loadPreviewBgImage() {
    this.$refs.bgInput.click();
  },

  onBgImport(e) {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      this.previewBgImage = ev.target.result;
      const img = new Image();
      img.onload = () => { this._previewBgImg = img; this.renderCanvas(); this.savePreviewBg(); };
      img.src = ev.target.result;
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  },

  clearPreviewBg() {
    this.previewBgImage = null;
    this._previewBgImg = null;
    this.previewBg = '#000000';
    this.savePreviewBg();
    this.renderCanvas();
  },

  savePreviewBg() {
    try {
      localStorage.setItem('obd_preview_bg_color', this.previewBg);
      if (this.previewBgImage) {
        localStorage.setItem('obd_preview_bg_image', this.previewBgImage);
      } else {
        localStorage.removeItem('obd_preview_bg_image');
      }
    } catch(_) {}
  },

  loadPreviewBg() {
    try {
      const color = localStorage.getItem('obd_preview_bg_color');
      if (color) this.previewBg = color;
      const imgData = localStorage.getItem('obd_preview_bg_image');
      if (imgData) {
        this.previewBgImage = imgData;
        const img = new Image();
        img.onload = () => { this._previewBgImg = img; this.renderCanvas(); };
        img.src = imgData;
      }
    } catch(_) {}
  }
};
