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 System.Transactions;
using MTWorkHR.Core.Entities.Base;
using System.Threading.Tasks;
using MTWorkHR.Application.Filters;

namespace MTWorkHR.Application.Services
{
    
    public class CompanyService :BaseService<Company, CompanyDto, CompanyDto>, ICompanyService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly AppSettingsConfiguration _configuration;
        private readonly GlobalInfo _globalInfo;
        private readonly IUserService _userService;
        private readonly IFileService _fileService;
        private readonly ApplicationUserManager _userManager;
        private readonly RoleManager<ApplicationRole> _roleManager;


        public CompanyService(ApplicationUserManager userManager, IUnitOfWork unitOfWork, GlobalInfo globalInfo, AppSettingsConfiguration configuration, IUserService userService, IFileService fileService, RoleManager<ApplicationRole> roleManager) : base(unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _configuration = configuration;
            _globalInfo = globalInfo;
            _userService = userService;
            _fileService = fileService;
            _userManager = userManager;
            _roleManager = roleManager;
        }

       

        public async Task<CompanyDto> GetById()
        {
            if (_globalInfo.CompanyId.HasValue)
                return await GetById(_globalInfo.CompanyId.Value);
            else 
                return new CompanyDto();
        }
        public override async Task<CompanyDto> GetById(long companyId)
        {
            var companyResponse = new CompanyDto();
            if (companyId > 0)
            {
                var entity = await _unitOfWork.Company.GetByIdWithAllChildren(companyId);
                if(entity == null)
                    throw new AppException(ExceptionEnum.RecordAlreadyExist);

                companyResponse = MapperObject.Mapper.Map<CompanyDto>(entity);
                var userDto = await _userService.GetById(entity.UserId);
                companyResponse.CommercialRegAttach = userDto.CommercialRegAttach;
                if (userDto != null)
                {
                    companyResponse.PassportAttach = userDto.PassportAttach;
                    companyResponse.IdAttach = userDto.IdAttach;
                    companyResponse.ExperienceCertificateAttach = userDto.ExperienceCertificateAttach;
                    companyResponse.TaxDeclarationAttach = userDto.TaxDeclarationAttach;
                    companyResponse.CompanyUser = MapperObject.Mapper.Map<CompanyUserDto>(userDto);
                    companyResponse.UserType = companyResponse.CompanyUser.UserType;
                }
            }
            return companyResponse;
        }

