// @vitest-environment happy-dom
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { UndoManager } from '@presentation/services/UndoManager';

describe('UndoManager', () => {
  beforeEach(() => {
    UndoManager.clear();
    // Reset le callback
    UndoManager.onUndo(() => {});
  });

  afterEach(() => {
    // Toujours nettoyer electronAPI sans supprimer window
    delete (window as any).electronAPI;
  });

  describe('snapshot / canUndo / label', () => {
    it('canUndo retourne false sans snapshot', () => {
      expect(UndoManager.canUndo()).toBe(false);
    });

    it('canUndo retourne true après un snapshot', () => {
      UndoManager.snapshot('{"data":1}', 'test');
      expect(UndoManager.canUndo()).toBe(true);
    });

    it('label retourne le label du snapshot', () => {
      UndoManager.snapshot('{}', 'Suppression enclos');
      expect(UndoManager.label()).toBe('Suppression enclos');
    });

    it('label retourne une chaîne vide sans snapshot', () => {
      expect(UndoManager.label()).toBe('');
    });

    it('un nouveau snapshot écrase le précédent (1 seul niveau)', () => {
      UndoManager.snapshot('{"v":1}', 'premier');
      UndoManager.snapshot('{"v":2}', 'second');
      expect(UndoManager.label()).toBe('second');
    });
  });

  describe('clear', () => {
    it('clear supprime le snapshot', () => {
      UndoManager.snapshot('{}', 'test');
      expect(UndoManager.canUndo()).toBe(true);
      UndoManager.clear();
      expect(UndoManager.canUndo()).toBe(false);
    });

    it('label retourne vide après clear', () => {
      UndoManager.snapshot('{}', 'test');
      UndoManager.clear();
      expect(UndoManager.label()).toBe('');
    });
  });

  describe('expiration', () => {
    it('canUndo retourne false après expiration (10s)', () => {
      UndoManager.snapshot('{}', 'test');
      vi.spyOn(Date, 'now').mockReturnValue(Date.now() + 11_000);
      expect(UndoManager.canUndo()).toBe(false);
      vi.restoreAllMocks();
    });

    it('canUndo retourne true avant expiration', () => {
      UndoManager.snapshot('{}', 'test');
      vi.spyOn(Date, 'now').mockReturnValue(Date.now() + 5_000);
      expect(UndoManager.canUndo()).toBe(true);
      vi.restoreAllMocks();
    });
  });

  describe('undo', () => {
    it('undo retourne false sans snapshot', () => {
      expect(UndoManager.undo()).toBe(false);
    });

    it('undo retourne false sans electronAPI', () => {
      UndoManager.snapshot('{}', 'test');
      expect(UndoManager.undo()).toBe(false);
    });

    it('undo appelle saveData et le callback', () => {
      const saveData = vi.fn();
      const undoCallback = vi.fn();
      (window as any).electronAPI = { saveData };

      UndoManager.onUndo(undoCallback);
      UndoManager.snapshot('{"restored":true}', 'test');
      const result = UndoManager.undo();

      expect(result).toBe(true);
      expect(saveData).toHaveBeenCalledWith('{"restored":true}');
      expect(undoCallback).toHaveBeenCalled();
      expect(UndoManager.canUndo()).toBe(false);
    });

    it('undo ne fonctionne pas deux fois de suite', () => {
      const saveData = vi.fn();
      (window as any).electronAPI = { saveData };
      UndoManager.onUndo(() => {});

      UndoManager.snapshot('{}', 'test');
      UndoManager.undo();
      expect(UndoManager.undo()).toBe(false);
    });
  });

  describe('snapshotCurrent', () => {
    it('retourne false sans electronAPI', async () => {
      const result = await UndoManager.snapshotCurrent('test');
      expect(result).toBe(false);
    });

    it('retourne false si loadData retourne null', async () => {
      (window as any).electronAPI = { loadData: vi.fn().mockResolvedValue(null) };
      const result = await UndoManager.snapshotCurrent('test');
      expect(result).toBe(false);
    });

    it('prend un snapshot si loadData retourne des données', async () => {
      (window as any).electronAPI = { loadData: vi.fn().mockResolvedValue('{"ok":true}') };
      const result = await UndoManager.snapshotCurrent('Suppression DD');
      expect(result).toBe(true);
      expect(UndoManager.canUndo()).toBe(true);
      expect(UndoManager.label()).toBe('Suppression DD');
    });
  });
});
