using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Contracts.Enums;
using FoodsharingSiegen.Server.Auth;
using Microsoft.EntityFrameworkCore;

namespace FoodsharingSiegen.Server.Data.Service
{
    /// <summary>
    ///     The user service class (a. beging, 11.04.2022)
    /// </summary>
    /// <seealso cref="ServiceBase" />
    public class UserService : ServiceBase
    {
        #region Private Properties

        /// <summary>
        ///     Gets the value of the audit service (ab)
        /// </summary>
        private AuditService AuditService { get; }

        #endregion

        #region Setup/Teardown

        /// <summary>
        ///     Initializes a new instance of the <see cref="UserService" /> class
        /// </summary>
        /// <param name="context">The context</param>
        /// <param name="authService"></param>
        /// <param name="auditService"></param>
        public UserService(FsContext context, AuthService authService, AuditService auditService) : base(context, authService) => AuditService = auditService;

        #endregion

        #region Public Method AddUserAsync

        /// <summary>
        ///     Adds the user using the specified user (a. beging, 11.04.2022)
        /// </summary>
        /// <param name="user">The user</param>
        /// <returns>A task containing an operation result of user</returns>
        public async Task<OperationResult<User>> AddUserAsync(User user)
        {
            try
            {
                if (await Context.Users!.AnyAsync(x => x.Mail.ToLower().Equals(user.Mail.ToLower())))
                    return new(new Exception("Diese E-Mail Adresse wird bereits verwendet"));

                user.Created = DateTime.UtcNow;

                if (string.IsNullOrWhiteSpace(user.Password))
                    user.Password = string.Empty;

                await Context.Users!.AddAsync(user);

                var saveResult = await Context.SaveChangesAsync();
                if (saveResult == 0) return new(new Exception("Fehler bei der Registrierung"));

                await AuditService.Insert(AuditType.CreateUser, user.Mail);

                return new(user);
            }
            catch (Exception e)
            {
                return new(e);
            }
        }

        #endregion

        #region Public Method GetUsersAsync

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Gets users asynchronous. </summary>
        /// <remarks>   A Beging, 20.10.2021. </remarks>
        /// <returns>   An asynchronous result that yields the users. </returns>
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        public async Task<OperationResult<List<User>>> GetUsersAsync()
        {
            try
            {
                var users = await Context.Users!.AsNoTracking().ToListAsync();
                return new(users);
            }
            catch (Exception e)
            {
                return new(e);
            }
        }

        #endregion

        #region Public Method Remove

        /// <summary>
        ///     Removes the user id (a. beging, 08.02.2023)
        /// </summary>
        /// <param name="userId">The user id</param>
        /// <returns>A task containing the operation result</returns>
        public async Task<OperationResult> RemoveAsync(Guid userId)
        {
            try
            {
                var user = await Context.Users!.Include(x => x.Interactions).FirstOrDefaultAsync(x => x.Id == userId);
                if (user == null) return new(new Exception("User not found"));

                if (user.Type == UserType.Admin)
                {
                    var adminCount = await Context.Users!.CountAsync(x => x.Type == UserType.Admin && x.Id != userId);
                    if (adminCount == 0)
                        return new(new Exception("Der letzte Administrator kann nicht gelöscht werden."));
                }

                // Interaktionen vom aktuellen Nutzer übernehmen
                if(CurrentUser?.Id != null)
                    foreach (var userInteraction in user.Interactions)
                    {
                        userInteraction.UserID = CurrentUser.Id;
                    }
                
                // Audits des users löschen
                var audits = Context.Audits!.Where(x => x.UserID == userId);
                Context.Audits?.RemoveRange(audits);
                
                Context.Users?.Remove(user);
                var saveR = await Context.SaveChangesAsync();

                if (saveR < 1) return new(new Exception("Fehler beim Löschen"));
                await AuditService.Insert(AuditType.RemoveUser, user.Mail);
                return new();
            }
            catch (Exception e)
            {
                return new(e);
            }
        }

        #endregion

        #region Public Method SetPassword

        /// <summary>
        ///     Sets the password using the specified user (a. beging, 20.05.2022)
        /// </summary>
        /// <param name="user">The user</param>
        /// <returns>A task containing the operation result</returns>
        public async Task<OperationResult> SetPassword(User user)
        {
            try
            {
                var entityUser = await Context.Users!.FirstOrDefaultAsync(x => x.Id == user.Id);
                if (entityUser == null) return new(new Exception("User not found"));

                entityUser.Password = user.Password;

                var saveR = await Context.SaveChangesAsync();

                if (saveR < 1) return new(new Exception("Fehler beim Speichern"));

                if (CurrentUser?.Id == user.Id)
                {
                    await AuditService.Insert(AuditType.ChangeOwnPassword);
                }
                else
                {
                    await AuditService.Insert(AuditType.SetUserPassword, user.Mail);
                }

                return new();
            }
            catch (Exception e)
            {
                return new(e);
            }
        }

        #endregion

        #region Public Method Update

        /// <summary>
        ///     Updates the user (a. beging, 11.04.2022)
        /// </summary>
        /// <param name="user">The user</param>
        /// <returns>A task containing the operation result</returns>
        public async Task<OperationResult> Update(User user)
        {
            try
            {
                var entityUser = await Context.Users!.FirstOrDefaultAsync(x => x.Id == user.Id);
                if (entityUser == null) return new(new Exception("User not found"));

                if (entityUser.Type == UserType.Admin && user.Type != UserType.Admin)
                {
                    var adminCount = await Context.Users!.CountAsync(x => x.Type == UserType.Admin && x.Id != user.Id);
                    if (adminCount == 0)
                        return new(new Exception("Der Typ des letzten Administrators kann nicht geändert werden."));
                }

                if (entityUser.Mail != user.Mail ||
                    entityUser.Type != user.Type ||
                    entityUser.Groups != user.Groups)
                    entityUser.ForceLogout = true;

                entityUser.Memo = user.Memo;
                entityUser.Mail = user.Mail;
                entityUser.Name = user.Name;
                entityUser.Type = user.Type;
                entityUser.Groups = user.Groups;
                entityUser.Network = user.Network;

                if (!Context.HasChanges())
                    return new(new Exception("Nichts zum Speichern gefunden"));

                var saveR = await Context.SaveChangesAsync();

                if (saveR < 1) return new(new Exception("Fehler beim Speichern"));
                await AuditService.Insert(AuditType.UpdateUser, user.Mail);
                return new();
            }
            catch (Exception e)
            {
                return new(e);
            }
        }

        #endregion
    }
}