        public async  Task<List<CompanyDto>> GetAllCompanies()
        {
            var Companys = await _unitOfWork.Company.GetAllAsync();
            var response = MapperObject.Mapper.Map<List<CompanyDto>>(Companys);
            return response;
        }

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

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

            }

            var UserCreated = await CreateCompanyUser(input);
            input.UserId = UserCreated.Id;
            var entity = MapperObject.Mapper.Map<Company>(input);
            if (entity is null)
            {
                throw new AppException(ExceptionEnum.MapperIssue);
            }
           
            var company = await _unitOfWork.Company.AddAsync(entity);

            UserCreated.CompanyId = company.Id;
            await _unitOfWork.CompleteAsync();

            var response = MapperObject.Mapper.Map<CompanyDto>(company);
            return response;
        }
        public async Task<ApplicationUser> CreateCompanyUser(CompanyDto input)
        {
            var companyUser = MapperObject.Mapper.Map<UserDto>(input.CompanyUser);
            input.Email = string.IsNullOrEmpty( input.Email)? input.CompanyUser?.Email : input.Email;
            input.PhoneNumber = string.IsNullOrEmpty(input.PhoneNumber) ? input.CompanyUser?.PhoneNumber: input.PhoneNumber;
            input.Address = string.IsNullOrEmpty(input.Address) ? input.CompanyUser?.UserAddress?.AddressDesc: input.Address;
            input.CountryId = input.CountryId!=null && input.CountryId > 0 ?  input.CountryId : input.CompanyUser?.UserAddress?.CountryId;
            input.CityId = input.CityId != null && input.CityId > 0 ?  input.CityId : input.CompanyUser?.UserAddress?.CityId;
            input.PostalCode = input.PostalCode != null ?  input.PostalCode : input.CompanyUser?.UserAddress?.PostalCode;
            //UserDto companyUser = new UserDto
            //{
            //    UserType = UserTypeEnum.Business,
            //    FirstName = input.AuthorizedName,
            //    IdNumber = input.IdNumber, 
            //    Email = input.Email,
            //    PassportNumber = input.PassportNumber,
            //    UserName = input.Email,
            //    PhoneNumber = input.PhoneNumber,
            //    UserAddress = input.Address,
            //};
            var  UserAttachments = new List<AttachmentDto>();
            if (input.ProfileImage != null)
            {
                UserAttachments.Add(new AttachmentDto { FileData = input.ProfileImage, OriginalName = input.ProfileImage?.Name, FileName = input.ProfileImage?.FileName, AttachmentTypeId = 9 });
            }
            if (input.CommercialRegAttach != null)
            {
                UserAttachments.Add(new AttachmentDto { FileData = input.CommercialRegAttach, OriginalName = input.CommercialRegAttach?.Name, FileName = input.CommercialRegAttach?.FileName, AttachmentTypeId = 6 });
            }
            if (input.PassportAttach != null)
            {
                UserAttachments.Add(new AttachmentDto { FileData = input.PassportAttach, OriginalName = input.PassportAttach?.Name, FileName = input.PassportAttach?.FileName, AttachmentTypeId = 2 });
            }
            if (input.TaxDeclarationAttach != null)
            {
                UserAttachments.Add(new AttachmentDto { FileData = input.TaxDeclarationAttach, OriginalName = input.TaxDeclarationAttach?.Name, FileName = input.TaxDeclarationAttach?.FileName, AttachmentTypeId = 7 });
            }
            if (input.ExperienceCertificateAttach != null)
            {
                UserAttachments.Add(new AttachmentDto { FileData = input.ExperienceCertificateAttach, OriginalName = input.ExperienceCertificateAttach?.Name, FileName = input.ExperienceCertificateAttach?.FileName, AttachmentTypeId = 4 });
            }
            if (input.IdAttach != null)
            {
                UserAttachments.Add(new AttachmentDto { FileData = input.IdAttach, OriginalName = input.IdAttach?.Name, FileName = input.IdAttach?.FileName, AttachmentTypeId = 8 });
            }
            _fileService.CopyFileToCloud(ref UserAttachments);
            companyUser.UserAttachments = UserAttachments;

            // var userResp = await _userService.Create(companyUser);
            var user = MapperObject.Mapper.Map<ApplicationUser>(companyUser);
           
            var result = await _userManager.CreateAsync(user, companyUser.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);
            }
            if (user.UserType == 0)
            {
                user.UserType = (int)UserTypeEnum.Business;
                var employeeRole = await _roleManager.FindByNameAsync("Business");
                if (employeeRole != null)
                {
                    await _userManager.AddToRoleAsync(user, "Business");
                }
            }
            return user;
        }


        public override async Task<CompanyDto> Update(CompanyDto input)
        {
            //var companyUser = MapperObject.Mapper.Map<UserUpdateDto>(input.CompanyUser);
            _unitOfWork.BeginTran();

            var returnedUser = await UpdateCompanyUser(input);
            //var entity = await GetById(input.Id);
            var entity = await _unitOfWork.Company.GetByIdAsync(input.Id);

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

            MapperObject.Mapper.Map(input, entity, typeof(CompanyDto), typeof(Company));

            await _unitOfWork.CompleteAsync();
            _unitOfWork.CommitTran();

            return input;
        }
        public async Task<UserUpdateDto> UpdateCompanyUser(CompanyDto input)
        {
            try
            {
                var entity = _userManager.Users.Include(x => x.UserAttachments).FirstOrDefault(x => x.Id == input.UserId);

                if (entity == null)
                    throw new AppException(ExceptionEnum.RecordNotExist);
                if (input.CompanyUser != null && input.CompanyUser.UserAttachments == null)
                    input.CompanyUser.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.CompanyUser?.UserAttachments.Add(new AttachmentDto { FileData = input.ProfileImage, OriginalName = input.ProfileImage?.Name, FileName = input.ProfileImage?.FileName, AttachmentTypeId = 9 });
                }
                if (input.CommercialRegAttach != null)
                {
                    var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 1 || x.OriginalName == input.CommercialRegAttach?.Name).FirstOrDefault();
                    if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
                    input.CompanyUser?.UserAttachments.Add(new AttachmentDto { FileData = input.CommercialRegAttach, OriginalName = input.CommercialRegAttach?.Name, FileName = input.CommercialRegAttach?.FileName, AttachmentTypeId = 6 });
                }
                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.CompanyUser?.UserAttachments.Add(new AttachmentDto { FileData = input.PassportAttach, OriginalName = input.PassportAttach?.Name, FileName = input.PassportAttach?.FileName, AttachmentTypeId = 2 });
                }
                if (input.TaxDeclarationAttach != null)
                {
                    var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 3 || x.OriginalName == input.TaxDeclarationAttach?.Name).FirstOrDefault();
                    if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
                    input.CompanyUser?.UserAttachments.Add(new AttachmentDto { FileData = input.TaxDeclarationAttach, OriginalName = input.TaxDeclarationAttach?.Name, FileName = input.TaxDeclarationAttach?.FileName, AttachmentTypeId = 7});
                }
                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.CompanyUser?.UserAttachments.Add(new AttachmentDto { FileData = input.ExperienceCertificateAttach, OriginalName = input.ExperienceCertificateAttach?.Name, FileName = input.ExperienceCertificateAttach?.FileName, AttachmentTypeId = 4 });
                }
                if (input.IdAttach != null)
                {
                    var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 5 || x.OriginalName == input.IdAttach?.Name).FirstOrDefault();
                    if (oldAttach != null) entity.UserAttachments.Remove(oldAttach);
                    input.CompanyUser?.UserAttachments.Add(new AttachmentDto { FileData = input.IdAttach, OriginalName = input.IdAttach?.Name, FileName = input.IdAttach?.FileName, AttachmentTypeId = 8 });
                }
                List<AttachmentDto> attachs = input.CompanyUser?.UserAttachments.ToList();
                _fileService.CopyFileToCloud(ref attachs);
                input.CompanyUser.UserAttachments = attachs;

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


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

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

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

        public override async  Task Delete(long id)
        {
            var entity = await _unitOfWork.Company.GetByIdAsync(id);
            if(entity == null)
                throw new AppException(ExceptionEnum.RecordNotExist);

            await _userService.Delete(entity.UserId); // delete user first
            entity.IsDeleted = true;
            await _userService.UnAssignCompanyEmployees(id);
            await _unitOfWork.CompleteAsync();
        }

        public async Task Suspend(long id)
        {
            var entity = await _unitOfWork.Company.GetByIdAsync(id);
            await _userService.Suspend(entity.UserId); // suspend user first
            entity.IsSuspended = true;
            await _unitOfWork.CompleteAsync();
        }
    }
}