import '../engine/game_event.dart';

/// Stage win condition with immutable progress tracking. New objective types
/// plug in as subclasses with a JSON tag.
sealed class Objective {
  const Objective();

  factory Objective.clearGems(int count) => ClearGemsObjective(count, 0);
  factory Objective.reachScore(int target) => ReachScoreObjective(target, 0);
  factory Objective.clearLines(int count) => ClearLinesObjective(count, 0);

  factory Objective.fromJson(Map<String, dynamic> json) {
    switch (json['type']) {
      case 'clearGems':
        return Objective.clearGems(json['count'] as int);
      case 'reachScore':
        return Objective.reachScore(json['target'] as int);
      case 'clearLines':
        return Objective.clearLines(json['count'] as int);
      default:
        throw FormatException('Unknown objective type: ${json['type']}');
    }
  }

  int get target;
  int get current;
  bool get isComplete => current >= target;

  Objective onEvent(GameEvent event);

  Map<String, dynamic> toJson();
}

class ClearGemsObjective extends Objective {
  const ClearGemsObjective(this.target, this.current);

  @override
  final int target;
  @override
  final int current;

  @override
  Objective onEvent(GameEvent event) => switch (event) {
        LinesCleared(:final gems) =>
          ClearGemsObjective(target, current + gems),
        _ => this,
      };

  @override
  Map<String, dynamic> toJson() => {'type': 'clearGems', 'count': target};
}

class ReachScoreObjective extends Objective {
  const ReachScoreObjective(this.target, this.current);

  @override
  final int target;
  @override
  final int current;

  @override
  Objective onEvent(GameEvent event) => switch (event) {
        ScoreChanged(:final total) => ReachScoreObjective(target, total),
        _ => this,
      };

  @override
  Map<String, dynamic> toJson() => {'type': 'reachScore', 'target': target};
}

class ClearLinesObjective extends Objective {
  const ClearLinesObjective(this.target, this.current);

  @override
  final int target;
  @override
  final int current;

  @override
  Objective onEvent(GameEvent event) => switch (event) {
        LinesCleared(:final lines) =>
          ClearLinesObjective(target, current + lines),
        _ => this,
      };

  @override
  Map<String, dynamic> toJson() => {'type': 'clearLines', 'count': target};
}
