using Furion.DatabaseAccessor;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Senparc.Weixin.WxOpen.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Znyc.Dispatching.Application.Dto.Input;
using Znyc.Dispatching.Core;
using Znyc.Dispatching.Core.Entitys;
using Znyc.Dispatching.Core.Extension;
using Znyc.Dispatching.Core.Util;
using Znyc.Dispatching.WeChat.Core.CommonService.TemplateMessage.WxOpen;

namespace Znyc.Dispatching.Application
{
    /// <summary>
    ///     用户服务
    /// </summary>
    [ApiDescriptionSettings(Name = "user", Order = 10)]
    public class UserService : IUserService, IDynamicApiController, ITransient
    {
        private readonly ICacheService _cacheService;

        private readonly SmsProviderOptions _smsProviderOptions;
        private readonly IUserManager _userManager;
        private readonly WeixinSettingOptions _weixinSettingOptions;
        
        private readonly IWxUserRelationService _wxUserRelationService;
        private readonly IRoleMenuService _roleMenuService;
        private readonly ILogger<UserService> _logger;

        private readonly IRepository<User> _repository;
        private readonly IRepository<Company> _companyRepository;
        private readonly IRepository<Employee> _employeeRepository;
        private readonly IRepository<UserRole> _userRoleRepository;
        private readonly IRepository<VehiclePerson> _vehiclePersonRepository;
        private readonly IRepository<WorkPlace> workPlaceRepository;
        private readonly IRepository<ProjectPerson> _projectPersonRepository;
        private readonly IRepository<WorkPlaceEmployee> wrokPlaceEmployee;



        public UserService(
            IUserManager userManager,
            ICacheService cacheService,
            IRepository<Company> companyRepository,
            IRepository<User> repository,
            IRepository<Employee> employeeRepository,
            IRepository<UserRole> userRoleRepository,
            IOptions<SmsProviderOptions> smsProviderOptions,
            IWxUserRelationService wxUserRelationService,
            IRoleMenuService roleMenuService,
            IOptions<WeixinSettingOptions> weixinSettingOptions,
            ILogger<UserService> logger,
            IRepository<VehiclePerson> vehiclePersonRepository,
            IRepository<WorkPlace> workPlaceRepository,
            IRepository<WorkPlaceEmployee> wrokPlaceEmployee,
            IRepository<ProjectPerson> projectPersonRepository)
        {
            _repository = repository;
            _userManager = userManager;
            _cacheService = cacheService;
            _companyRepository = companyRepository;
            _employeeRepository = employeeRepository;
            _smsProviderOptions = smsProviderOptions.Value;
            _weixinSettingOptions = weixinSettingOptions.Value;
            _userRoleRepository = userRoleRepository;
            _wxUserRelationService = wxUserRelationService;
            _roleMenuService = roleMenuService;
            _logger = logger;
            _vehiclePersonRepository = vehiclePersonRepository;
            _projectPersonRepository = projectPersonRepository;
            this.workPlaceRepository = workPlaceRepository;
            this.wrokPlaceEmployee = wrokPlaceEmployee;
        }

        /// <summary>
        ///     个人资料
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("api/v1/user")]
        public async Task<UserOutput> GetUserAsync()
        {
            var userOutput = await _cacheService.GetUserAsync(_userManager.UserId);
            if (userOutput.IsNull())
            {
                var user = await _repository.Where(x => x.Id == _userManager.UserId).Select(x => new
                {
                    x.Id,
                    x.UserName,
                    x.Phone,
                    x.AvatarUrl
                }).FirstOrDefaultAsync();

                var employeeList = await _employeeRepository.Where(x => x.UserId == user.Id && x.Status == (int)CommonStatusEnum.ENABLE).ToListAsync();
                var cIds = employeeList.Select(x => x.CompanyId).ToArray();
                //有管理员优先登陆管理员
                if (employeeList.Any(x => x.RoleId == (int)RoleStatusEnum.Administrator))
                {
                    cIds = employeeList.Where(x => x.RoleId == (long)RoleStatusEnum.Administrator).Select(x => x.CompanyId).ToArray();
                }
                var company = await _companyRepository.Where(x => cIds.Contains(x.Id) && x.Status == (int)CommonStatusEnum.ENABLE).Select(x => new
                {
                    x.Id,
                    x.CompanyName
                }).FirstOrDefaultAsync();
                var employee = employeeList.Where(x => x.CompanyId == company.Id).Select(x => new
                {
                    x.RoleId,
                    x.RoleName,
                    x.CompanyId,
                    x.UserId,
                    x.EmployeeName,
                    x.IsDefault
                }).FirstOrDefault();

                userOutput = new UserOutput
                {
                    Id = user.Id,
                    AvatarUrl = user.AvatarUrl,
                    UserName = employee.EmployeeName,
                    RoleId = employee.RoleId,
                    RoleName = employee.RoleName,
                    CompanyId = employee.CompanyId,
                    CompanyName = company.CompanyName,
                    Phone = user.Phone
                };
                await _cacheService.SetUserAsync(userOutput);
            }
            return userOutput;
        }

