import 'package:flutter_test/flutter_test.dart';

import 'package:badnote/models/ink_point.dart';
import 'package:badnote/models/ink_stroke.dart';
import 'package:badnote/models/pen_tool.dart';
import 'package:badnote/services/undo_manager.dart';

InkStroke _stroke(String id) => InkStroke(
  id: id,
  points: const [
    InkPoint(x: 0, y: 0, pressure: 0.5, tilt: 0, timestamp: 0),
    InkPoint(x: 1, y: 1, pressure: 0.5, tilt: 0, timestamp: 1),
  ],
  tool: PenTool.pen,
  createdAt: DateTime(2024, 1, 1),
);

void main() {
  group('UndoManager', () {
    test('starts empty with no undo/redo available', () {
      final m = UndoManager();
      expect(m.currentStrokes, isEmpty);
      expect(m.canUndo, isFalse);
      expect(m.canRedo, isFalse);
    });

    test('addStroke then undo/redo round-trips the stroke', () {
      final m = UndoManager();
      final s = _stroke('a');

      m.addStroke(s);
      expect(m.currentStrokes.map((e) => e.id), ['a']);
      expect(m.canUndo, isTrue);

      m.undo();
      expect(m.currentStrokes, isEmpty);
      expect(m.canRedo, isTrue);

      m.redo();
      expect(m.currentStrokes.map((e) => e.id), ['a']);
    });

    test('a new stroke clears the redo stack', () {
      final m = UndoManager();
      m.addStroke(_stroke('a'));
      m.undo();
      expect(m.canRedo, isTrue);

      m.addStroke(_stroke('b'));
      expect(m.canRedo, isFalse);
      expect(m.currentStrokes.map((e) => e.id), ['b']);
    });

    test('removeStroke with replacements (partial erase) is reversible', () {
      final m = UndoManager();
      m.addStroke(_stroke('whole'));

      m.removeStroke(
        _stroke('whole'),
        replacements: [_stroke('part1'), _stroke('part2')],
      );
      expect(m.currentStrokes.map((e) => e.id), ['part1', 'part2']);

      // Undo restores the original and drops the replacements.
      m.undo();
      expect(m.currentStrokes.map((e) => e.id), ['whole']);

      // Redo re-applies the erase.
      m.redo();
      expect(m.currentStrokes.map((e) => e.id), ['part1', 'part2']);
    });

    test('currentStrokes is an unmodifiable view', () {
      final m = UndoManager();
      m.addStroke(_stroke('a'));
      expect(() => m.currentStrokes.add(_stroke('b')), throwsUnsupportedError);
    });
  });
}
