using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MTWorkHR.Application.Identity;
using MTWorkHR.Application.Mapper;
using MTWorkHR.Application.Models;
using MTWorkHR.Core.Global;
using MTWorkHR.Core.IRepositories;
using MTWorkHR.Core.UnitOfWork;
using MTWorkHR.Application.Services.Interfaces;
using MTWorkHR.Core.Email;
using MTWorkHR.Core.Entities;
using MTWorkHR.Infrastructure.UnitOfWorks;
using MTWorkHR.Infrastructure.Entities;
using static Org.BouncyCastle.Crypto.Engines.SM2Engine;
using System.Web;
using System.Data;
using MTWorkHR.Core.IDto;
using System.Linq.Dynamic.Core;
using MTWorkHR.Core.Entities.Base;
using MTWorkHR.Infrastructure.EmailService;
using Countries.NET.Database;
using Microsoft.AspNetCore.Http;
using System.Collections;
using System.Linq;
using System.ComponentModel.Design;
using static iText.StyledXmlParser.Jsoup.Select.Evaluator;
using static iText.IO.Util.IntHashtable;
using Microsoft.Extensions.Logging;

namespace MTWorkHR.Application.Services
{
    public class UserService : IUserService
    {
        private readonly RoleManager<ApplicationRole> _roleManager;
        private readonly ApplicationUserManager _userManager;
        private readonly IUnitOfWork _unitOfWork;
        private readonly IUserRoleRepository<IdentityUserRole<string>> _userRole;
        private readonly AppSettingsConfiguration _configuration;
        private readonly IMailSender _emailSender;
        private readonly GlobalInfo _globalInfo;
        private readonly IFileService _fileService;
        private readonly IOTPService _oTPService;
        private readonly ILogger<UserService> _logger;

        public UserService(ApplicationUserManager userManager, IUnitOfWork unitOfWork
             , RoleManager<ApplicationRole> roleManager, GlobalInfo globalInfo, AppSettingsConfiguration configuration, IMailSender emailSender
            , IUserRoleRepository<IdentityUserRole<string>> userRole, IFileService fileService, IOTPService oTPService, ILogger<UserService> logger)
        {
            _userManager = userManager;
            _unitOfWork = unitOfWork;
            _roleManager = roleManager;
            _userRole = userRole;
            _configuration = configuration;
            _emailSender = emailSender;
            _globalInfo = globalInfo;
            _fileService = fileService;
            _oTPService = oTPService;
            _logger = logger;
        }

        public async Task<UserDto> GetById()
        {
            return await GetById(_globalInfo.UserId);
        }
        public async Task<UserDto> GetByEmail(string email)
        {
            var user = _userManager.Users.FirstOrDefault(x => x.Email == email);
            if(user == null)
                throw new AppException(ExceptionEnum.UserNotExist);

            return await GetById(user.Id);
        }
        public async Task<UserDto> GetById(string id)
        {
            var entity = await _userManager.Users
                .Include(x => x.UserRoles)
                .Include(x => x.UserAddress).ThenInclude(x=> x.City)
                .Include(x => x.UserAddress).ThenInclude(x=> x.Country)
                .Include(x => x.UserAttachments)
                .Include(x => x.JobTitle)
                .Include(x => x.Industry)
                .Include(x => x.University)
                .Include(x => x.Country)
                .Include(x => x.Qualification)
                .FirstOrDefaultAsync(x => x.Id == id);
            var response = MapperObject.Mapper.Map<UserDto>(entity);
            if (response.UserAttachments != null)
                foreach (var attach in response.UserAttachments.Where(a => a.Content != null))
                {
                    switch (attach.AttachmentTypeId)
                    {
                        case 1:
                            response.CVAttach = attach;
                            break;
                        case 2:
                            response.PassportAttach = attach;
                            break;
                        case 3:
                            response.EduCertificateAttach = attach;
                            break;
                        case 4:
                            response.ExperienceCertificateAttach= attach;
                            break;
                        case 5:
                            response.ProfCertificateAttach = attach;
                            break;
                        case 6:
                            response.CommercialRegAttach = attach;
                            break;
                        case 7:
                            response.TaxDeclarationAttach = attach;
                            break;
                        case 8:
                            response.IdAttach = attach;
                            break;
                        case 9:
                            response.ProfileImage = attach;
                            response.ProfileImagePath = attach.FilePath;
                            break;
                    }
                    attach.Content = new byte[0];
                }
            
            var attendance = await _unitOfWork.Attendance.GetAttendanceByUserId(id, DateTime.UtcNow.Date);
            response.IsCheckedIn = attendance != null && attendance.CheckInTime.HasValue;
            response.IsCheckedOut = attendance != null && attendance.CheckOutTime.HasValue;
            return response;
        }


