using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Meezi.API.Hubs; using Meezi.API.Models.Notifications; using Meezi.API.Models.Orders; using Meezi.API.Models.Public; using Meezi.API.Services.Printing; using Meezi.Core.Entities; using Meezi.Core.Enums; using Meezi.Core.Interfaces; using Meezi.Infrastructure.Data; namespace Meezi.API.Services; public class OrderNotificationService : IOrderNotificationService { private readonly AppDbContext _db; private readonly IHubContext _kdsHub; private readonly IHubContext _guestHub; private readonly ISmsService _sms; private readonly ILogger _logger; public OrderNotificationService( AppDbContext db, IHubContext kdsHub, IHubContext guestHub, ISmsService sms, ILogger logger) { _db = db; _kdsHub = kdsHub; _guestHub = guestHub; _sms = sms; _logger = logger; } public async Task NotifyGuestOrderPlacedAsync(Order order, LiveOrderDto live, CancellationToken ct = default) { if (order.Source != OrderSource.GuestQr) return; var tableNumber = await ResolveTableNumberAsync(order, ct); var orderNumber = ReceiptPrintFormatting.OrderNumberLabel(order.DisplayNumber > 0 ? order.DisplayNumber : ReceiptPrintFormatting.StableDisplayNumberFromId(order.Id)); var notification = new CafeNotification { CafeId = order.CafeId, Type = "guest_order_new", Title = $"سفارش جدید میز {tableNumber ?? "—"}", Body = $"شماره {orderNumber} · {order.Total:N0} ت", ReferenceId = order.Id, TableNumber = tableNumber }; _db.CafeNotifications.Add(notification); await _db.SaveChangesAsync(ct); var dto = MapNotification(notification); await _kdsHub.Clients.Group(KdsHub.GroupName(order.CafeId)) .SendAsync("NotificationReceived", dto, ct); await PushGuestTrackAsync(order, ct); } public async Task NotifyOrderStatusChangedAsync(Order order, CancellationToken ct = default) { await PushGuestTrackAsync(order, ct); if (order.Source != OrderSource.GuestQr) return; if (order.Status == OrderStatus.Ready) { var tableNumber = await ResolveTableNumberAsync(order, ct); var orderNumber = ReceiptPrintFormatting.OrderNumberLabel(order.DisplayNumber > 0 ? order.DisplayNumber : ReceiptPrintFormatting.StableDisplayNumberFromId(order.Id)); var readyNotification = new CafeNotification { CafeId = order.CafeId, Type = "guest_order_ready", Title = $"سفارش آماده — میز {tableNumber ?? "—"}", Body = $"شماره {orderNumber}", ReferenceId = order.Id, TableNumber = tableNumber }; _db.CafeNotifications.Add(readyNotification); await _db.SaveChangesAsync(ct); await _kdsHub.Clients.Group(KdsHub.GroupName(order.CafeId)) .SendAsync("NotificationReceived", MapNotification(readyNotification), ct); await TrySmsGuestReadyAsync(order, tableNumber, orderNumber, ct); } } private async Task PushGuestTrackAsync(Order order, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(order.GuestTrackingToken)) return; var full = await _db.Orders .Include(o => o.Items) .ThenInclude(i => i.MenuItem) .Include(o => o.Table) .FirstOrDefaultAsync(o => o.Id == order.Id, cancellationToken); if (full is null) return; var track = OrderTrackingHelper.BuildTrackDto(full); await _guestHub.Clients.Group(GuestOrderHub.OrderGroup(order.Id)) .SendAsync("OrderTrackUpdated", track, cancellationToken); } private async Task TrySmsGuestReadyAsync( Order order, string? tableNumber, string orderNumber, CancellationToken ct) { var phone = order.GuestPhone?.Trim(); if (string.IsNullOrEmpty(phone)) return; try { var msg = $"میزی: سفارش {orderNumber} (میز {tableNumber ?? "—"}) آماده است."; await _sms.SendMessageAsync(phone, msg, ct); } catch (Exception ex) { _logger.LogWarning(ex, "Guest ready SMS failed for order {OrderId}", order.Id); } } private async Task ResolveTableNumberAsync(Order order, CancellationToken cancellationToken) { if (order.Table is not null) return order.Table.Number; if (string.IsNullOrEmpty(order.TableId)) return null; return await _db.Tables .Where(t => t.Id == order.TableId) .Select(t => t.Number) .FirstOrDefaultAsync(cancellationToken); } public async Task NotifyCallWaiterAsync(string cafeId, string tableId, string tableNumber, CancellationToken ct = default) { var notification = new CafeNotification { CafeId = cafeId, Type = "table_call_waiter", Title = $"درخواست گارسون — میز {tableNumber}", Body = "مشتری درخواست خدمت کرد", ReferenceId = tableId, TableNumber = tableNumber }; _db.CafeNotifications.Add(notification); await _db.SaveChangesAsync(ct); await _kdsHub.Clients.Group(KdsHub.GroupName(cafeId)) .SendAsync("NotificationReceived", MapNotification(notification), ct); } private static CafeNotificationDto MapNotification(CafeNotification n) => new(n.Id, n.Type, n.Title, n.Body, n.ReferenceId, n.TableNumber, n.IsRead, n.CreatedAt); }