using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WorkFlowCheck.Common.DTO;
using WorkFlowCheck.BL.Services.Interfaces;
using WorkFlowCheck.DL;
using WorkFlowCheck.Common.Security;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using WorkFlowCheck.DL.Entities;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Identity;

namespace WorkFlowCheck.BL.Services
{
    public class UserService : IUserService
    {
        private AppDbContext _dbContext;
        private IMapper _mapper;
        private IConfiguration _configuration;

        public UserService(AppDbContext dbContext, IMapper mapper, IConfiguration configuration)
        {
            _dbContext = dbContext;
            _mapper = mapper;
            _configuration = configuration;
        }

        public async Task<UserDTO> GetUserAsync(int Id)
        {
            var retVal = new UserDTO() { RoleDTO = new List<RoleDTO>() };

            try
            {
                var user = await _dbContext.Users
                    .Include(i => i.UserRoles)
                    .ThenInclude(i => i.Role)
                    .Where(w => w.Id == Id).FirstOrDefaultAsync();
                if (user != null)
                {
                    retVal = _mapper.Map<UserDTO>(user);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<List<UserDTO>> GetAllUserAsync()
        {
            var retVal = new List<UserDTO>
                {
                    new UserDTO
                    {
                        RoleDTO = new List<RoleDTO>()
                    }
                };
            try
            {
                var users = await _dbContext.Users.ToListAsync();
                if (users != null)
                {
                    retVal = _mapper.Map<List<UserDTO>>(users);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<UserDTO> Authenticate(string userName, string password)
        {
            var retVal = new UserDTO() { RoleDTO = new List<RoleDTO>() };

            try
            {
                var user = await _dbContext.Users
                    .Include(i => i.UserRoles)
                    .ThenInclude(i => i.Role)
                    .Where(w => w.UserName == userName)
                    .FirstOrDefaultAsync();
                if (user != null && PasswordHasher.VerifyPassword(user.PasswordHash, password))
                {

                    var userDTO = new UserDTO()
                    {
                        Id = user.Id,
                        UserName = user.UserName,
                        RoleDTO = new List<RoleDTO>(),
                    };
                    foreach (var item in user.UserRoles)
                    {
                        userDTO.RoleDTO.Add(new RoleDTO()
                        {
                            Id = item.Role.Id,
                            RoleName = item.Role.RoleName,
                        });
                    }
                    user.JwtToken = GenerateJwtToken(userDTO);
                    await _dbContext.SaveChangesAsync();

                    retVal = _mapper.Map<UserDTO>(user);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public string GenerateJwtToken(UserDTO userDTO)
        {
            var jwtSettings = _configuration.GetSection("Jwt");
            var secretKey = jwtSettings["SecretKey"];
            var issuer = jwtSettings["Issuer"];
            var audience = jwtSettings["Audience"];
            var tokenLifetime = int.Parse(jwtSettings["TokenLifetimeMinutes"]);

            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, userDTO.UserName),
                new Claim(ClaimTypes.NameIdentifier, userDTO.Id.ToString())
            };

            foreach (var role in userDTO.RoleDTO)
            {
                claims.Add(new Claim(ClaimTypes.Role, role.RoleName));
            }

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                issuer: issuer,
                audience: audience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(tokenLifetime),
                signingCredentials: creds);

            return new JwtSecurityTokenHandler().WriteToken(token);
        }
        public async Task<UserDTO> UpdateUserAsync(UserDTO userDTO)
        {
            var retVal = new UserDTO() { RoleDTO = new List<RoleDTO>() };
            try
            {
                if (userDTO.Id == 0)
                {
                    var user = new User();
                    user.UserName = userDTO.UserName;
                    user.FirstName = userDTO.FirstName;
                    user.LastName = userDTO.LastName;
                    user.Email = userDTO.Email;
                    user.JwtToken = "";

                    user.PasswordHash = PasswordHasher.HashPassword(userDTO.Password);


                    _dbContext.Users.Add(user);
                    await _dbContext.SaveChangesAsync();

                    retVal = _mapper.Map<UserDTO>(user);
                }
                else
                {
                    var user = await _dbContext.Users.Where(w => w.Id == userDTO.Id).FirstOrDefaultAsync();

                    if (user != null)
                    {
                        user.UserName = userDTO.UserName;
                        user.FirstName = userDTO.FirstName;
                        user.LastName = userDTO.LastName;
                        user.Email = userDTO.Email;
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<UserDTO>(user);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }

            return retVal;
        }

        public async Task<RoleDTO> GetRoleAsync(int Id)
        {
            var retVal = new RoleDTO();

            try
            {
                var user = await _dbContext.Roles.Where(w => w.Id == Id).FirstOrDefaultAsync();
                if (user != null)
                {
                    retVal = _mapper.Map<RoleDTO>(user);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<List<RoleDTO>> GetAllRoleAsync()
        {
            var retVal = new List<RoleDTO>();
            try
            {
                var roles = await _dbContext.Roles.ToListAsync();
                if (roles != null)
                {
                    retVal = _mapper.Map<List<RoleDTO>>(roles);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<RoleDTO> UpdateRoleAsync(RoleDTO roleDTO)
        {
            var retVal = new RoleDTO();
            try
            {
                if (roleDTO.Id == 0)
                {
                    var role = new Role();
                    role.RoleName = roleDTO.RoleName;

                    _dbContext.Roles.Add(role);
                    await _dbContext.SaveChangesAsync();

                    retVal = _mapper.Map<RoleDTO>(role);
                }
                else
                {
                    var role = await _dbContext.Roles.Where(w => w.Id == roleDTO.Id).FirstOrDefaultAsync();

                    if (role != null)
                    {
                        role.RoleName += roleDTO.RoleName;
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<RoleDTO>(role);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }

            return retVal;
        }

        public async Task<UserRoleDTO> GetUserRoleAsync(int Id)
        {
            var retVal = new UserRoleDTO();

            try
            {
                var userRole = await _dbContext.UserRoles.Where(w => w.Id == Id).FirstOrDefaultAsync();
                if (userRole != null)
                {
                    retVal = _mapper.Map<UserRoleDTO>(userRole);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<List<UserRoleDTO>> GetAllUserRoleAsync()
        {
            var retVal = new List<UserRoleDTO>();
            try
            {
                var userRoles = await _dbContext.UserRoles
                    .Include(i => i.User)
                    .Include(i => i.Role)
                    .AsNoTracking()
                    .ToListAsync();
                if (userRoles != null)
                {
                    retVal = _mapper.Map<List<UserRoleDTO>>(userRoles);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<UserRoleDTO> UpdateUserRoleAsync(UserRoleDTO userRoleDTO)
        {
            var retVal = new UserRoleDTO();
            try
            {
                if (userRoleDTO.Id == 0)
                {
                    var userRole = new UserRole();
                    userRole.UserId = userRoleDTO.UserId;
                    userRole.RoleId = userRoleDTO.RoleId;

                    _dbContext.UserRoles.Add(userRole);
                    await _dbContext.SaveChangesAsync();

                    retVal = _mapper.Map<UserRoleDTO>(userRole);
                }
                else
                {
                    var userRole = await _dbContext.UserRoles.Where(w => w.Id == userRoleDTO.Id).FirstOrDefaultAsync();

                    if (userRole != null)
                    {
                        userRole.UserId = userRoleDTO.UserId;
                        userRole.RoleId = userRoleDTO.RoleId;
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<UserRoleDTO>(userRole);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }

            return retVal;
        }

        public async Task<RoleCheckListTemplateHeaderDTO> GetRoleCheckListTemplateHeaderAsync(int Id)
        {
            var retVal = new RoleCheckListTemplateHeaderDTO();

            try
            {
                var userRole = await _dbContext.RoleCheckListTemplateHeaders.Where(w => w.Id == Id).FirstOrDefaultAsync();
                if (userRole != null)
                {
                    retVal = _mapper.Map<RoleCheckListTemplateHeaderDTO>(userRole);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<List<RoleCheckListTemplateHeaderDTO>> GetAllRoleCheckListTemplateHeadersAsync()
        {
            var retVal = new List<RoleCheckListTemplateHeaderDTO>();
            try
            {
                var results = await _dbContext.RoleCheckListTemplateHeaders
                                        .Include(i => i.CheckListTemplateHeader)
                                        .Include(i => i.Role)
                                        .AsNoTracking()
                                        .ToListAsync();
                if (results != null)
                {
                    retVal = _mapper.Map<List<RoleCheckListTemplateHeaderDTO>>(results);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<RoleCheckListTemplateHeaderDTO> UpdateRoleCheckListTemplateHeaderAsync(RoleCheckListTemplateHeaderDTO roleCheckListTemplateHeaderDTO)
        {
            var retVal = new RoleCheckListTemplateHeaderDTO();
            try
            {
                if (roleCheckListTemplateHeaderDTO.Id == 0)
                {
                    var roleCheckListTemplateHeader_Exists = await _dbContext.RoleCheckListTemplateHeaders
                                                                             .Where(w => w.RoleId == roleCheckListTemplateHeaderDTO.RoleId &&
                                                                                         w.CheckListTemplateHeaderId == roleCheckListTemplateHeaderDTO.CheckListTemplateHeaderId)
                                                                             .FirstOrDefaultAsync();
                    if (roleCheckListTemplateHeader_Exists == null)
                    {
                        var roleCheckListTemplateHeader = new RoleCheckListTemplateHeader();
                        roleCheckListTemplateHeader.CheckListTemplateHeaderId = roleCheckListTemplateHeaderDTO.CheckListTemplateHeaderId;
                        roleCheckListTemplateHeader.RoleId = roleCheckListTemplateHeaderDTO.RoleId;
                        roleCheckListTemplateHeader.Enabled = roleCheckListTemplateHeaderDTO.Enabled;

                        _dbContext.RoleCheckListTemplateHeaders.Add(roleCheckListTemplateHeader);
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<RoleCheckListTemplateHeaderDTO>(roleCheckListTemplateHeader);
                    }
                    else 
                    {
                        retVal = _mapper.Map<RoleCheckListTemplateHeaderDTO>(roleCheckListTemplateHeader_Exists);
                    }
                }
                else
                {
                    var roleCheckListTemplateHeader = await _dbContext.RoleCheckListTemplateHeaders.Where(w => w.Id == roleCheckListTemplateHeaderDTO.Id).FirstOrDefaultAsync();

                    if (roleCheckListTemplateHeader != null)
                    {
                        roleCheckListTemplateHeader.CheckListTemplateHeaderId = roleCheckListTemplateHeaderDTO.CheckListTemplateHeaderId;
                        roleCheckListTemplateHeader.RoleId = roleCheckListTemplateHeaderDTO.RoleId;
                        roleCheckListTemplateHeader.Enabled = roleCheckListTemplateHeaderDTO.Enabled;
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<RoleCheckListTemplateHeaderDTO>(roleCheckListTemplateHeader);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }

            return retVal;
        }

        public async Task<RoleCheckPointDTO> GetRoleCheckPointAsync(int Id)
        {
            var retVal = new RoleCheckPointDTO();

            try
            {
                var userRole = await _dbContext.RoleCheckPoints.Where(w => w.Id == Id).FirstOrDefaultAsync();
                if (userRole != null)
                {
                    retVal = _mapper.Map<RoleCheckPointDTO>(userRole);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<List<RoleCheckPointDTO>> GetAllRoleCheckPointsAsync()
        {
            var retVal = new List<RoleCheckPointDTO>();
            try
            {
                var results = await _dbContext.RoleCheckPoints
                                        .Include(i => i.CheckPoint)
                                        .Include(i => i.Role)
                                        .AsNoTracking()
                                        .ToListAsync();
                if (results != null)
                {
                    retVal = _mapper.Map<List<RoleCheckPointDTO>>(results);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }
            return retVal;
        }
        public async Task<RoleCheckPointDTO> UpdateRoleCheckPointAsync(RoleCheckPointDTO roleCheckPointDTO)
        {
            var retVal = new RoleCheckPointDTO();
            try
            {
                if (roleCheckPointDTO.Id == 0)
                {
                    var roleCheckPoint_Exists = await _dbContext.RoleCheckPoints
                                                                             .Where(w => w.RoleId == roleCheckPointDTO.RoleId &&
                                                                                         w.CheckPointId == roleCheckPointDTO.CheckPointId)
                                                                             .FirstOrDefaultAsync();
                    if (roleCheckPoint_Exists == null)
                    {
                        var roleCheckPoint = new RoleCheckPoint();
                        roleCheckPoint.CheckPointId = roleCheckPointDTO.CheckPointId;
                        roleCheckPoint.RoleId = roleCheckPointDTO.RoleId;
                        roleCheckPoint.Enabled = roleCheckPointDTO.Enabled;

                        _dbContext.RoleCheckPoints.Add(roleCheckPoint);
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<RoleCheckPointDTO>(roleCheckPoint);
                    }
                    else
                    {
                        retVal = _mapper.Map<RoleCheckPointDTO>(roleCheckPoint_Exists);
                    }
                }
                else
                {
                    var roleCheckPoint = await _dbContext.RoleCheckPoints.Where(w => w.Id == roleCheckPointDTO.Id).FirstOrDefaultAsync();

                    if (roleCheckPoint != null)
                    {
                        roleCheckPoint.CheckPointId = roleCheckPointDTO.CheckPointId;
                        roleCheckPoint.RoleId = roleCheckPointDTO.RoleId;
                        roleCheckPoint.Enabled = roleCheckPointDTO.Enabled;
                        await _dbContext.SaveChangesAsync();

                        retVal = _mapper.Map<RoleCheckPointDTO>(roleCheckPoint);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message);
            }

            return retVal;
        }
    }
}