        public async Task<EmployeeInfoDto> GetEmployeeInfo(string id)
        {
            var entity = await _userManager.Users
                .Include(x => x.UserRoles)
                .Include(x => x.UserAddress).ThenInclude(x => x.City)
                .Include(x => x.UserAddress).ThenInclude(x => x.Country)
                .Include(x => x.UserAttachments)
                .Include(x => x.JobTitle)
                .Include(x => x.Industry)
                .Include(x => x.University)
                .Include(x => x.Country)
                .Include(x => x.Qualification)
                .FirstOrDefaultAsync(x => x.Id == id);
            if(entity == null)
            {
                throw new AppException(ExceptionEnum.RecordNotExist);
            }
            var response = MapperObject.Mapper.Map<EmployeeInfoDto>(entity);
            if (entity.UserAttachments != null)
                foreach (var attach in entity.UserAttachments.Where(a => a.Content != null))
                {
                    //var stream = new MemoryStream(attach.Content);
                    //IFormFile file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FileName);

                    using (var stream = new MemoryStream(attach.Content))
                    {
                        var file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FilePath)
                        {
                            Headers = new HeaderDictionary(),
                            ContentType = attach.ContentType,

                        };
                        System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
                        {
                            FileName = file.FileName
                        };
                        file.ContentDisposition = cd.ToString();
                        switch (attach.AttachmentTypeId)
                        {
                            case 1:
                                response.CVAttach = file;
                                break;
                            case 4:
                                response.ExperienceCertificateAttach = file;
                                break;
                            case 5:
                                response.ProfCertificateAttach = file;
                                break;
                            case 9:
                                response.ProfileImage = file;
                                response.ProfileImagePath = attach.FilePath;
                                break;
                        }
                        attach.Content = new byte[0];
                    }
                }
            return response;
        }
        public async Task<UserDto> GetUserById(string id)
        {
            var entity = await _userManager.Users
                .FirstOrDefaultAsync(x => x.Id == id);
            var response = MapperObject.Mapper.Map<UserDto>(entity);

            return response;
        }
        public async Task<string> GetUserFullName(string userId)
        {
            var entity = await GetUserById(userId);
            var name = $"{entity.FirstName} {entity.LastName}".Trim();
            return name;
        }

        public async Task<(string FullName, string Email)> GetUserNameEmail(string userId)
        {
            var entity = await GetUserById(userId);

            if (entity == null)
                return (string.Empty, string.Empty);

            var name = $"{entity.FirstName} {entity.LastName}".Trim();
            var email = entity.Email ?? string.Empty;

            return (name, email);
        }

        public async Task<UserDto> GetUserWithAttachmentById(string id)
        {
            var entity = await _userManager.Users.Include(u=> u.UserAttachments).Include(u=> u.JobTitle)
                .FirstOrDefaultAsync(x => x.Id == id);
            var response = MapperObject.Mapper.Map<UserDto>(entity);

            return response;
        }

        public async Task<List<UserDto>> GetUsersWithAttachmentsByIds(List<string> ids)
        {
            // Validate input
            if (ids == null || !ids.Any())
            {
                return new List<UserDto>();
            }

            // Query users with related data
            var users = await _userManager.Users
                .AsNoTracking() // Optional: Use if read-only data is sufficient
                .Include(u => u.UserAttachments)
                .Include(u => u.JobTitle)
                .Where(u => ids.Contains(u.Id))
                .ToListAsync();
            var response = MapperObject.Mapper.Map<List<UserDto>>(users);
            return response;
        }

