using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using AppKit.Core.Ef6MssqlImpl.Security.Extensions;
using AppKit.Core.Ef6MssqlImpl.Security.Models;
using AppKit.Core.Security.FeatureBasedSecurity.DataAccess;
using AppKit.Core.Security.FeatureBasedSecurity.Models;

namespace AppKit.Core.Ef6MssqlImpl.Security.FeatureBasedSecurity
{
    public class UserQuery : IUserQuery
    {
        public List<User> GetAllUsers()
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                List<User> users = context.Users
                    .AsNoTracking()
                    .Map().ToList();

                return users;
            }
        }

        public List<User> GetAllUsersAndRoles()
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                List<Role> roles = context.Roles
                    .AsNoTracking()
                    .Map().ToList();

                List<User> users = context.Users
                    .AsNoTracking()
                    .Map().ToList();

                List<UserRoleDb> userRoleDbs = context.UserRoles.AsNoTracking().ToList();

                foreach (UserRoleDb userRoleDb in userRoleDbs)
                {
                    Role role = roles.First(r => r.Id == userRoleDb.RoleId);
                    User user = users.First(u => u.Id == userRoleDb.UserId);
                    role.Users.Add(user);
                    user.Roles.Add(role);
                }

                return users;
            }
        }


        public User GetUserByUserName(string userName)
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                User user = context.Users
                    .AsNoTracking()
                    .Where(u => u.UserName == userName)
                    .Map().FirstOrDefault();

                return user;
            }    
        }

        public User GetUserByUserId(Guid id)
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                User user = context.Users
                    .AsNoTracking()
                    .Where(u => u.Id == id)
                    .Map().FirstOrDefault();

                return user;
            }
        }



        public User GetRolesByUserName(string userName)
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                User user = context.Users
                    .AsNoTracking()
                    .Where(u => u.UserName == userName)
                    .Map().FirstOrDefault();
                if (user == null)
                    return null;

                return GetRolesOfUser(context, user);
            }
        }

        public User GetRolesByUserId(Guid userId)
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                User user = context.Users
                    .AsNoTracking()
                    .Where(u => u.Id == userId)
                    .Map().FirstOrDefault();
                if (user == null)
                    return null;

                return GetRolesOfUser(context, user);
            }
        }

        private static User GetRolesOfUser(SecurityDbContext context, User user)
        {
            List<Role> roles = context.Roles
                .AsNoTracking()
                .Where(r => r.UserRoles.Any(ur => ur.UserId == user.Id))
                .Map()
                .ToList();

            user.Roles.AddRange(roles);

            return user;
        }


        public User GetRolesAndFeaturesByUserName(string userName)
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                User user = context.Users
                    .AsNoTracking()
                    .Where(u => u.UserName == userName)
                    .Map().FirstOrDefault();
                if (user == null)
                    return null;

                return GetRolesAndFeaturesOfUser(context, user);
            }
        }

        public User GetRolesAndFeaturesByUserId(Guid userId)
        {
            using (SecurityDbContext context = SecurityDbContextFactory.CreateContext())
            {
                User user = context.Users
                    .AsNoTracking()
                    .Where(u => u.Id == userId)
                    .Map().FirstOrDefault();
                if (user == null)
                    return null;

                return GetRolesAndFeaturesOfUser(context, user);
            }
        }

        private static User GetRolesAndFeaturesOfUser(SecurityDbContext context, User user)
        {
            List<RoleDb> roleDbs = context.Roles
                .AsNoTracking()
                .Include(r => r.FeatureRoles)
                .Where(r => r.UserRoles.Any(ur => ur.UserId == user.Id))
                .ToList();

            List<FeatureRoleDb> featureRoleDbs = roleDbs.SelectMany(r => r.FeatureRoles).ToList();

            List<Role> roles = roleDbs.Select(r => r.Map()).ToList();

            List<Feature> features = context.Features
                .AsNoTracking()
                .Where(f => f.FeatureRoles.Any(fr => fr.Role.UserRoles.Any(ur => ur.UserId == user.Id)))
                .Map().ToList();

            foreach (FeatureRoleDb featureRoleDb in featureRoleDbs)
            {
                Role role = roles.First(r => r.Id == featureRoleDb.RoleId);
                Feature feature = features.First(f => f.Id == featureRoleDb.FeatureId);
                role.Features.Add(feature);
                feature.Roles.Add(role);
            }

            user.Roles.AddRange(roles);

            return user;
        }
    }
}
