using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using PowderCoating.Application.Interfaces;
using PowderCoating.Core.Entities;
using PowderCoating.Infrastructure.Data;
using PowderCoating.Web.Hubs;

namespace PowderCoating.Web.Services;

/// <summary>
/// Creates and delivers in-app notifications: persists a record to <c>InAppNotifications</c> and
/// then pushes a real-time update to connected clients via SignalR. Persistence happens first so
/// that the notification is available in the bell history even if the SignalR push fails (e.g.,
/// the recipient has no active browser session). SignalR failures are caught and logged at Warning
/// level so that a transient hub error never rolls back the database write.
/// </summary>
public class InAppNotificationService : IInAppNotificationService
{
    private readonly ApplicationDbContext _db;
    private readonly IHubContext<NotificationHub> _hub;
    private readonly ILogger<InAppNotificationService> _logger;

    /// <summary>
    /// Initializes the service with direct DbContext access, the SignalR hub context, and a logger.
    /// DbContext is injected directly (rather than through IUnitOfWork) because notification
    /// creation must commit immediately and independently of any surrounding business transaction.
    /// </summary>
    public InAppNotificationService(
        ApplicationDbContext db,
        IHubContext<NotificationHub> hub,
        ILogger<InAppNotificationService> logger)
    {
        _db = db;
        _hub = hub;
        _logger = logger;
    }

    /// <summary>
    /// Persists an in-app notification for a specific tenant company and broadcasts it in real time
    /// to all users in that company's SignalR group ("company-{companyId}"). Optional FK parameters
    /// (<paramref name="quoteId"/>, <paramref name="invoiceId"/>, <paramref name="customerId"/>)
    /// allow the bell history page to deep-link directly to the related record.
    /// </summary>
    public async Task CreateAsync(int companyId, string title, string message, string notificationType,
        string? link = null, int? quoteId = null, int? invoiceId = null, int? customerId = null)
    {
        var now = DateTime.UtcNow;
        var notification = new InAppNotification
        {
            CompanyId = companyId,
            Title = title,
            Message = message,
            NotificationType = notificationType,
            Link = link,
            QuoteId = quoteId,
            InvoiceId = invoiceId,
            CustomerId = customerId,
            IsRead = false,
            CreatedAt = now,
            UpdatedAt = now
        };

        _db.InAppNotifications.Add(notification);
        await _db.SaveChangesAsync();

        try
        {
            await _hub.Clients.Group($"company-{companyId}").SendAsync("NewInAppNotification", new
            {
                id = notification.Id,
                title = notification.Title,
                message = notification.Message,
                link = notification.Link,
                notificationType = notification.NotificationType,
                customerId = notification.CustomerId,
                createdAt = now.ToString("o")
            });
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to push in-app notification SignalR for company {CompanyId}", companyId);
        }
    }

    /// <summary>
    /// Broadcasts a notification to every active tenant company — one <c>InAppNotification</c> row
    /// per company so it appears in the bell for all users of that company. Used for platform-wide
    /// announcements such as new release notes. Skips the demo company (ID&nbsp;1) since that is the
    /// SuperAdmin home company, not a real tenant. SignalR pushes are attempted for each company
    /// group; individual push failures are logged at Warning but do not stop the remaining companies.
    /// </summary>
    public async Task CreateForAllCompaniesAsync(string title, string message, string notificationType, string? link = null)
    {
        var companyIds = await _db.Companies
            .IgnoreQueryFilters()
            .Where(c => !c.IsDeleted && c.Id != 1)
            .Select(c => c.Id)
            .ToListAsync();

        var now = DateTime.UtcNow;
        var notifications = companyIds.Select(cid => new InAppNotification
        {
            CompanyId = cid,
            Title = title,
            Message = message,
            NotificationType = notificationType,
            Link = link,
            IsRead = false,
            CreatedAt = now,
            UpdatedAt = now
        }).ToList();

        _db.InAppNotifications.AddRange(notifications);
        await _db.SaveChangesAsync();

        foreach (var notification in notifications)
        {
            try
            {
                await _hub.Clients.Group($"company-{notification.CompanyId}").SendAsync("NewInAppNotification", new
                {
                    id = notification.Id,
                    title = notification.Title,
                    message = notification.Message,
                    link = notification.Link,
                    notificationType = notification.NotificationType,
                    createdAt = now.ToString("o")
                });
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to push release note SignalR notification for company {CompanyId}", notification.CompanyId);
            }
        }
    }

    /// <summary>
    /// Persists a platform-level notification and pushes it to all connected SuperAdmin users via
    /// the "superadmin" SignalR group. Uses <c>CompanyId = 0</c> as a platform-level sentinel value
    /// (no tenant owns CompanyId 0) so that SuperAdmin notifications are stored in the same table
    /// without a tenant FK and are excluded from per-company global query filters.
    /// </summary>
    public async Task CreateForSuperAdminsAsync(string title, string message, string notificationType, string? link = null)
    {
        var now = DateTime.UtcNow;
        // CompanyId = 0 is the platform-level convention — no tenant owns it
        var notification = new InAppNotification
        {
            CompanyId = 0,
            Title = title,
            Message = message,
            NotificationType = notificationType,
            Link = link,
            IsRead = false,
            CreatedAt = now,
            UpdatedAt = now
        };

        _db.InAppNotifications.Add(notification);
        await _db.SaveChangesAsync();

        try
        {
            await _hub.Clients.Group("superadmin").SendAsync("NewInAppNotification", new
            {
                id = notification.Id,
                title = notification.Title,
                message = notification.Message,
                link = notification.Link,
                notificationType = notification.NotificationType,
                createdAt = now.ToString("o")
            });
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to push SuperAdmin in-app notification via SignalR");
        }
    }
}
