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> _kdsHub;
    private readonly IHubContext<GuestOrderHub> _guestHub;
    private readonly ISmsService _sms;
    private readonly ILogger<OrderNotificationService> _logger;

    public OrderNotificationService(
        AppDbContext db,
        IHubContext<KdsHub> kdsHub,
        IHubContext<GuestOrderHub> guestHub,
        ISmsService sms,
        ILogger<OrderNotificationService> 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<string?> 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);
}
