import 'package:block_seasons/core/rng.dart';
import 'package:block_seasons/game/engine/game_engine.dart';
import 'package:block_seasons/game/engine/piece_generator.dart';
import 'package:block_seasons/game/models/cell.dart';
import 'package:block_seasons/game/models/grid.dart';
import 'package:block_seasons/game/models/piece_library.dart';
import 'package:block_seasons/game/models/stage.dart';
import 'package:block_seasons/state/providers.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

StageConfig _stage({List<Map<String, dynamic>>? objectives}) =>
    StageConfig.fromJson({
      'id': 'ui_stage',
      'seed': 99,
      'moveLimit': 20,
      'preset': [
        for (var x = 1; x < GridState.size; x++)
          {'x': x, 'y': 3, 't': 'filled', 'c': 0},
      ],
      'objectives': objectives ??
          [
            {'type': 'reachScore', 'target': 999999},
          ],
      'stars': {
        'two': {'movesLeft': 5},
        'three': {'movesLeft': 10},
      },
      'generatorProfile': 'mid',
    });

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

void main() {
  test('starts idle with no session', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    expect(container.read(gameSessionProvider), isNull);
  });

  test('startStage exposes a fresh view state', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    final notifier = container.read(gameSessionProvider.notifier);
    notifier.startStage(_stage(), generator: _smallPool());

    final view = container.read(gameSessionProvider)!;
    expect(view.phase, GamePhase.playing);
    expect(view.tray, hasLength(3));
    expect(view.score, 0);
    expect(view.movesLeft, 20);
    expect(view.grid.cellAt(1, 3).type, CellType.filled);
    expect(view.fxTick, 0);
  });

  test('tryPlace mutates the view and bumps fxTick', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    final notifier = container.read(gameSessionProvider.notifier);
    notifier.startStage(_stage(), generator: _smallPool());

    final placed = notifier.tryPlace(0, 0, 0);
    expect(placed, isTrue);

    final view = container.read(gameSessionProvider)!;
    expect(view.tray, hasLength(2));
    expect(view.score, greaterThan(0));
    expect(view.fxTick, 1);
    expect(view.lastPlacement, isNotNull);
  });

  test('illegal placement leaves state untouched', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    final notifier = container.read(gameSessionProvider.notifier);
    notifier.startStage(_stage(), generator: _smallPool());

    final placed = notifier.tryPlace(0, 1, 3); // occupied preset cell
    expect(placed, isFalse);
    final view = container.read(gameSessionProvider)!;
    expect(view.tray, hasLength(3));
    expect(view.fxTick, 0);
  });

  test('winning surfaces stars and cleared lines fx', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    final notifier = container.read(gameSessionProvider.notifier);
    notifier.startStage(
      _stage(objectives: [
        {'type': 'clearLines', 'count': 1},
      ]),
      generator: _smallPool(),
    );

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

    final view = container.read(gameSessionProvider)!;
    expect(view.phase, GamePhase.won);
    expect(view.starsEarned, 3);
    expect(view.lastPlacement!.clearedRows, [3]);
  });

  test('restart deals a fresh attempt with a different tray sequence', () {
    final container = ProviderContainer();
    addTearDown(container.dispose);
    final notifier = container.read(gameSessionProvider.notifier);
    notifier.startStage(_stage());
    notifier.tryPlace(0, 0, 0);
    notifier.restart();

    final view = container.read(gameSessionProvider)!;
    expect(view.phase, GamePhase.playing);
    expect(view.score, 0);
    expect(view.movesLeft, 20);
    expect(view.tray, hasLength(3));
  });
}
