using Meezi.API.Models.Public; using Meezi.Core.Entities; using Meezi.Core.Enums; using Meezi.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace Meezi.API.Services; public interface IReservationService { Task> GetReservationsAsync( string cafeId, DateOnly? date, ReservationStatus? status, CancellationToken cancellationToken = default); Task CreateAsync( string cafeId, CreateReservationRequest request, CancellationToken cancellationToken = default); Task UpdateStatusAsync( string cafeId, string reservationId, ReservationStatus status, CancellationToken cancellationToken = default); } public class ReservationService : IReservationService { private readonly AppDbContext _db; private readonly IKdsNotifier _kdsNotifier; public ReservationService(AppDbContext db, IKdsNotifier kdsNotifier) { _db = db; _kdsNotifier = kdsNotifier; } public async Task> GetReservationsAsync( string cafeId, DateOnly? date, ReservationStatus? status, CancellationToken cancellationToken = default) { var query = _db.TableReservations .Include(r => r.Table) .Where(r => r.CafeId == cafeId); if (date.HasValue) query = query.Where(r => r.Date == date.Value); if (status.HasValue) query = query.Where(r => r.Status == status.Value); var list = await query .OrderBy(r => r.Date) .ThenBy(r => r.Time) .ToListAsync(cancellationToken); return list.Select(Map).ToList(); } public async Task CreateAsync( string cafeId, CreateReservationRequest request, CancellationToken cancellationToken = default) { if (!string.IsNullOrEmpty(request.TableId)) { var tableOk = await _db.Tables.AnyAsync( t => t.Id == request.TableId && t.CafeId == cafeId, cancellationToken); if (!tableOk) return null; } var entity = new TableReservation { CafeId = cafeId, TableId = request.TableId, GuestName = request.GuestName.Trim(), GuestPhone = request.GuestPhone.Trim(), Date = request.Date, Time = request.Time, PartySize = request.PartySize, Notes = request.Notes, Status = ReservationStatus.Confirmed }; _db.TableReservations.Add(entity); await _db.SaveChangesAsync(cancellationToken); if (!string.IsNullOrEmpty(entity.TableId)) await _kdsNotifier.NotifyTableStatusChangedAsync(cafeId, cancellationToken); var loaded = await _db.TableReservations .Include(r => r.Table) .FirstAsync(r => r.Id == entity.Id, cancellationToken); return Map(loaded); } public async Task UpdateStatusAsync( string cafeId, string reservationId, ReservationStatus status, CancellationToken cancellationToken = default) { var entity = await _db.TableReservations .Include(r => r.Table) .FirstOrDefaultAsync(r => r.Id == reservationId && r.CafeId == cafeId, cancellationToken); if (entity is null) return null; entity.Status = status; await _db.SaveChangesAsync(cancellationToken); if (!string.IsNullOrEmpty(entity.TableId)) await _kdsNotifier.NotifyTableStatusChangedAsync(cafeId, cancellationToken); return Map(entity); } internal static ReservationDto Map(TableReservation r) => new( r.Id, r.CafeId, r.TableId, r.Table?.Number, r.GuestName, r.GuestPhone, r.Date, r.Time, r.PartySize, r.Status, r.Notes); }