        public async Task<string> GetProfileImage(string userId)
        {
            string imagePath = "";
            var user = await _userManager.Users.Include(u => u.UserAttachments)
                .FirstOrDefaultAsync(x => x.Id == userId);
            if (user != null)
            {
                var imageAtt = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
                imagePath = imageAtt != null ? imageAtt.FilePath : "";
            }
            return imagePath;
        }
        //public async Task<List<UserDto>> GetAll(PagingInputDto pagingInput)
        //{
        //    var employees = await _userManager.GetUsersInRoleAsync("Employee");
        //    return employees.Select(e => new UserDto
        //    {
        //        Email = e.Email,
        //        FirstName = e.FirstName,
        //        LastName = e.LastName,
        //        Id = e.Id
        //    }).ToList();
        //}
        public virtual async Task<PagingResultDto<UserAllDto>> GetAll(UserPagingInputDto PagingInputDto)
        {
            var companies = await _unitOfWork.Company.GetAllAsync();
           
            var query = _userManager.Users
                .Include(u => u.Qualification)
                .Include(u => u.JobTitle)
                .Include(u => u.University)
                .Include(u => u.Industry)
                .Include(u => u.Country)
                .Where(u => !u.IsDeleted && !u.IsStopped && ( _globalInfo.CompanyId == null || u.CompanyId != _globalInfo.CompanyId));

            var filter = PagingInputDto.Filter?.Trim();

            // Filtering by text fields and handling full name search
            if (!string.IsNullOrEmpty(filter))
            {
                var filters = filter.Split(" ");
                var firstNameFilter = filters[0];
                var lastNameFilter = filters.Length > 1 ? filters[1] : "";

                query = query.Where(u =>
                    u.UserName.Contains(filter) || u.Email.Contains(filter) || u.FavoriteName.Contains(filter) ||
                    u.Position.Contains(filter) || u.PhoneNumber.Contains(filter) ||
                    u.FirstName.Contains(firstNameFilter) && (string.IsNullOrEmpty(lastNameFilter) || u.LastName.Contains(lastNameFilter)) ||
                    u.LastName.Contains(lastNameFilter) && (string.IsNullOrEmpty(firstNameFilter) || u.FirstName.Contains(firstNameFilter))
                );
            }

            // Applying additional filters
            if (PagingInputDto.IndustryId?.Count > 0)
                query = query.Where(u => u.IndustryId.HasValue && PagingInputDto.IndustryId.Contains(u.IndustryId.Value));

            if (PagingInputDto.QualificationId.HasValue)
                query = query.Where(u => u.QualificationId == PagingInputDto.QualificationId);

            if (PagingInputDto.JobTitleId.HasValue)
                query = query.Where(u => u.JobTitleId == PagingInputDto.JobTitleId);

            if (PagingInputDto.UniversityId.HasValue)
                query = query.Where(u => u.UniversityId == PagingInputDto.UniversityId);

            if (PagingInputDto.CountryId?.Count > 0)
                query = query.Where(u => u.CountryId.HasValue && PagingInputDto.CountryId.Contains(u.CountryId.Value));

            if (PagingInputDto.UserTypeId?.Count > 0)
                query = query.Where(u => PagingInputDto.UserTypeId.Contains(u.UserType));

            if (PagingInputDto.Employed.HasValue)
                query = query.Where(u => PagingInputDto.Employed.Value ? u.CompanyId != null : u.CompanyId == null);

            // Ordering by specified field and direction
            var orderByField = !string.IsNullOrEmpty(PagingInputDto.OrderByField) ? PagingInputDto.OrderByField : "UserName";
            var orderByDirection = PagingInputDto.OrderType?.ToLower() == "desc" ? "desc" : "asc";
            query = query.OrderBy($"{orderByField} {orderByDirection}");

            // Pagination
            var total = await query.CountAsync();
            var result = await query
                .Skip((PagingInputDto.PageNumber - 1) * PagingInputDto.PageSize)
                .Take(PagingInputDto.PageSize)
                .ToListAsync();

            var list = MapperObject.Mapper.Map<IList<UserAllDto>>(result);

            var ss = list
             .Join(companies.Item1,               // Join the list with companies.Item1
                   u => u.CompanyId,               // Key selector for User (CompanyId)
                   c => c.Id,                      // Key selector for Company (Id)
                   (u, c) => {                     // Project the join result
                       u.CompanyName = c.CompanyName;     // Assuming 'Name' is the CompanyName in Company entity
                       return u;                   // Return the updated UserAllDto with CompanyName filled
                   })
             .ToList();

            return new PagingResultDto<UserAllDto> { Result = list, Total = total };
        }

