using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; using TournamentOrganizer.Models; namespace TournamentOrganizer.ViewModels; public partial class TeamsViewModel : ViewModelBase { private readonly TournamentContext _context; [ObservableProperty] private ObservableCollection _teams = []; [ObservableProperty] private TeamDisplay? _selectedTeam; [ObservableProperty] private string _teamName = string.Empty; [ObservableProperty] private ObservableCollection _players = []; [ObservableProperty] private PlayerEntry? _selectedPlayer; [ObservableProperty] private PlayerEntry? _selectedLeader; [ObservableProperty] private string _newPlayerName = string.Empty; [ObservableProperty] private string _newPlayerContact = string.Empty; [ObservableProperty] private string _filterMemberName = string.Empty; [ObservableProperty] private string? _selectedEventFilter; [ObservableProperty] private string? _selectedTournamentFilter; [ObservableProperty] private ObservableCollection _availableEvents = []; [ObservableProperty] private ObservableCollection _availableTournaments = []; [ObservableProperty] private string _eventFilterSearch = string.Empty; [ObservableProperty] private string _tournamentFilterSearch = string.Empty; [ObservableProperty] private bool _isEventDropdownOpen; [ObservableProperty] private bool _isTournamentDropdownOpen; [ObservableProperty] private bool _isEditing; [ObservableProperty] private string _statusMessage = string.Empty; private readonly ObservableCollection _filteredEvents = []; public ObservableCollection FilteredEvents => _filteredEvents; private readonly ObservableCollection _filteredTournaments = []; public ObservableCollection FilteredTournaments => _filteredTournaments; partial void OnEventFilterSearchChanged(string value) => UpdateFilteredEvents(); partial void OnTournamentFilterSearchChanged(string value) => UpdateFilteredTournaments(); private void UpdateFilteredEvents() { _filteredEvents.Clear(); foreach (var e in AvailableEvents.Where(e => string.IsNullOrWhiteSpace(EventFilterSearch) || e.ToLower().Contains(EventFilterSearch.ToLower()))) { _filteredEvents.Add(e); } } private void UpdateFilteredTournaments() { _filteredTournaments.Clear(); foreach (var t in AvailableTournaments.Where(t => string.IsNullOrWhiteSpace(TournamentFilterSearch) || t.ToLower().Contains(TournamentFilterSearch.ToLower()))) { _filteredTournaments.Add(t); } } public TeamsViewModel() { _context = new TournamentContext(); } public async Task LoadTeams() { var events = await _context.Events.ToListAsync(); AvailableEvents.Clear(); foreach (var e in events) { AvailableEvents.Add(e.Name); } UpdateFilteredEvents(); var games = await _context.Games.ToListAsync(); AvailableTournaments.Clear(); foreach (var g in games) { foreach (var e in events) { AvailableTournaments.Add($"{g.Name} @ {e.Name}"); } } UpdateFilteredTournaments(); var query = _context.Teams .Include(t => t.Players) .Include(t => t.Leader) .Include(t => t.Matches) .ThenInclude(tp => tp.Round) .ThenInclude(m => m.Tournament) .ThenInclude(t => t.Event) .AsSplitQuery(); var allTeams = await query.ToListAsync(); var filtered = allTeams.AsEnumerable(); if (!string.IsNullOrWhiteSpace(FilterMemberName)) { var filter = FilterMemberName.ToLower(); filtered = filtered.Where(t => t.Players.Any(p => p.Name.ToLower().Contains(filter))); } if (!string.IsNullOrWhiteSpace(SelectedEventFilter)) { var filter = SelectedEventFilter.ToLower(); filtered = filtered.Where(t => GetAssociatedEvents(t).Any(e => e.ToLower().Contains(filter))); } if (!string.IsNullOrWhiteSpace(SelectedTournamentFilter)) { var filter = SelectedTournamentFilter.ToLower(); filtered = filtered.Where(t => GetAssociatedTournaments(t).Any(tn => tn.ToLower().Contains(filter))); } Teams.Clear(); foreach (var team in filtered) { Teams.Add(new TeamDisplay(team)); } SelectedTeam = null; IsEditing = false; } public void SelectEventFilter(string eventName) { SelectedEventFilter = eventName; IsEventDropdownOpen = false; EventFilterSearch = string.Empty; _ = LoadTeams(); } public void ClearEventFilter() { SelectedEventFilter = null; EventFilterSearch = string.Empty; _ = LoadTeams(); } public void SelectTournamentFilter(string tournamentName) { SelectedTournamentFilter = tournamentName; IsTournamentDropdownOpen = false; TournamentFilterSearch = string.Empty; _ = LoadTeams(); } public void ClearTournamentFilter() { SelectedTournamentFilter = null; TournamentFilterSearch = string.Empty; _ = LoadTeams(); } [RelayCommand] private async Task RefreshTeams() { await LoadTeams(); } [RelayCommand] private void CreateNewTeam() { TeamName = "New Team"; Players.Clear(); SelectedLeader = null; SelectedPlayer = null; IsEditing = true; SelectedTeam = null; NewPlayerName = string.Empty; NewPlayerContact = string.Empty; StatusMessage = "Creating new team"; } [RelayCommand] private async Task SaveTeam() { if (string.IsNullOrWhiteSpace(TeamName)) { StatusMessage = "Team name is required"; return; } if (Players.Count == 0) { StatusMessage = "Team must have at least one member"; return; } Team? team; if (SelectedTeam != null && SelectedTeam.Id > 0) { team = await _context.Teams .Include(t => t.Players) .FirstOrDefaultAsync(t => t.Id == SelectedTeam.Id); if (team == null) { StatusMessage = "Team not found"; return; } team.Name = TeamName; var existingPlayerIds = team.Players.Select(p => p.Id).ToHashSet(); var currentPlayerIds = Players.Where(p => p.Id > 0).Select(p => p.Id).ToHashSet(); var playersToRemove = team.Players.Where(p => !currentPlayerIds.Contains(p.Id)).ToList(); foreach (var player in playersToRemove) { team.Players.Remove(player); _context.Players.Remove(player); } foreach (var entry in Players) { if (entry.Id > 0) { var existing = team.Players.FirstOrDefault(p => p.Id == entry.Id); if (existing != null) { existing.Name = entry.Name; existing.Contact = entry.Contact; } } else { var newPlayer = new Player { Name = entry.Name, Contact = entry.Contact, Team = team }; team.Players.Add(newPlayer); entry.Id = newPlayer.Id; } } if (SelectedLeader != null) { var leader = team.Players.FirstOrDefault(p => (SelectedLeader.Id > 0 && p.Id == SelectedLeader.Id) || (SelectedLeader.Id == 0 && p.Name == SelectedLeader.Name && p.Contact == SelectedLeader.Contact)); team.Leader = leader; } else { team.Leader = null; } } else { team = new Team { Name = TeamName, Players = [], Matches = [] }; var playerEntries = new List<(PlayerEntry Entry, Player Player)>(); foreach (var entry in Players) { var player = new Player { Name = entry.Name, Contact = entry.Contact, Team = team }; team.Players.Add(player); playerEntries.Add((entry, player)); } _context.Teams.Add(team); await _context.SaveChangesAsync(); if (team.Players.Count > 0) { int leaderIndex = 0; if (SelectedLeader != null) { var idx = Players.IndexOf(SelectedLeader); if (idx >= 0) leaderIndex = idx; } team.LeaderId = playerEntries[leaderIndex].Player.Id; await _context.SaveChangesAsync(); } } await _context.SaveChangesAsync(); StatusMessage = $"Team '{TeamName}' saved successfully"; await LoadTeams(); } [RelayCommand] private async Task DeleteTeam() { if (SelectedTeam == null || SelectedTeam.Id == 0) { StatusMessage = "Select a team to delete"; return; } var team = await _context.Teams .Include(t => t.Players) .FirstOrDefaultAsync(t => t.Id == SelectedTeam.Id); if (team == null) { StatusMessage = "Team not found"; return; } _context.Teams.Remove(team); await _context.SaveChangesAsync(); StatusMessage = $"Team '{team.Name}' deleted"; await LoadTeams(); } [RelayCommand] private void AddPlayer() { if (string.IsNullOrWhiteSpace(NewPlayerName)) { StatusMessage = "Player name is required"; return; } var entry = new PlayerEntry { Name = NewPlayerName, Contact = NewPlayerContact }; Players.Add(entry); if (SelectedLeader == null) { SelectedLeader = entry; } NewPlayerName = string.Empty; NewPlayerContact = string.Empty; StatusMessage = $"Player '{entry.Name}' added"; } [RelayCommand] private void RemovePlayer() { if (SelectedPlayer == null) { StatusMessage = "Select a player to remove"; return; } var wasLeader = SelectedLeader == SelectedPlayer; Players.Remove(SelectedPlayer); if (wasLeader) { SelectedLeader = Players.FirstOrDefault(); } SelectedPlayer = null; StatusMessage = "Player removed"; } [RelayCommand] private void SetLeader() { if (SelectedPlayer == null) { StatusMessage = "Select a player to make leader"; return; } SelectedLeader = SelectedPlayer; StatusMessage = $"{SelectedPlayer.Name} is now the leader"; } partial void OnSelectedTeamChanged(TeamDisplay? value) { if (value == null) { IsEditing = false; return; } TeamName = value.Name; Players.Clear(); foreach (var player in value.Players) { Players.Add(player); } SelectedLeader = Players.FirstOrDefault(p => p.Id == value.LeaderId); SelectedPlayer = null; IsEditing = true; NewPlayerName = string.Empty; NewPlayerContact = string.Empty; StatusMessage = $"Editing team '{value.Name}'"; } partial void OnFilterMemberNameChanged(string value) => ApplyFilters(); private async void ApplyFilters() { await LoadTeams(); } private static List GetAssociatedEvents(Team team) { var events = new List(); if (team.Matches != null) { foreach (var tp in team.Matches) { if (tp.Round?.Tournament?.Event != null) { var eventName = tp.Round.Tournament.Event.Name; if (!events.Contains(eventName)) { events.Add(eventName); } } } } return events; } private static List GetAssociatedTournaments(Team team) { var tournaments = new List(); if (team.Matches != null) { foreach (var tp in team.Matches) { if (tp.Round?.Tournament != null) { var tournamentName = GetTournamentDisplayName(tp.Round.Tournament); if (!tournaments.Contains(tournamentName)) { tournaments.Add(tournamentName); } } } } return tournaments; } public static string GetTournamentDisplayName(Tournament tournament) { return $"{tournament.Game.Name} @ {tournament.Event.Name}"; } } public class TeamDisplay { public int Id { get; set; } public string Name { get; set; } = string.Empty; public int? LeaderId { get; set; } public string? LeaderName { get; set; } public List Players { get; set; } = []; public List AssociatedEvents { get; set; } = []; public List AssociatedTournaments { get; set; } = []; public TeamDisplay() { } public TeamDisplay(Team team) { Id = team.Id; Name = team.Name; LeaderId = team.LeaderId; LeaderName = team.Leader?.Name; Players = team.Players?.Select(p => new PlayerEntry(p)).ToList() ?? []; var events = new HashSet(); var tournaments = new HashSet(); if (team.Matches != null) { foreach (var tp in team.Matches) { if (tp.Round?.Tournament != null) { tournaments.Add(TeamsViewModel.GetTournamentDisplayName(tp.Round.Tournament)); if (tp.Round.Tournament.Event != null) { events.Add(tp.Round.Tournament.Event.Name); } } } } AssociatedEvents = events.ToList(); AssociatedTournaments = tournaments.ToList(); } } public partial class PlayerEntry : ObservableObject { [ObservableProperty] private int _id; [ObservableProperty] private string _name = string.Empty; [ObservableProperty] private string _contact = string.Empty; public PlayerEntry() { } public PlayerEntry(Player player) { Id = player.Id; Name = player.Name; Contact = player.Contact; } }