import 'package:block_seasons/core/rng.dart';
import 'package:block_seasons/game/engine/piece_generator.dart';
import 'package:block_seasons/game/models/piece_library.dart';
import 'package:block_seasons/game/models/stage.dart';
import 'package:block_seasons/l10n/gen/app_localizations.dart';
import 'package:block_seasons/state/providers.dart';
import 'package:block_seasons/ui/screens/game_screen.dart';
import 'package:block_seasons/ui/widgets/board_widget.dart';
import 'package:block_seasons/ui/widgets/piece_painter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

final _stage = StageConfig.fromJson({
  'id': 'drag_stage',
  'seed': 11,
  'moveLimit': 20,
  'preset': const <Map<String, dynamic>>[],
  'objectives': [
    {'type': 'reachScore', 'target': 999999},
  ],
  'stars': {
    'two': {'movesLeft': 5},
    'three': {'movesLeft': 10},
  },
  'generatorProfile': 'mid',
});

PieceGenerator _smallPool() => PieceGenerator(
      SeededRng(3),
      pool: [
        PieceLibrary.byId('mono'),
        PieceLibrary.byId('domino_h'),
        PieceLibrary.byId('domino_v'),
      ],
    );

void main() {
  testWidgets('dragging a tray piece onto the board places it',
      (tester) async {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    container
        .read(gameSessionProvider.notifier)
        .startStage(_stage, generator: _smallPool());

    await tester.pumpWidget(
      UncontrolledProviderScope(
        container: container,
        child: const MaterialApp(
          localizationsDelegates: AppLocalizations.localizationsDelegates,
          supportedLocales: AppLocalizations.supportedLocales,
          home: GameScreen(),
        ),
      ),
    );
    await tester.pumpAndSettle();

    final view0 = container.read(gameSessionProvider)!;
    final monoIndex = view0.tray.indexWhere((p) => p.id == 'mono');
    expect(monoIndex, greaterThanOrEqualTo(0));

    final boardRect = tester.getRect(find.byType(BoardWidget));
    final cell = boardRect.width / 8;

    // The dragged piece floats 70px above the finger, so aim the finger
    // below the intended landing cell (0, 0).
    final targetCenter = boardRect.topLeft + Offset(cell * 0.5, cell * 0.5);
    final fingerEnd = targetCenter + Offset(0, 70 + cell / 2);

    final start = tester.getCenter(find.byType(PieceWidget).at(monoIndex));
    final gesture = await tester.startGesture(start);
    await gesture.moveBy(const Offset(0, -30));
    await tester.pump();
    await gesture.moveTo(fingerEnd);
    await tester.pump();
    await gesture.up();
    await tester.pumpAndSettle();

    final view = container.read(gameSessionProvider)!;
    expect(view.grid.isOccupied(0, 0), isTrue,
        reason: 'mono should land on (0,0)');
    expect(view.tray, hasLength(2));
    expect(view.score, 1);
  });

  testWidgets('result overlay appears when out of moves', (tester) async {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    final oneMove = StageConfig.fromJson({
      ..._stage.toJson(),
      'moveLimit': 1,
    });
    container
        .read(gameSessionProvider.notifier)
        .startStage(oneMove, generator: _smallPool());

    await tester.pumpWidget(
      UncontrolledProviderScope(
        container: container,
        child: const MaterialApp(
          localizationsDelegates: AppLocalizations.localizationsDelegates,
          supportedLocales: AppLocalizations.supportedLocales,
          home: GameScreen(),
        ),
      ),
    );
    await tester.pumpAndSettle();

    container.read(gameSessionProvider.notifier).tryPlace(0, 0, 0);
    await tester.pumpAndSettle();

    expect(find.text('Out of moves'), findsOneWidget);
    expect(find.text('+5 moves (ad)'), findsOneWidget);
  });
}