        public async Task<List<UserDto>> GetAllEmployees()
        {
            var employees = await _userManager.GetUsersInRoleAsync("Employee");
            return employees.Select(e => new UserDto
            {
                Email = e.Email,
                FirstName = e.FirstName,
                LastName = e.LastName,
                Id = e.Id
            }).ToList();
        }


        public async Task<List<UserAllDto>> GetAllCompanyEmployees()
        {
            var AllEmployees = await _userManager.GetUsersInRoleAsync("Employee");
            var AllContractors = await _userManager.GetUsersInRoleAsync("Contractor");

            var employees = AllEmployees.Where(e => e.CompanyId == _globalInfo.CompanyId).ToList();
            var contractors = AllContractors.Where(e => e.CompanyId == _globalInfo.CompanyId).ToList();

            var allUsers = employees.Union(contractors);
            var response = MapperObject.Mapper.Map<List<UserAllDto>>(allUsers);

            return response;
        }
        public async Task<bool> UnAssignCompanyEmployees(long companyId)
        {
            try
            {
                var AllEmployees = await _userManager.GetUsersInRoleAsync("Employee");
                var AllContractors = await _userManager.GetUsersInRoleAsync("Contractor");
                var employees = AllEmployees.Where(e => e.CompanyId == companyId).ToList();
                var contractors = AllContractors.Where(e => e.CompanyId == companyId).ToList();
                foreach (var emp in employees.Union(contractors))
                {
                    emp.CompanyId = null;
                    await _userManager.UpdateAsync(emp);

                }
                await _unitOfWork.CompleteAsync();
                return true;
            }
            catch(Exception ex) 
            { return false; }
        }
        public async Task Delete(string id)
        {
            var user = await _userManager.FindByIdAsync(id);
            if (user == null)
                throw new AppException(ExceptionEnum.RecordNotExist);
            if (!user.IsDeleted )
            {
                user.IsDeleted = true;

                await _userManager.UpdateAsync(user);
                await _unitOfWork.CompleteAsync();
            }
        }
        public async Task Suspend(string id)
        {
            var user = await _userManager.FindByIdAsync(id);
            if (user == null)
                throw new AppException(ExceptionEnum.RecordNotExist);
            if (!user.IsStopped)
            {
                user.IsStopped = true;
                await _userManager.UpdateAsync(user);
                await _unitOfWork.CompleteAsync();
            }
        }
        public async Task ActiveUser(string userId)
        {
            var entity = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
            if (entity == null)
                throw new AppException(ExceptionEnum.RecordNotExist);
            entity.IsStopped = false;
            entity.AccessFailedCount = 0;
            entity.LockoutEnabled = false;
            entity.LockoutEnd = null;
            await _userManager.UpdateAsync(entity);
            await _unitOfWork.CompleteAsync();
        }


