// test/pen_select_move_test.dart
//
// Pins the SELECT tool's select + move + delete on the PenCanvas editors (note):
//   * tapping a committed stroke selects it (onSelectStroke fires with its index);
//   * dragging the selection translates the stroke's normalized points (the
//     committed stroke list is replaced with shifted points);
//   * the move is one undoable step (undo restores the original points).
//
// Drives the real PenNoteScreen with a pre-loaded stroke and a stylus gesture.

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'package:badnote/editor/canvas/pen_canvas.dart';
import 'package:badnote/editor/canvas/pen_note_screen.dart';
import 'package:badnote/editor/canvas/pen_stroke.dart';
import 'package:badnote/editor/engine/shape_geometry.dart';
import 'package:badnote/models/ink_point.dart';
import 'package:badnote/models/ink_stroke.dart';
import 'package:badnote/models/note.dart';
import 'package:badnote/models/pen_tool.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  // A note with a single stroke crossing the page center so a center tap hits it.
  Note noteWithStroke() => Note(
        id: 'n1',
        title: 'Test',
        strokes: [
          InkStroke(
            id: 's1',
            // Through the page center (kNoteLogicalPage = 1000 x 1414) so a
            // center tap on the canvas hits the stroke.
            points: const [
              InkPoint(x: 300, y: 707, timestamp: 0),
              InkPoint(x: 500, y: 707, timestamp: 0),
              InkPoint(x: 700, y: 707, timestamp: 0),
            ],
            tool: PenTool.pen,
            createdAt: DateTime.fromMillisecondsSinceEpoch(0),
          ),
        ],
        createdAt: DateTime.fromMillisecondsSinceEpoch(0),
        updatedAt: DateTime.fromMillisecondsSinceEpoch(0),
      );

  PenCanvas canvas(WidgetTester tester) =>
      tester.widget<PenCanvas>(find.byType(PenCanvas));

  List<PenStroke> strokes(WidgetTester tester) => canvas(tester).strokes;

  /// Activate the SELECT tool via its toolbar button (the "Select" tooltip).
  Future<void> activateSelect(WidgetTester tester) async {
    await tester.tap(find.byTooltip('Select'));
    await tester.pumpAndSettle();
  }

  testWidgets('tapping a stroke selects it, dragging moves it, undo restores',
      (tester) async {
    SharedPreferences.setMockInitialValues({});
    await tester.pumpWidget(ProviderScope(
      child: MaterialApp(home: PenNoteScreen(note: noteWithStroke())),
    ));
    await tester.pump(); // PenConfig load

    expect(strokes(tester), hasLength(1));
    final before = strokes(tester).single;
    final beforeBounds = penStrokeBounds(before)!;

    await activateSelect(tester);
    expect(canvas(tester).tool, CanvasTool.select);

    // Tap the stroke (page center) to select, then drag it down-right.
    final center = tester.getCenter(find.byType(PenCanvas));
    final g = await tester.startGesture(center, kind: PointerDeviceKind.stylus);
    await tester.pump();
    // selection should now be set.
    expect(canvas(tester).selectedStrokeIndex, 0);

    await g.moveBy(const Offset(40, 30));
    await g.moveBy(const Offset(20, 10));
    await g.up();
    await tester.pump();

    final after = strokes(tester).single;
    final afterBounds = penStrokeBounds(after)!;
    // The stroke translated: its bounds shifted right and down.
    expect(afterBounds.left, greaterThan(beforeBounds.left),
        reason: 'stroke moved right');
    expect(afterBounds.top, greaterThan(beforeBounds.top),
        reason: 'stroke moved down');
    // Same number of points (a translate, not a redraw).
    expect(after.points.length, before.points.length);

    // Undo restores the original position (one undoable step).
    await tester.tap(find.byTooltip('Undo'));
    await tester.pump();
    final undoneBounds = penStrokeBounds(strokes(tester).single)!;
    expect(undoneBounds.left, closeTo(beforeBounds.left, 1e-6));
    expect(undoneBounds.top, closeTo(beforeBounds.top, 1e-6));
  });

  testWidgets('delete-selection removes the selected stroke (undoable)',
      (tester) async {
    SharedPreferences.setMockInitialValues({});
    await tester.pumpWidget(ProviderScope(
      child: MaterialApp(home: PenNoteScreen(note: noteWithStroke())),
    ));
    await tester.pump();

    await activateSelect(tester);
    final center = tester.getCenter(find.byType(PenCanvas));
    final g = await tester.startGesture(center, kind: PointerDeviceKind.stylus);
    await g.up();
    await tester.pump();
    expect(canvas(tester).selectedStrokeIndex, 0);

    // The delete button appears only with a live selection.
    await tester.tap(find.byTooltip('Delete selection'));
    await tester.pump();
    expect(strokes(tester), isEmpty);

    await tester.tap(find.byTooltip('Undo'));
    await tester.pump();
    expect(strokes(tester), hasLength(1));
  });
}