        /// <summary>
        ///    邀请用户注册并登陆
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        [UnitOfWork]
        [Route("api/v1/user/register")]
        public async Task<LoginOutput> RegisterUserAsync(UserAddInput input)
        {

            User userEntity = new User();

            var jsCode2JsonResult =
                await SnsApi.JsCode2JsonAsync(_weixinSettingOptions.WxOpenAppId, _weixinSettingOptions.WxOpenAppSecret,
                    input.DecryptPhoneAddInput.JsCode);
            if (jsCode2JsonResult.IsNull())
            {
                throw Oops.Bah("jsCode2JsonResult为空");
            }
            var company = await _companyRepository.FindOrDefaultAsync(input.CompanyId);
            if (company.IsNull()) throw Oops.Bah("公司不存在");

            if (company.Status == (int)CommonStatusEnum.REVIEW)
            {
                throw Oops.Bah("您注册公司正在审核中,请稍等").StatusCode(4012);
            }
            if (company.Status == (int)CommonStatusEnum.DISABLE)
            {
                throw Oops.Bah("公司已被停用,请联系管理员");
            }
            var user = await _repository.FirstOrDefaultAsync(x => x.OpenId == jsCode2JsonResult.openid);
            var employee = new Employee();
            if (user.IsNull())
            {
                //解密电话       
                string phone = " ";
                if (input.DecryptPhoneAddInput.EncryptedData.IsNotNull() && input.DecryptPhoneAddInput.Iv.IsNotNull())
                {
                    try
                    {
                        var rijndael = new RijndaelManaged
                        {
                            BlockSize = 128,
                            Mode = CipherMode.CBC,
                            Padding = PaddingMode.PKCS7
                        };

                        var encryptedData = Convert.FromBase64String(input.DecryptPhoneAddInput.EncryptedData);
                        var key = Convert.FromBase64String(jsCode2JsonResult.session_key);//第一步获取到的session_key
                        var iv = Convert.FromBase64String(input.DecryptPhoneAddInput.Iv);

                        var phoneInfo = MiniProgramUtil.AESDecrypt(input.DecryptPhoneAddInput.EncryptedData, jsCode2JsonResult.session_key, input.DecryptPhoneAddInput.Iv);
                        phone = phoneInfo.PurePhoneNumber;
                        Console.WriteLine(phoneInfo.PurePhoneNumber);

                        //var decryptor = rijndael.CreateDecryptor(key, iv);
                        
                        //using (var msDecrypt = new MemoryStream(encryptedData))
                        //{
                        //    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        //    {
                        //        Console.WriteLine("csDecrypt:"+ csDecrypt);
                        //        using (var srDecrypt = new StreamReader(csDecrypt))
                        //        {
                        //            var plaintext = srDecrypt.ReadToEnd();
                        //            Console.WriteLine("plaintext:"+ plaintext);
                        //            var json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(plaintext);
                        //            Console.WriteLine("json:"+json);
                        //            var phoneNumber = json.phoneNumber;
                        //            var purePhoneNumber = json.purePhoneNumber;
                                    
                        //            //至此,成功获取到手机号
                        //            phone = Convert.ToString(purePhoneNumber);
                        //        }
                        //    }
                        //}
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"RegisterUserAsync:{ex.Message}");
                        _logger.LogError(ex.StackTrace);
                    }
                }

                //用户
                var userEntityTask = await _repository.InsertNowAsync(new User
                {
                    OpenId = jsCode2JsonResult.openid,
                    UserName = input.UserName,
                    AvatarUrl = input.AvatarUrl,
                    Phone = phone,
                    Status = CommonStatusEnum.ENABLE
                });

                userEntity = userEntityTask.Entity;

                var isDefault = input.RoleId == (int)RoleStatusEnum.Outside ? false : true;
                //员工
                var employeeEntity = await _employeeRepository.InsertNowAsync(new Employee
                {
                    UserId = userEntity.Id,
                    AvatarUrl = input.AvatarUrl,
                    EmployeeName = input.UserName,
                    EmployeePhone = phone,
                    CompanyId = input.CompanyId,
                    RoleId = input.RoleId,
                    RoleName = typeof(RoleStatusEnum).GetDescription(input.RoleId.ObjToInt()),
                    IsDefault = isDefault,
                    Status = (int)CommonStatusEnum.ENABLE
                });


                //清除员工列表Cache
                await _cacheService.RemoveEmployeeListAsync(input.CompanyId);
                //用户中间表
                await _wxUserRelationService.AddOrUpdateAsync(userEntity.Id, jsCode2JsonResult.openid, jsCode2JsonResult.unionid);
                user = userEntity;
                employee = employeeEntity.Entity;


            }
            else
            {
                userEntity = user;
                employee = await _employeeRepository.Where(x => x.UserId == userEntity.Id && x.Status == (int)CommonStatusEnum.ENABLE && x.CompanyId == input.CompanyId).FirstOrDefaultAsync();
                if (employee.IsNull())
                {
                    var employeenew = new Employee
                    {
                        UserId = userEntity.Id,
                        AvatarUrl = input.AvatarUrl,
                        EmployeeName = input.UserName,
                        EmployeePhone = userEntity.Phone,
                        CompanyId = input.CompanyId,
                        RoleId = input.RoleId,
                        RoleName = typeof(RoleStatusEnum).GetDescription((int)input.RoleId),
                        IsDefault = true,
                        Status = (int)CommonStatusEnum.ENABLE
                    };

                    Console.WriteLine($"{JsonConvert.SerializeObject(employeenew)}");
                    var isDefault = input.RoleId == (int)RoleStatusEnum.Outside ? false : true;

                    //员工
                    var employeeEntity = await _employeeRepository.InsertNowAsync(new Employee
                    {
                        UserId = userEntity.Id,
                        AvatarUrl = input.AvatarUrl,
                        EmployeeName = input.UserName,
                        EmployeePhone = userEntity.Phone,
                        CompanyId = input.CompanyId,
                        RoleId = input.RoleId,
                        RoleName = typeof(RoleStatusEnum).GetDescription(input.RoleId.ObjToInt()),
                        IsDefault = isDefault,
                        Status = (int)CommonStatusEnum.ENABLE
                    });
                    employee = employeeEntity.Entity;
                    company = new Company() { Id = input.CompanyId };
                }
            }

