import 'package:clock/clock.dart'; import 'package:collection/collection.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/score_entry.dart'; import 'package:tallee/data/models/team.dart'; import 'package:uuid/uuid.dart'; class Match { final String id; final DateTime createdAt; final DateTime? endedAt; final String name; final Game game; final Group? group; final List players; final List? teams; final String notes; Map scores; Match({ required this.name, required this.game, required this.players, this.endedAt, this.group, this.teams, this.notes = '', String? id, DateTime? createdAt, Map? scores, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? clock.now(), scores = scores ?? {for (Player p in players) p.id: null}; @override String toString() { return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, mvp: $mvp}'; } Match copyWith({ String? id, DateTime? createdAt, DateTime? endedAt, String? name, Game? game, Group? group, List? players, List? teams, String? notes, Map? scores, }) { return Match( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, endedAt: endedAt ?? this.endedAt, name: name ?? this.name, game: game ?? this.game, group: group ?? this.group, players: players ?? this.players, teams: teams ?? this.teams, notes: notes ?? this.notes, scores: scores ?? this.scores, ); } @override bool operator ==(Object other) => identical(this, other) || other is Match && runtimeType == other.runtimeType && id == other.id && createdAt == other.createdAt && endedAt == other.endedAt && name == other.name && game == other.game && group == other.group && const DeepCollectionEquality().equals(players, other.players) && const DeepCollectionEquality().equals(teams, other.teams) && notes == other.notes && const DeepCollectionEquality().equals(scores, other.scores); @override int get hashCode => Object.hash( id, createdAt, endedAt, name, game, group, const DeepCollectionEquality().hash(players), const DeepCollectionEquality().hash(teams), notes, const DeepCollectionEquality().hash(scores), ); Match.fromJson(Map json) : id = json['id'], createdAt = DateTime.parse(json['createdAt']), endedAt = json['endedAt'] != null ? DateTime.parse(json['endedAt']) : null, name = json['name'], game = Game( name: '', ruleset: Ruleset.singleWinner, description: '', color: AppColor.blue, icon: '', ), group = null, players = [], teams = [], scores = json['scores'] != null ? (json['scores'] as Map).map( (key, value) => MapEntry( key, value != null ? ScoreEntry.fromJson(value as Map) : null, ), ) : {}, notes = json['notes'] ?? ''; Map toJson() => { 'id': id, 'createdAt': createdAt.toIso8601String(), 'endedAt': endedAt?.toIso8601String(), 'name': name, 'gameId': game.id, 'groupId': group?.id, 'playerIds': players.map((player) => player.id).toList(), 'teams': teams?.map((team) => team.toJson()).toList(), 'scores': scores.map((key, value) => MapEntry(key, value?.toJson())), 'notes': notes, }; List get mvp { if (players.isEmpty || scores.isEmpty) return []; switch (game.ruleset) { case Ruleset.highestScore: return _getPlayersWithHighestScore(); case Ruleset.lowestScore: return _getPlayersWithLowestScore(); case Ruleset.singleWinner: return _getPlayersWithHighestScore().take(1).toList(); case Ruleset.singleLoser: return _getPlayersWithLowestScore().take(1).toList(); case Ruleset.multipleWinners: return _getPlayersWithHighestScore().toList(); case Ruleset.placement: return _getPlayersWithHighestScore().take(1).toList(); } } List _getPlayersWithHighestScore() { if (players.isEmpty || scores.values.every((score) => score == null)) { return []; } final int highestScore = players .map((player) => scores[player.id]?.score) .whereType() .reduce((max, score) => score > max ? score : max); return players.where((player) { final playerScores = scores[player.id]; if (playerScores == null) return false; return playerScores.score == highestScore; }).toList(); } List _getPlayersWithLowestScore() { if (players.isEmpty || scores.values.every((score) => score == null)) { return []; } final int lowestScore = players .map((player) => scores[player.id]?.score) .whereType() .reduce((min, score) => score < min ? score : min); return players.where((player) { final playerScore = scores[player.id]; if (playerScore == null) return false; return playerScore.score == lowestScore; }).toList(); } }