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;

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.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 Task<UserDTO> UpdateUserAsync(UserDTO userDTO) => throw new NotImplementedException();
        public Task<RoleDTO> GetRoleAsync(int Id) => throw new NotImplementedException();
        public Task<List<RoleDTO>> GetAllRoleAsync() => throw new NotImplementedException();
        public Task<RoleDTO> UpdateRoleAsync(RoleDTO roleDTO) => throw new NotImplementedException();
        Task<UserRoleDTO> IUserService.GetUserRoleAsync(int Id) => throw new NotImplementedException();
        public Task<List<UserRoleDTO>> GetAllUserRoleAsync() => throw new NotImplementedException();
        public Task<UserRoleDTO> UpdateUserRoleAsync(RoleDTO roleDTO) => throw new NotImplementedException();
    }
}