        public async Task<UserDto> Create(UserDto input)
        {
            var emailExists = await _userManager.FindByEmailAsync(input.Email);
            if (emailExists != null)
                throw new AppException(ExceptionEnum.RecordEmailAlreadyExist);

            var phoneExists = await _userManager.FindByAnyAsync(input.PhoneNumber);
            if (phoneExists != null)
                throw new AppException(ExceptionEnum.RecordPhoneAlreadyExist);

            var userExists = await _userManager.FindByAnyAsync(input.UserName);
            if (userExists != null)
                throw new AppException(ExceptionEnum.RecordNameAlreadyExist);
            //loop for given list of attachment, and move each file from Temp path to Actual path
            //  _fileService.UploadFiles(files);
            input.CountryId = input.CountryId == null || input.CountryId == 0 ? input.UserAddress?.CountryId : input.CountryId;
           // if (input.UserAttachments == null )
            input.UserAttachments = new List<AttachmentDto>();
            AddAttachment(input.ProfileImage, 9,  input.UserAttachments);
            AddAttachment(input.CVAttach, 1,  input.UserAttachments);
            AddAttachment(input.PassportAttach, 2,  input.UserAttachments);
            AddAttachment(input.EduCertificateAttach, 3,  input.UserAttachments);
            AddAttachment(input.ExperienceCertificateAttach, 4,  input.UserAttachments);
            AddAttachment(input.ProfCertificateAttach, 5,  input.UserAttachments);


            var files = input.UserAttachments.Select(a=> a.FileData).ToList();
            List<AttachmentDto> attachs = input.UserAttachments.ToList();
             _fileService.CopyFileToCloud(ref attachs);
            //if (!res)
            //    throw new AppException(ExceptionEnum.CouldNotMoveFiles);
            input.UserAttachments = attachs;
            var user = MapperObject.Mapper.Map<ApplicationUser>(input);
            if(user.UserType == 0)
            {
                user.UserType = (int)UserTypeEnum.Employee;//default if not selected
            }
            _unitOfWork.BeginTran();
            user.CreateDate = DateTime.UtcNow;
            //saving user
            var result = await _userManager.CreateAsync(user, input.Password);
            if (!result.Succeeded)
            {
                if(result.Errors != null && result.Errors.Count() > 0)
                {
                    var msg = result.Errors.Select(a => a.Description ).Aggregate((a,b) => a + " /r/n " + b);
                    throw new AppException(msg);
                }
                throw new AppException(ExceptionEnum.RecordCreationFailed);
            }
                
            input.Id = user.Id;

            //saving userRoles
            if(input.UserRoles == null || input.UserRoles.Count == 0)
            {
                if(user.UserType == (int)UserTypeEnum.Employee)
                {
                    var employeeRole = await _roleManager.FindByNameAsync("Employee");
                    if (employeeRole != null)
                    {
                        await _userManager.AddToRoleAsync(user, "Employee");
                    }
                }
                else if (user.UserType == (int)UserTypeEnum.Contractor)
                {
                    var employeeRole = await _roleManager.FindByNameAsync("Contractor");
                    if (employeeRole != null)
                    {
                        await _userManager.AddToRoleAsync(user, "Contractor");
                    }
                }

            }
            else
            {
                var userRoles = MapperObject.Mapper.Map<List<IdentityUserRole<string>>>(input.UserRoles);
                foreach (var role in userRoles)
                {
                    role.UserId = user.Id;
                    if (await _roleManager.FindByIdAsync(role.RoleId) == null)
                        throw new AppException(ExceptionEnum.RecordNotExist);
                    var roleOb = input.UserRoles?.FirstOrDefault(r => r.RoleId == role.RoleId);
                    var roleName = roleOb != null ? roleOb.RoleName : "Employee";
                    await _userManager.AddToRoleAsync(user, roleName);
                }
            }
            // await _userRole.AddRangeAsync(userRoles);

            await _unitOfWork.CompleteAsync();
            _unitOfWork.CommitTran();
            try
            {
                var resultPassReset = await GetConfirmEmailURL(user.Id);

                var sendMailResult = await _emailSender.SendEmail(new EmailMessage
                {
                    Subject = "Register Confirmation",
                    To = input.Email,
                    Body = "Please Set Your Password (this link will expired after 24 hours)"
                    ,
                    url = resultPassReset.Item1,
                    userId = user.Id
                });
                if (!sendMailResult)
                {
                    _logger.LogError("User created, but could not send the email!");
                    throw new AppException("User created, but could not send the email!");
                }
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.Message);
                throw new AppException("User created, but could not send the email!");
            }
            return input;
        }



        private void AddAttachment(AttachmentDto attachment, int attachmentTypeId, IList<AttachmentDto> attachList)
        {
            if (attachment != null)
            {
                attachList.Add(new AttachmentDto
                {
                    FilePath = attachment.FilePath,
                    OriginalName = attachment.OriginalName,
                    FileName = attachment.FileName,
                    AttachmentTypeId = attachmentTypeId
                });
            }
        }
        public async Task<BlobObject> Download(string filePath)
        {
            var file = await _fileService.Download(filePath);
                return file;
        }
        public async Task<bool> ConfirmEmail(ConfirmEmailDto input)
        {
            var user = await _userManager.FindByIdAsync(input.UserId);
            if (user == null)
                throw new AppException(ExceptionEnum.UserNotExist);
            var result = await _userManager.ConfirmEmailAsync(user, input.Token);
            return result.Succeeded;
        }
        private async Task<Tuple<string, string>> GetResetPasswordURL(string userId)
        {
            var user = await _userManager.Users.FirstOrDefaultAsync(x => !x.IsDeleted && x.Id.Equals(userId));
            if (user == null)
                throw new AppException(ExceptionEnum.UserNotExist);

            string code = await _userManager.GeneratePasswordResetTokenAsync(user);
            var route = "auth/ConfirmEmail";
            var origin = _configuration.JwtSettings.Audience;
            var endpointUri = new Uri(string.Concat($"{origin}/", route));
            var userURL = QueryHelpers.AddQueryString(endpointUri.ToString(), "userId", user.Id);
            var passwordResetURL = QueryHelpers.AddQueryString(userURL.ToString(), "token", code);
            return new Tuple<string, string>(passwordResetURL, user.Email);
        }

        private async Task<Tuple<string, string>> GetConfirmEmailURL(string userId)
        {
            var user = await _userManager.Users.FirstOrDefaultAsync(x => !x.IsDeleted && x.Id.Equals(userId));
            if (user == null)
                throw new AppException(ExceptionEnum.UserNotExist);

            string token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            string codeHtmlVersion = HttpUtility.UrlEncode(token);

            var route = "auth/ConfirmEmail";
            var origin = _configuration.JwtSettings.Audience;
            var endpointUri = new Uri(string.Concat($"{origin}/", route));
            var userURL = QueryHelpers.AddQueryString(endpointUri.ToString(), "userId", user.Id);
            var confirmEmailUrl = QueryHelpers.AddQueryString(userURL.ToString(), "token", codeHtmlVersion);
            return new Tuple<string, string>(confirmEmailUrl, user.Email);
        }
        public async Task<UserUpdateDto> Update(UserUpdateDto input)
        {
            try
            {
                var entity = await _userManager.Users
                .Include(x => x.UserAttachments)
                .FirstOrDefaultAsync(x => x.Id == input.Id);

                if (entity == null)
                    throw new AppException(ExceptionEnum.UserNotExist);

                input.UserAttachments = new List<AttachmentDto>();

                var oldAttachList = entity.UserAttachments;

                // Process each attachment type
                ProcessAttachment(oldAttachList, input.UserAttachments, input.ProfileImage, 9);
                ProcessAttachment(oldAttachList, input.UserAttachments, input.CVAttach, 1);
                ProcessAttachment(oldAttachList, input.UserAttachments, input.PassportAttach, 2);
                ProcessAttachment(oldAttachList, input.UserAttachments, input.EduCertificateAttach, 3);
                ProcessAttachment(oldAttachList, input.UserAttachments, input.ExperienceCertificateAttach, 4);
                ProcessAttachment(oldAttachList, input.UserAttachments, input.ProfCertificateAttach, 5);

                // Cloud copy
                List<AttachmentDto> attachments = input.UserAttachments.ToList();
                _fileService.CopyFileToCloud(ref attachments);
                input.UserAttachments = attachments;

                // Map updated input to entity
                MapperObject.Mapper.Map(input, entity);
                entity.UpdateDate = DateTime.UtcNow;

                _unitOfWork.BeginTran();
                var result = await _userManager.UpdateAsync(entity);
                if (!result.Succeeded)
                    throw new AppException(ExceptionEnum.RecordUpdateFailed);

                await _unitOfWork.CompleteAsync();
                _unitOfWork.CommitTran();
            }
            catch (Exception e)
            {
                throw e;
            }
            // Return updated user
            var userResponse = await GetById(input.Id);
            return MapperObject.Mapper.Map<UserUpdateDto>(userResponse);
        }

        private void ProcessAttachment(ICollection<UserAttachment> oldAttachments, IList<AttachmentDto> newAttachments, AttachmentDto attachment,int attachmentTypeId)
        {
            if (attachment == null)
                return;

            var oldAttach = oldAttachments
                .FirstOrDefault(x => x.AttachmentTypeId == attachmentTypeId || x.FileName == attachment.FileName);

            if (oldAttach != null)
                oldAttachments.Remove(oldAttach);

            newAttachments.Add(new AttachmentDto
            {
                FilePath = attachment.FilePath,
                OriginalName = attachment.OriginalName,
                FileName = attachment.FileName,
                AttachmentTypeId = attachmentTypeId
            });
        }

        //public async Task<UserUpdateDto> Update2(UserUpdateDto input)
        //{
        //    try
        //    {
        //        var entity =  _userManager.Users.Include(x => x.UserAttachments).FirstOrDefault(x=> x.Id == input.Id);

        //        if (entity == null)
        //            throw new AppException(ExceptionEnum.UserNotExist);
        //        if (input.UserAttachments == null)
        //            input.UserAttachments = new List<AttachmentDto>();
        //        var oldAttachList = entity.UserAttachments;
        //        if (input.ProfileImage != null)
        //        {
        //            var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 9 || x.OriginalName == input.ProfileImage?.Name).FirstOrDefault();
        //            if(oldAttach != null) entity.UserAttachments.Remove(oldAttach);
        //            input.UserAttachments.Add(new AttachmentDto { FileData = input.ProfileImage, OriginalName = input.ProfileImage?.Name, FileName = input.ProfileImage?.FileName, AttachmentTypeId = 9 });
        //        }
        //        if (input.CVAttach != null)
        //        {
        //            var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 1 || x.OriginalName == input.CVAttach?.Name).FirstOrDefault();
        //            if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
        //            input.UserAttachments.Add(new AttachmentDto { FileData = input.CVAttach, OriginalName = input.CVAttach?.Name, FileName = input.CVAttach?.FileName, AttachmentTypeId = 1 });
        //        }
        //        if (input.PassportAttach != null)
        //        {
        //            var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 2 || x.OriginalName == input.PassportAttach?.Name).FirstOrDefault();
        //            if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
        //            input.UserAttachments.Add(new AttachmentDto { FileData = input.PassportAttach, OriginalName = input.PassportAttach?.Name, FileName = input.PassportAttach?.FileName, AttachmentTypeId = 2 });
        //        }
        //        if (input.EduCertificateAttach != null)
        //        {
        //            var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 3 || x.OriginalName == input.EduCertificateAttach?.Name).FirstOrDefault();
        //            if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
        //            input.UserAttachments.Add(new AttachmentDto { FileData = input.EduCertificateAttach, OriginalName = input.EduCertificateAttach?.Name, FileName = input.EduCertificateAttach?.FileName, AttachmentTypeId = 3 });
        //        }
        //        if (input.ExperienceCertificateAttach != null)
        //        {
        //            var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 4 || x.OriginalName == input.ExperienceCertificateAttach?.Name).FirstOrDefault();
        //            if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
        //            input.UserAttachments.Add(new AttachmentDto { FileData = input.ExperienceCertificateAttach, OriginalName = input.ExperienceCertificateAttach?.Name, FileName = input.ExperienceCertificateAttach?.FileName, AttachmentTypeId = 4 });
        //        }
        //        if (input.ProfCertificateAttach != null)
        //        {
        //            var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 5 || x.OriginalName == input.ProfCertificateAttach?.Name).FirstOrDefault();
        //            if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
        //            input.UserAttachments.Add(new AttachmentDto { FileData = input.ProfCertificateAttach, OriginalName = input.ProfCertificateAttach?.Name, FileName = input.ProfCertificateAttach?.FileName, AttachmentTypeId = 5 });
        //        }
        //        List<AttachmentDto> attachs = input.UserAttachments.ToList();
        //        _fileService.CopyFileToCloud(ref attachs);
        //        input.UserAttachments = attachs;

        //        //if (!await _fileService.CopyFileToActualFolder(input.UserAttachments.ToList()))
        //        //    throw new AppException(ExceptionEnum.CouldNotMoveFiles);


        //        MapperObject.Mapper.Map(input, entity);

        //        _unitOfWork.BeginTran();
        //        entity.UpdateDate = DateTime.UtcNow;
        //        //saving user
        //        var result = await _userManager.UpdateAsync(entity);
        //        if (!result.Succeeded)
        //            throw new AppException(ExceptionEnum.RecordUpdateFailed);

             

        //        await _unitOfWork.CompleteAsync();
        //        _unitOfWork.CommitTran();
        //    }
        //    catch (Exception e)
        //    {
        //        throw e;
        //    }
        //    var userResponse = await GetById(input.Id);
        //    var user = MapperObject.Mapper.Map<UserUpdateDto>(userResponse);
        //    return user;
        //}

        public async Task<bool> Update(string userId, long companyId)
        {
            try
            {
                var entity = _userManager.Users.FirstOrDefault(x => x.Id == userId);

                if (entity == null)
                    throw new AppException(ExceptionEnum.UserNotExist);


                entity.CompanyId = companyId;
                entity.UpdateDate = DateTime.UtcNow;

                //saving user
                var result = await _userManager.UpdateAsync(entity);
                if (!result.Succeeded)
                    throw new AppException(ExceptionEnum.RecordUpdateFailed);
                await _unitOfWork.CompleteAsync();
                return true;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        public async Task<bool> IsExpiredToken(ConfirmEmailDto input)
        {
            var user = await _userManager.Users.IgnoreQueryFilters().FirstOrDefaultAsync(x => x.Id == input.UserId);
            if (user == null)
                throw new AppException(ExceptionEnum.UserNotExist);
            var purpose = UserManager<ApplicationUser>.ResetPasswordTokenPurpose;
            var result = await _userManager.VerifyUserTokenAsync(user, "Default", purpose, input.Token);
            return !result;
        }

        public async Task<bool> ResetPassword(ResetPasswordDto input)
        {
            var user = await _userManager.FindByIdAsync(_globalInfo.UserId);

            if (user == null)
                throw new AppException(ExceptionEnum.UserNotExist);

            if (!await _userManager.CheckPasswordAsync(user, input.OldPassword))
                throw new AppException(ExceptionEnum.WrongCredentials);

            var token = await _userManager.GeneratePasswordResetTokenAsync(user);

            var result = await _userManager.ResetPasswordAsync(user, token, input.NewPassword);
            if (!result.Succeeded)
                throw new AppException(ExceptionEnum.RecordUpdateFailed);

            return true;
        }
     
        public async Task<ForgetPasswordResponseDto> ForgetPasswordMail(string email) //Begin forget password
        {
            var foundUser = await _userManager.FindByEmailAsync(email);
            if (foundUser != null)
            {
                string oneTimePassword = await _oTPService.RandomOneTimePassword(foundUser.Id);
                await _oTPService.SentOTPByMail(foundUser.Id, foundUser.Email, oneTimePassword);
                ForgetPasswordResponseDto res = new ForgetPasswordResponseDto { UserId = foundUser.Id};
                return res;
            }
            else
            {
                throw new AppException(ExceptionEnum.UserNotExist);
            }
        }
        public async Task<bool> VerifyOTP(VerifyOTPDto input)
        {
            if (! await _oTPService.VerifyOTP(input.UserId, input.OTP))
                throw new AppException(ExceptionEnum.WrongOTP);
            return true;
        }
        public async Task<bool> ForgetPassword(ForgetPasswordDto input)
        {
            var user = await _userManager.Users.IgnoreQueryFilters().FirstOrDefaultAsync(x => x.Id == input.UserId);
            if (user == null)
                throw new AppException(ExceptionEnum.UserNotExist);
           
            string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);

            var result = await _userManager.ResetPasswordAsync(user, resetToken, input.Password);
            if (!result.Succeeded)
            {
                if (result.Errors != null && result.Errors.Count() > 0)
                {
                    var msg = result.Errors.Select(a => a.Description).Aggregate((a, b) => a + " /r/n " + b);
                    throw new AppException(msg);
                }
                throw new AppException(ExceptionEnum.RecordCreationFailed);
            }
            return result.Succeeded;
        }


        public async Task StopUser(string userId)
        {
            var entity = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
            if (entity == null)
                throw new AppException(ExceptionEnum.UserNotExist);
            if (!entity.IsStopped)
            {
                entity.IsStopped = true;
                await _unitOfWork.CompleteAsync();
            }
        }
        public async Task<JobInterviewDto> SetUpInterview(JobInterviewDto input) 
        {
            var foundUser = await _userManager.FindByEmailAsync(input.Email);
            if (foundUser != null)
            {
                if (input.SendByMail.HasValue && input.SendByMail.Value)
                {
                    var sendMailResult = await _emailSender.SendEmail(new EmailMessage
                    {
                        Subject = "Interview meeting link",
                        To = input.Email,
                        Body = "Please attend the job interview by using the following link"
                        ,
                        url = input.InterviewLink,
                        userId = foundUser.Id
                    });

                    if (!sendMailResult)
                    {
                        throw new AppException("could not send the email!");
                    }
                }
                input.UserId = foundUser.Id;
                var jobInterview = MapperObject.Mapper.Map<JobInterview>(input);
                var result = await _unitOfWork.JobInterview.AddAsync(jobInterview);
                await _unitOfWork.CompleteAsync();

                var res = MapperObject.Mapper.Map<JobInterviewDto>(result);
                return res;
            }
            else
            {
                throw new AppException(ExceptionEnum.UserNotExist);
            }
        }
    }
}
