using Microsoft.EntityFrameworkCore;
using ROLAC.API.Data;
using ROLAC.API.Entities.Notifications;
using ROLAC.API.Services.Logging;

namespace ROLAC.API.Services.Notifications;

/// <summary>
/// Resolves recipients (member emails + raw addresses, deduped), sends each via the SMTP
/// dispatcher, and writes a NotificationLog row per recipient. A single failure never aborts the
/// batch — it is recorded and reported in the summary.
/// </summary>
public sealed class EmailService : IEmailService
{
    private readonly AppDbContext _db;
    private readonly ISmtpDispatcher _dispatcher;
    private readonly CurrentUserAccessor _currentUser;

    public EmailService(AppDbContext db, ISmtpDispatcher dispatcher, CurrentUserAccessor currentUser)
    {
        _db = db;
        _dispatcher = dispatcher;
        _currentUser = currentUser;
    }

    public async Task<NotificationResult> SendAsync(EmailMessage message, CancellationToken ct = default)
    {
        var recipients = await ResolveRecipientsAsync(message, ct);
        if (recipients.Count == 0) return NotificationResult.Empty;

        var sentBy = message.SentByUserId ?? _currentUser.UserIdOrSystem;
        var attachments = message.Attachments ?? Array.Empty<EmailAttachment>();
        var failures = new List<NotificationFailure>();
        var sentCount = 0;

        foreach (var recipient in recipients)
        {
            var log = new NotificationLog
            {
                Channel          = NotificationChannels.Email,
                TargetType       = NotificationTargetTypes.Email,
                TargetExternalId = recipient.Address,
                Subject          = message.Subject,
                MemberId         = recipient.MemberId,
                Body             = NotificationLogText.Truncate(message.HtmlBody),
                SentByUserId     = sentBy,
                SentAt           = DateTime.UtcNow,
            };

            try
            {
                await _dispatcher.SendAsync(
                    new OutboundEmail(recipient.Address, message.Subject, message.HtmlBody, attachments), ct);
                log.Status = NotificationStatuses.Sent;
                sentCount++;
            }
            catch (Exception ex)
            {
                log.Status = NotificationStatuses.Failed;
                log.Error  = ex.Message;
                failures.Add(new NotificationFailure(recipient.Address, ex.Message));
            }

            _db.NotificationLogs.Add(log);
        }

        await _db.SaveChangesAsync(ct);
        return new NotificationResult(sentCount, failures.Count, failures);
    }

    private async Task<IReadOnlyList<(string Address, int? MemberId)>> ResolveRecipientsAsync(
        EmailMessage message, CancellationToken ct)
    {
        var resolved = new List<(string Address, int? MemberId)>();
        var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        if (message.MemberIds.Count > 0)
        {
            var members = await _db.Members
                .Where(member => message.MemberIds.Contains(member.Id) && member.Email != null && member.Email != "")
                .Select(member => new { member.Id, member.Email })
                .ToListAsync(ct);
            foreach (var member in members)
                if (seen.Add(member.Email!))
                    resolved.Add((member.Email!, member.Id));
        }

        foreach (var address in message.Addresses)
        {
            var trimmed = address?.Trim();
            if (!string.IsNullOrWhiteSpace(trimmed) && seen.Add(trimmed))
                resolved.Add((trimmed, null));
        }

        return resolved;
    }

}
