using Microsoft.AspNetCore.SignalR;
using Meezi.Admin.API.Hubs;
using Meezi.Admin.API.Models;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Meezi.Admin.API.Services;

public interface IAdminNotificationService
{
    Task<IReadOnlyList<AdminNotificationRowDto>> ListAsync(
        int limit,
        string? cafeId,
        CancellationToken ct = default);

    Task<BroadcastNotificationResult> BroadcastAsync(
        string title,
        string body,
        string adminId,
        CancellationToken ct = default);

    Task<bool> DeleteAsync(string notificationId, CancellationToken ct = default);
}

public class AdminNotificationService : IAdminNotificationService
{
    private readonly AppDbContext _db;
    private readonly IHubContext<KdsHub> _hub;

    public AdminNotificationService(AppDbContext db, IHubContext<KdsHub> hub)
    {
        _db = db;
        _hub = hub;
    }

    public async Task<IReadOnlyList<AdminNotificationRowDto>> ListAsync(
        int limit,
        string? cafeId,
        CancellationToken ct = default)
    {
        limit = Math.Clamp(limit, 1, 200);
        var q =
            from n in _db.CafeNotifications.AsNoTracking()
            join c in _db.Cafes.AsNoTracking() on n.CafeId equals c.Id
            select new { n, c };

        if (!string.IsNullOrWhiteSpace(cafeId))
            q = q.Where(x => x.n.CafeId == cafeId);

        return await q
            .OrderByDescending(x => x.n.CreatedAt)
            .Take(limit)
            .Select(x => new AdminNotificationRowDto(
                x.n.Id,
                x.n.CafeId,
                x.c.Name,
                x.n.Type,
                x.n.Title,
                x.n.Body,
                x.n.IsRead,
                x.n.CreatedAt))
            .ToListAsync(ct);
    }

    public async Task<BroadcastNotificationResult> BroadcastAsync(
        string title,
        string body,
        string adminId,
        CancellationToken ct = default)
    {
        var cafes = await _db.Cafes
            .AsNoTracking()
            .Where(c => !c.IsSuspended)
            .Select(c => c.Id)
            .ToListAsync(ct);

        var notifications = new List<Core.Entities.CafeNotification>();
        foreach (var cafeId in cafes)
        {
            notifications.Add(new Core.Entities.CafeNotification
            {
                CafeId = cafeId,
                Type = "platform_broadcast",
                Title = title.Trim(),
                Body = body.Trim(),
                ReferenceId = adminId
            });
        }

        _db.CafeNotifications.AddRange(notifications);
        await _db.SaveChangesAsync(ct);

        foreach (var n in notifications)
        {
            var dto = new CafeNotificationDto(
                n.Id,
                n.Type,
                n.Title,
                n.Body,
                n.ReferenceId,
                n.TableNumber,
                n.IsRead,
                n.CreatedAt);

            await _hub.Clients.Group(KdsHub.GroupName(n.CafeId))
                .SendAsync("NotificationReceived", dto, ct);
        }

        return new BroadcastNotificationResult(cafes.Count, notifications.Count);
    }

    public async Task<bool> DeleteAsync(string notificationId, CancellationToken ct = default)
    {
        var row = await _db.CafeNotifications
            .IgnoreQueryFilters()
            .FirstOrDefaultAsync(n => n.Id == notificationId, ct);

        if (row is null || row.DeletedAt is not null)
            return false;

        row.DeletedAt = DateTime.UtcNow;
        await _db.SaveChangesAsync(ct);
        return true;
    }
}
