import 'cell.dart';
import 'grid.dart';
import 'objective.dart';

/// A pre-placed cell in a stage layout.
class PresetCell {
  const PresetCell({
    required this.x,
    required this.y,
    required this.type,
    this.colorId = 0,
  });

  factory PresetCell.fromJson(Map<String, dynamic> json) => PresetCell(
        x: json['x'] as int,
        y: json['y'] as int,
        type: switch (json['t']) {
          'gem' => CellType.gem,
          'filled' => CellType.filled,
          _ => throw FormatException('Unknown preset cell type: ${json['t']}'),
        },
        colorId: (json['c'] as int?) ?? 0,
      );

  final int x;
  final int y;
  final CellType type;
  final int colorId;

  Map<String, dynamic> toJson() => {
        'x': x,
        'y': y,
        't': type.name,
        if (type == CellType.filled) 'c': colorId,
      };
}

/// Star rating from moves remaining at the win.
class StarThresholds {
  const StarThresholds({
    required this.twoMovesLeft,
    required this.threeMovesLeft,
  });

  factory StarThresholds.fromJson(Map<String, dynamic> json) =>
      StarThresholds(
        twoMovesLeft: (json['two'] as Map<String, dynamic>)['movesLeft'] as int,
        threeMovesLeft:
            (json['three'] as Map<String, dynamic>)['movesLeft'] as int,
      );

  final int twoMovesLeft;
  final int threeMovesLeft;

  int starsFor({required int movesLeft}) {
    if (movesLeft >= threeMovesLeft) return 3;
    if (movesLeft >= twoMovesLeft) return 2;
    return 1;
  }

  Map<String, dynamic> toJson() => {
        'two': {'movesLeft': twoMovesLeft},
        'three': {'movesLeft': threeMovesLeft},
      };
}

/// Immutable stage definition, the unit of season pack content.
class StageConfig {
  const StageConfig({
    required this.id,
    required this.seed,
    required this.moveLimit,
    required this.preset,
    required this.objectives,
    required this.stars,
    required this.generatorProfile,
    this.endless = false,
  });

  factory StageConfig.endless({required int seed}) => StageConfig(
        id: 'endless',
        seed: seed,
        moveLimit: 0,
        preset: const [],
        objectives: const [],
        stars: const StarThresholds(
            twoMovesLeft: 1 << 30, threeMovesLeft: 1 << 30),
        generatorProfile: 'mid',
        endless: true,
      );

  factory StageConfig.fromJson(Map<String, dynamic> json) => StageConfig(
        id: json['id'] as String,
        seed: json['seed'] as int,
        moveLimit: json['moveLimit'] as int,
        preset: [
          for (final cell in (json['preset'] as List? ?? const []))
            PresetCell.fromJson(cell as Map<String, dynamic>),
        ],
        objectives: [
          for (final obj in json['objectives'] as List)
            Objective.fromJson(obj as Map<String, dynamic>),
        ],
        stars: StarThresholds.fromJson(json['stars'] as Map<String, dynamic>),
        generatorProfile: (json['generatorProfile'] as String?) ?? 'mid',
      );

  final String id;
  final int seed;
  final int moveLimit;
  final List<PresetCell> preset;
  final List<Objective> objectives;
  final StarThresholds stars;
  final String generatorProfile;

  /// Runtime-only: score-attack mode with no objectives or move limit.
  /// Never serialized — packs always describe objective stages.
  final bool endless;

  GridState initialGrid() {
    var grid = GridState.empty();
    for (final cell in preset) {
      grid = grid.withCell(
        cell.x,
        cell.y,
        Cell(cell.type, colorId: cell.colorId),
      );
    }
    return grid;
  }

  Map<String, dynamic> toJson() => {
        'id': id,
        'seed': seed,
        'moveLimit': moveLimit,
        'preset': [for (final cell in preset) cell.toJson()],
        'objectives': [for (final obj in objectives) obj.toJson()],
        'stars': stars.toJson(),
        'generatorProfile': generatorProfile,
      };
}