            // 工地方
            if(input.RoleId == 1009)
            {
                await this.wrokPlaceEmployee.InsertAsync(new WorkPlaceEmployee()
                {
                    UserId = userEntity.Id,
                    CreatedTime = DateTime.Now,
                    IsDeleted= false,
                });
            }

            // 生成 token
            string accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
            {
               {ClaimConst.CLAINM_USERID, user.Id},
               {ClaimConst.CLAINM_COMPANYID, company.Id},
               {ClaimConst.CLAINM_ROLEID, employee.RoleId},
               {ClaimConst.CLAINM_USERNAME, user.UserName},
               {ClaimConst.CLAINM_SUPERADMIN,new long[] { (long)RoleStatusEnum.Administrator, (long)RoleStatusEnum.Scheduling,(long)RoleStatusEnum.CarCaptain }.Contains(employee.RoleId) }
            });
            //RedisToken
            await _cacheService.SetTokenAsync(user.Id, accessToken);

            await _cacheService.RemoveEmployeeListAsync(input.CompanyId);
            LoginOutput loginOutput = new LoginOutput()
            {
                Token = accessToken,
                Menus = await _roleMenuService.GetRoleMenuListByIdAsync(employee.RoleId)
            };
            switch (employee.RoleId)
            {
                case (long)RoleStatusEnum.Administrator:
                case (long)RoleStatusEnum.Scheduling:
                case (long)RoleStatusEnum.CarCaptain:
                case (long)RoleStatusEnum.Salesman:
                case (long)RoleStatusEnum.Financial:
                case (long)RoleStatusEnum.WareHouse:
                case (long)RoleStatusEnum.PartTimeSalesman:
                    loginOutput.Url = CommonConst.DEFAULT_SUPERADMIN_INDEX;
                    break;
                case (long)RoleStatusEnum.CrewMembers:
                case (long)RoleStatusEnum.ProjectPerson:
                    loginOutput.Url = CommonConst.DEFAULT_DRIVER_INDEX;
                    break;
                case (long)RoleStatusEnum.Outside:
                    loginOutput.Url = CommonConst.DEFAULT_OUTSIDE_INDEX;
                    break;
                default:
                    loginOutput.Url = CommonConst.DEFAULT_DRIVER_INDEX;
                    break;
            }
            return loginOutput;
        }





        /// <summary>
        ///     修改用户信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPut]
        [Route("api/v1/user")]
        public async Task UpdateAsync(UserUpdateInput input)
        {
            var user = await _repository.FirstOrDefaultAsync(x => x.Id == input.Id);
            if (user.IsNull())
            {
                throw Oops.Oh("用户不存在");
            }
            var repeatPhone = await _repository.DetachedEntities.FirstOrDefaultAsync(x => x.Id != input.Id && x.Phone == input.Phone);
            if (repeatPhone.IsNotNull())
            {
                throw Oops.Bah("电话已存在");
            }
            user.Phone = input.Phone;
            user.UserName = input.UserName;
            user.AvatarUrl = CommonConst.Prefix_AvataUrl + input.AvatarUrl[(input.AvatarUrl.LastIndexOf("/") + 1)..];
            await _repository.UpdateNowAsync(user);

            //更新当前用户下所有角色姓名
            var employees =await  _employeeRepository.Where(x => x.UserId == user.Id).ToListAsync();
            foreach (var employee in employees)
            {
                if (employee.IsNotNull())
                {
                    employee.EmployeePhone = input.Phone;
                    employee.EmployeeName = input.UserName;
                    employee.AvatarUrl = CommonConst.Prefix_AvataUrl + input.AvatarUrl[(input.AvatarUrl.LastIndexOf("/") + 1)..];
                    await _employeeRepository.UpdateNowAsync(employee);
                    await _cacheService.RemoveEmployeeAsync(employee.Id);
                    await _cacheService.RemoveEmployeeListAsync(employee.CompanyId);
                }
            }      
            await _cacheService.RemoveUserAsync(user.Id);
            //同步车组人员表
            var vehiclePersons = await _vehiclePersonRepository.Where(x => x.IsDeleted == false && x.UserId == input.Id).ToListAsync();
            foreach (var item in vehiclePersons)
            {
                item.UserName = input.UserName;
                item.UserPhone = input.Phone;
                await _vehiclePersonRepository.UpdateNowAsync(item);
                //删除车组人员Cache
                await _cacheService.RemoveVehiclePersonAsync(item.VehicleId);
            }
            //同步工程联系人表
            var projectPersons = await _projectPersonRepository.Where(x => x.IsDeleted == false && x.ProjectPersonId == input.Id).ToListAsync();
            foreach (var item in projectPersons)
            {
                item.ProjectPersonName = input.UserName;
                item.ProjectPersonPhone = input.Phone;
                await _projectPersonRepository.UpdateNowAsync(item);
                //清除工程信息Cache
                await _cacheService.RemoveProjectAsync(item.ProjectId);
                //清除工程列表信息Cache
                await _cacheService.RemoveProjectListAsync(_userManager.CompanyId);
            }
        }





        /// <summary>
        ///     更新用户信息
        /// </summary>
        /// <param name="openId"></param>
        /// <param name="account"></param>
        /// <returns></returns>
        [HttpPut]
        [AllowAnonymous]
        public async Task UpdateAsync(string openId, string account)
        {
            //var user = await _repository.FirstOrDefaultAsync(x => x.Account == account);
            //if (user.IsNotNull())
            //{
            //    user.OpenId = openId;
            //    await _repository.UpdateNowAsync(user);
            //}
        }



        /// <summary>
        /// 解密电话号码
        /// </summary>
        /// <param name="decryptPhone"></param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("api/v1/decryptphone")]
        public async Task<string> DecryptPhoneNumber([FromBody] DecryptPhoneAddInput decryptPhone)
        {
            var jsCode2JsonResult = await SnsApi.JsCode2JsonAsync(_weixinSettingOptions.WxOpenAppId, _weixinSettingOptions.WxOpenAppSecret, decryptPhone.JsCode);
            if (jsCode2JsonResult.IsNull())
            {
                throw Oops.Oh("jsCode2JsonResult为空!");
            }
            string phone = "";
            if (decryptPhone.EncryptedData.IsNotNull() && decryptPhone.Iv.IsNotNull())
            {
                var phoneNumber = EncryptHelper.DecryptPhoneNumberBySessionKey(jsCode2JsonResult.session_key, decryptPhone.EncryptedData, decryptPhone.Iv);
                if (phoneNumber.IsNull())
                {
                    phone = phoneNumber.purePhoneNumber;
                }
            }
            return phone;
        }
    }
}