using Core.Entities; using Core.Services; using Data; using Microsoft.EntityFrameworkCore; using System.Security.Claims; namespace WebApp.Services; public class TeamMeetingHistoryService : ITeamMeetingHistoryService { private readonly AppDbContext _context; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; private readonly INotesService _notesService; private readonly INoteNamingService _noteNamingService; public TeamMeetingHistoryService( AppDbContext context, IHttpContextAccessor httpContextAccessor, ILogger logger, INotesService notesService, INoteNamingService noteNamingService) { _context = context; _httpContextAccessor = httpContextAccessor; _logger = logger; _notesService = notesService; _noteNamingService = noteNamingService; } private string? GetCurrentUserEmail() { var user = _httpContextAccessor.HttpContext?.User; if (user == null) return null; return user.FindFirstValue(ClaimTypes.Email) ?? user.Identity?.Name; } public async Task> GetMeetingHistoriesAsync(DateTime? startDate = null, DateTime? endDate = null) { var query = _context.TeamMeetingHistories .AsNoTracking() .Include(tmh => tmh.Teams) .ThenInclude(t => t.Event) .Include(tmh => tmh.Students) .AsQueryable(); if (startDate.HasValue) { var start = startDate.Value.Date; query = query.Where(tmh => tmh.MeetingDate >= start); } if (endDate.HasValue) { var end = endDate.Value.Date.AddDays(1); // Include the entire end date query = query.Where(tmh => tmh.MeetingDate < end); } return await query .OrderByDescending(tmh => tmh.MeetingDate) .ToListAsync(); } public async Task GetMeetingHistoryAsync(int id) { return await _context.TeamMeetingHistories .Include(tmh => tmh.Teams) .ThenInclude(t => t.Event) .Include(tmh => tmh.Teams) .ThenInclude(t => t.Students) .Include(tmh => tmh.Students) .FirstOrDefaultAsync(tmh => tmh.Id == id); } public async Task CreateMeetingHistoryAsync(TeamMeetingHistory meetingHistory) { // Create a new meeting history entity to avoid tracking conflicts var newMeetingHistory = new TeamMeetingHistory { MeetingDate = meetingHistory.MeetingDate.Date // Normalize to date only }; // Attach teams by loading them from the database to avoid tracking conflicts var teamIds = meetingHistory.Teams.Select(t => t.Id).ToList(); var teams = await _context.Teams .Where(t => teamIds.Contains(t.Id)) .ToListAsync(); // Attach students by loading them from the database to avoid tracking conflicts var studentIds = meetingHistory.Students.Select(s => s.Id).ToList(); var students = await _context.Students .Where(s => studentIds.Contains(s.Id)) .ToListAsync(); // Attach teams and students to the new meeting history newMeetingHistory.Teams = teams; newMeetingHistory.Students = students; _context.TeamMeetingHistories.Add(newMeetingHistory); await _context.SaveChangesAsync(); _logger.LogInformation("Meeting history created: {MeetingHistoryId} for date {MeetingDate}", newMeetingHistory.Id, newMeetingHistory.MeetingDate); return newMeetingHistory; } public async Task UpdateMeetingHistoryAsync(TeamMeetingHistory meetingHistory) { var existingHistory = await _context.TeamMeetingHistories .Include(tmh => tmh.Teams) .Include(tmh => tmh.Students) .FirstOrDefaultAsync(tmh => tmh.Id == meetingHistory.Id); if (existingHistory == null) { throw new InvalidOperationException($"Meeting history with ID {meetingHistory.Id} not found."); } // Update properties existingHistory.MeetingDate = meetingHistory.MeetingDate.Date; // Normalize to date only // Update teams existingHistory.Teams.Clear(); foreach (var team in meetingHistory.Teams) { var existingTeam = await _context.Teams.FindAsync(team.Id); if (existingTeam != null) { existingHistory.Teams.Add(existingTeam); } } // Update students existingHistory.Students.Clear(); foreach (var student in meetingHistory.Students) { var existingStudent = await _context.Students.FindAsync(student.Id); if (existingStudent != null) { existingHistory.Students.Add(existingStudent); } } await _context.SaveChangesAsync(); _logger.LogInformation("Meeting history updated: {MeetingHistoryId} by {User}", meetingHistory.Id, GetCurrentUserEmail()); return existingHistory; } public async Task DeleteMeetingHistoryAsync(int id) { var meetingHistory = await _context.TeamMeetingHistories .FirstOrDefaultAsync(tmh => tmh.Id == id); if (meetingHistory == null) { throw new InvalidOperationException($"Meeting history with ID {id} not found."); } _context.TeamMeetingHistories.Remove(meetingHistory); await _context.SaveChangesAsync(); _logger.LogInformation("Meeting history deleted: {MeetingHistoryId}", id); } /// /// Gets the meeting note for a specific meeting date by looking it up by title. /// /// The date of the meeting /// The note if found, null otherwise public async Task GetMeetingNoteAsync(DateTime meetingDate) { var noteTitle = _noteNamingService.GetMeetingNoteTitle(meetingDate); var notes = await _notesService.GetNotesAsync(includeDeleted: false); return notes.FirstOrDefault(n => n.Title == noteTitle); } public async Task> GetTeamsForDateAsync(DateTime date) { var normalizedDate = date.Date; return await _context.TeamMeetingHistories .AsNoTracking() .Where(tmh => tmh.MeetingDate == normalizedDate) .SelectMany(tmh => tmh.Teams) .Include(t => t.Event) .Distinct() .ToListAsync(); } public async Task> GetStudentsForDateAsync(DateTime date) { var normalizedDate = date.Date; return await _context.TeamMeetingHistories .AsNoTracking() .Where(tmh => tmh.MeetingDate == normalizedDate) .SelectMany(tmh => tmh.Students) .Distinct() .ToListAsync(); } }