using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;

namespace AsbCloudApp.Services.Notifications;

/// <summary>
/// Сервис для работы с уведомлениями
/// </summary>
public class NotificationService
{
    private readonly ICrudRepository<NotificationCategoryDto> notificationCategoryRepository;
    private readonly INotificationRepository notificationRepository;
    private readonly IEnumerable<INotificationTransportService> notificationTransportServices;

    /// <summary>
    /// Сервис для работы с уведомлениями
    /// </summary>
    /// <param name="notificationCategoryRepository"></param>
    /// <param name="notificationRepository"></param>
    /// <param name="notificationTransportServices"></param>
    public NotificationService(ICrudRepository<NotificationCategoryDto> notificationCategoryRepository,
        INotificationRepository notificationRepository,
        IEnumerable<INotificationTransportService> notificationTransportServices)
    {
        this.notificationCategoryRepository = notificationCategoryRepository;
        this.notificationRepository = notificationRepository;
        this.notificationTransportServices = notificationTransportServices;
    }
    
    /// <summary>
    /// Отправка нового уведомления
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    public async Task NotifyAsync(NotifyRequest request,
        CancellationToken cancellationToken)
    {
        var notificationCategory = await notificationCategoryRepository.GetOrDefaultAsync(request.IdNotificationCategory, cancellationToken)
            ?? throw new ArgumentInvalidException(nameof(request.IdNotificationCategory), "Категория уведомления не найдена");

        var notification = new NotificationDto
        {
            IdUser = request.IdUser,
            RegistrationDate = DateTimeOffset.UtcNow,
            IdNotificationCategory = notificationCategory.Id,
            Title = request.Title,
            Message = request.Message,
            IdTransportType = request.IdTransportType,
        };

        notification.Id = await notificationRepository.InsertAsync(notification, cancellationToken);
        notification.NotificationCategory = notificationCategory;

        var notificationTransportService = GetTransportService(request.IdTransportType);

        //todo Добавить задачу в WorkToSendEmail
        try
        {
            await notificationTransportService.SendAsync(notification, cancellationToken);
        }
        catch (SmtpException ex)
        {
            Console.WriteLine(ex.Message);
        }

        notification.SentDate = DateTimeOffset.UtcNow;
        await notificationRepository.UpdateAsync(notification, cancellationToken);
    }

    /// <summary>
    /// Обновление уведомления
    /// </summary>
    /// <param name="idNotification"></param>
    /// <param name="isRead"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task UpdateAsync(int idNotification,
        bool isRead,
        CancellationToken cancellationToken)
    {
        var notification = await notificationRepository.GetOrDefaultAsync(idNotification, cancellationToken)
            ?? throw new ArgumentInvalidException(nameof(idNotification), "Уведомление не найдено");

        if(isRead && !notification.SentDate.HasValue)
            throw new ArgumentInvalidException(nameof(isRead), "Уведомление не может быть прочитано");
        
        notification.ReadDate = isRead ? DateTimeOffset.UtcNow : null;

        await notificationRepository.UpdateAsync(notification,
            cancellationToken);
    }

    /// <summary>
    /// Отправка уведомлений, которые не были отправлены
    /// </summary>
    /// <param name="idUser"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task RenotifyAsync(int idUser, CancellationToken cancellationToken)
    {
        const int idTransportType = 0;
        
        var notifications = await notificationRepository.GetAllAsync(idUser, false, 
            idTransportType,
            cancellationToken);

        var notificationTransportService = GetTransportService(idTransportType);

        await notificationTransportService.SendRangeAsync(notifications,
            cancellationToken);

        var tasks = notifications.Select(notification =>
        {
            notification.SentDate = DateTimeOffset.UtcNow;
            return notificationRepository.UpdateAsync(notification, cancellationToken);
        });

        await Task.WhenAll(tasks);
    }

    private INotificationTransportService GetTransportService(int idTransportType)
    {
        var notificationTransportService = notificationTransportServices
            .FirstOrDefault(s => s.IdTransportType == idTransportType)
            ?? throw new ArgumentInvalidException(nameof(idTransportType), "Доставщик уведомлений не найден");

        return notificationTransportService;
    }
}