using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Contracts.Helper;
using FoodsharingSiegen.Server.Data;
using FoodsharingSiegen.Server.Service;
using FoodsharingSiegen.Shared.Helper;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore;

namespace FoodsharingSiegen.Server.Auth
{
    /// <summary>
    /// The auth service class (a. beging, 04.04.2022)
    /// </summary>
    public class AuthService
    {
        #region Public Properties

        /// <summary>
        /// Gets or sets the value of the user (ab)
        /// </summary>
        public User? User => _user;

        #endregion

        #region Private Properties

        /// <summary>
        /// Gets the value of the context (ab)
        /// </summary>
        private FsContext Context { get; }

        #endregion

        #region Private Fields

        /// <summary>
        /// The authentication state provider
        /// </summary>
        private readonly AuthenticationStateProvider _authenticationStateProvider;

        /// <summary>
        /// The local storage service
        /// </summary>
        private readonly LocalStorageService _localStorageService;

        /// <summary>
        /// The user
        /// </summary>
        private User? _user;

        #endregion

        #region Setup/Teardown

        /// <summary>
        /// Initializes a new instance of the <see cref="AuthService"/> class
        /// </summary>
        /// <param name="context">The context</param>
        /// <param name="localStorageService">The local storage service</param>
        /// <param name="authenticationStateProvider">The authentication state provider</param>
        public AuthService(
            FsContext context,
            LocalStorageService localStorageService,
            AuthenticationStateProvider authenticationStateProvider)
        {
            Context = context;
            _localStorageService = localStorageService;
            _authenticationStateProvider = authenticationStateProvider;
        }

        #endregion

        #region Public Method Initialize

        /// <summary>
        /// Initializes this instance (a. beging, 11.04.2022)
        /// </summary>
        public async Task Initialize()
        {
            if (_user != null) return;
            
            var token = await _localStorageService.GetItem<string>(StorageKeys.TokenKey);
            if (AuthHelper.ValidateToken(token, out var user) && user != null)
                _user = user;
        }

        #endregion

        #region Public Method Login

        /// <summary>
        /// Logins the mail address (a. beging, 04.04.2022)
        /// </summary>
        /// <param name="mailAddress">The mail address</param>
        /// <param name="password">The password</param>
        /// <returns>A task containing the operation result</returns>
        public async Task<OperationResult> Login(string mailAddress, string password)
        {
            #region Ensure Admin

            var existingTroogS = await Context.Users!.AnyAsync(x => x.Mail == "fs@beging.de");
            if (!existingTroogS)
            {
                var troogs = new User
                {
                    Name = "Andre",
                    Mail = "fs@beging.de",
                    GroupsList = new List<UserGroup> { UserGroup.Ambassador },
                    Type = UserType.Admin,
                    Created = DateTime.UtcNow,
                    EncryptedPassword = "qSIxTZo7J8M="
                };

                await Context.Users!.AddAsync(troogs);
                await Context.SaveChangesAsync();
            }

            #endregion Ensure Admin
            
            var encryptedPassword = Cryptor.Encrypt(password);

            _user = await Context.Users!.FirstOrDefaultAsync(x => x.Mail.ToLower() == mailAddress.ToLower() && x.EncryptedPassword == encryptedPassword);
            
            if (_user != null)
            {
                var serializedToken = AuthHelper.CreateToken(_user);
                await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken);
                
                if (_user.ForceLogout)
                {
                    _user.ForceLogout = false;
                    await Context.SaveChangesAsync();
                }
                
                Context.Entry(_user).State = EntityState.Detached;
                
                return new OperationResult();
            }

            return new OperationResult(new Exception("Benutzername oder Passwort falsch"));
        }

        #endregion

        #region Public Method Logout

        /// <summary>
        /// Logouts this instance (a. beging, 04.04.2022)
        /// </summary>
        /// <returns>A task containing the operation result</returns>
        public async Task<OperationResult> Logout()
        {
            try
            {
                await _localStorageService.RemoveItem(StorageKeys.TokenKey);
                _user = null;
                ((TokenAuthStateProvider) _authenticationStateProvider).MarkUserAsLoggedOut();
                return new OperationResult();
            }
            catch (Exception e)
            {
                return new OperationResult(e);
            }
        }

        #endregion

        #region Public Method RefreshState

        /// <summary>
        /// Refreshes the state (a. beging, 21.05.2022)
        /// </summary>
        public async Task RefreshState()
        {
            if (_user == null) return;

            _user = await Context.Users?.FirstOrDefaultAsync(x => x.Id == _user.Id)!;

            if (_user != null)
            {
                var serializedToken = AuthHelper.CreateToken(_user);
                await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken);
            }
        }

        #endregion
    }
}