import 'package:block_seasons/game/engine/line_clear.dart';
import 'package:block_seasons/game/models/cell.dart';
import 'package:block_seasons/game/models/grid.dart';
import 'package:flutter_test/flutter_test.dart';

GridState _fillRow(GridState grid, int y, {Set<int>? skip}) {
  for (var x = 0; x < GridState.size; x++) {
    if (skip?.contains(x) ?? false) continue;
    grid = grid.withCell(x, y, const Cell(CellType.filled, colorId: 0));
  }
  return grid;
}

GridState _fillCol(GridState grid, int x, {Set<int>? skip}) {
  for (var y = 0; y < GridState.size; y++) {
    if (skip?.contains(y) ?? false) continue;
    grid = grid.withCell(x, y, const Cell(CellType.filled, colorId: 0));
  }
  return grid;
}

void main() {
  group('detectAndClear', () {
    test('no full lines leaves the grid untouched', () {
      final grid = _fillRow(GridState.empty(), 3, skip: {5});
      final result = detectAndClear(grid);
      expect(result.clearedRows, isEmpty);
      expect(result.clearedCols, isEmpty);
      expect(result.linesCleared, 0);
      expect(result.gemsCleared, 0);
      expect(result.grid.occupiedCount, grid.occupiedCount);
    });

    test('clears a single full row', () {
      final grid = _fillRow(GridState.empty(), 2);
      final result = detectAndClear(grid);
      expect(result.clearedRows, [2]);
      expect(result.clearedCols, isEmpty);
      expect(result.linesCleared, 1);
      expect(result.grid.occupiedCount, 0);
    });

    test('clears a single full column', () {
      final grid = _fillCol(GridState.empty(), 6);
      final result = detectAndClear(grid);
      expect(result.clearedRows, isEmpty);
      expect(result.clearedCols, [6]);
      expect(result.grid.occupiedCount, 0);
    });

    test('clears two rows at once', () {
      var grid = _fillRow(GridState.empty(), 0);
      grid = _fillRow(grid, 7);
      final result = detectAndClear(grid);
      expect(result.clearedRows, [0, 7]);
      expect(result.linesCleared, 2);
      expect(result.grid.occupiedCount, 0);
    });

    test('row and column sharing a corner clear together', () {
      var grid = _fillRow(GridState.empty(), 4);
      grid = _fillCol(grid, 4);
      final result = detectAndClear(grid);
      expect(result.clearedRows, [4]);
      expect(result.clearedCols, [4]);
      expect(result.linesCleared, 2);
      // 8 + 8 - 1 shared cell were occupied; all gone now.
      expect(result.grid.occupiedCount, 0);
    });

    test('counts gems in cleared lines, intersection gem only once', () {
      var grid = GridState.empty();
      grid = grid.withCell(4, 4, const Cell(CellType.gem));
      grid = grid.withCell(0, 4, const Cell(CellType.gem));
      grid = grid.withCell(4, 0, const Cell(CellType.gem));
      grid = _fillRow(grid, 4, skip: {0, 4});
      grid = _fillCol(grid, 4, skip: {0, 4});
      final result = detectAndClear(grid);
      expect(result.clearedRows, [4]);
      expect(result.clearedCols, [4]);
      // Gems at (0,4), (4,0), and the shared (4,4) -> exactly 3.
      expect(result.gemsCleared, 3);
    });

    test('cells outside cleared lines survive', () {
      var grid = _fillRow(GridState.empty(), 1);
      grid = grid.withCell(3, 5, const Cell(CellType.filled, colorId: 2));
      final result = detectAndClear(grid);
      expect(result.grid.occupiedCount, 1);
      expect(result.grid.cellAt(3, 5).colorId, 2);
    });
  });
}
