using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MTWorkHR.Core.Global;
using MTWorkHR.Infrastructure.DBContext;


namespace MTWorkHR.Infrastructure.Entities
{
    public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        private UserStore<ApplicationUser, ApplicationRole, HRDataContext, string, IdentityUserClaim<string>
            , IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>
            , IdentityRoleClaim<string>>
            _store;
        private readonly GlobalInfo _globalInfo;
        public ApplicationUserManager(IUserStore<ApplicationUser> store, GlobalInfo globalInfo, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators, IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<ApplicationUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
        {
            _globalInfo = globalInfo;
        }
        private HRDataContext GetContext()
        {
            _store = (UserStore<ApplicationUser, ApplicationRole, HRDataContext, string, IdentityUserClaim<string>,
                    IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>)this.Store;

            var context = _store.Context;
            return context;
        }

        public async Task<bool> UserHasAccess(string userId, string permmisions)
        {
            var context = GetContext();

            //check if curr user has admin role
            bool isAdminUser = await IsAdminAsync(userId);

            if (isAdminUser)
                return true;

            var permissionsArr = permmisions.Split(",").ToArray();
            var count = await context.UserRoles
                .Join(context.RolePermissions,
                    userRole => userRole.RoleId,
                    rolePermission => rolePermission.RoleId,
                    (userRole, rolePermission) => new { userRole, rolePermission })
                .Join(context.Permissions,
                    rp => rp.rolePermission.PermissionId,
                    permission => permission.Id,
                    (rp, permission) => new { rp, permission })
                .Where(x => permissionsArr.Any(a => a == x.permission.Name) && x.rp.userRole.UserId == userId)
                .CountAsync();

            return count > 0;
        }

        public async Task<string[]> GetUserPermission(string userId)
        {
            var context = GetContext();
            string[] permissions = Array.Empty<string>();

            //check if curr user has admin role
            bool isAdminUser = await IsAdminAsync(userId);

            //if curr user has admin role, return all available permissions for curr tenant
            if (isAdminUser)
            {
                permissions = context.Permissions
                    .Select(xx => xx.Name).ToArray();
            }
            //if not admin then return only assigned permissions
            else
            {
                permissions = await context.UserRoles
                .Join(context.RolePermissions,
                    userRole => userRole.RoleId,
                    rolePermission => rolePermission.RoleId,
                    (userRole, rolePermission) => new { userRole, rolePermission })
                .Join(context.Permissions,
                    rp => rp.rolePermission.PermissionId,
                    permission => permission.Id,
                    (rp, permission) => new { rp, permission })
                .Where(x => x.rp.userRole.UserId == userId)
                .Select(x => x.permission.Name)
                .AsNoTracking().ToArrayAsync();
            }
            return permissions;
        }

        private async Task<bool> IsAdminAsync(string userId)
        {
            var context = GetContext();
            return await context.Roles
                    .Join(context.UserRoles,
                        role => role.Id,
                        userRole => userRole.RoleId,
                        (role, userRole) => new { role, userRole })
                    .Where(x => x.userRole.UserId == userId && x.role.IsAdmin == true)
                    .CountAsync() > 0;
        }

        public async Task<ApplicationUser> FindByAnyAsync(string name)
        {
            var context = GetContext();

            var res = await context.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == name.ToUpper() || x.NormalizedEmail == name.ToUpper() || x.PhoneNumber.ToUpper() == name.ToUpper());
            return res;
        }

        public async Task<bool> IsStopped(string userId)
        {
            var context = GetContext();
            var user = await context.Users
                .FirstOrDefaultAsync(x => x.Id == userId);
            return user.IsStopped;
        }
    }
}