using Back.Common;
using Back.Data.Contracts;
using Back.Data.Infrastructure.Repository;
using Back.Data.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Org.BouncyCastle.Crypto.Tls;
using Shared.DTOs;
using System.ComponentModel.Design;
using System.Data.SqlTypes;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Claims;
using System.Text;

namespace Back.Services
{
    public class servUser
    {
        private readonly IConfiguration _configuration;

        private readonly servNotification _servNotification;
        private readonly IAsyncRepository<User> _RepoUser;
        private readonly RepositoryBase<Company> _RepoCompany;
        private readonly IAsyncRepository<PermissionPeriod> _RepoPermissionPeriod;
        private readonly IAsyncRepository<InvoiceItem> _invoiceitemRepo;
        private readonly IAsyncRepository<Customer> _RepoCus;
        private readonly IAsyncRepository<Invoice> _RepoInvoice;
        private readonly IAsyncRepository<CODItem> _RepoCODItem;
        public servUser(IConfiguration configuration
            , servNotification servNotification
            , IAsyncRepository<User> RepoUser
            , IAsyncRepository<PermissionPeriod> RepoPermissionPeriod
            , RepositoryBase<Company> repoCompany, IAsyncRepository<Customer> RepoCus
            , IAsyncRepository<Invoice> RepoInvoice, IAsyncRepository<CODItem> RepoCODItem
            , IAsyncRepository<InvoiceItem> invoiceitemRepo)
        {
            _invoiceitemRepo = invoiceitemRepo;
            _RepoCus = RepoCus;
            _RepoInvoice = RepoInvoice;
            _RepoCODItem = RepoCODItem;
            _configuration = configuration;
            _servNotification = servNotification;
            _RepoUser = RepoUser;
            _RepoPermissionPeriod = RepoPermissionPeriod;
            _RepoCompany = repoCompany;
        }
        public async Task<User?> GetUserByUserNameAndPassword(string UserName, string Password)
        {
            return await _RepoUser.Get(w => w.Username == UserName && w.Password == Password.encrypted() && w.IsActive)
                  // .Include(i => i.RolUsers)
                  //  .ThenInclude(ti => ti.rolePermissions)
                  // .Include(i => i.RolUsers)
                  // .ThenInclude(ti=>ti.Company)
                  //.ThenInclude(ti => ti.PermissionPeriods)
                  // .ThenInclude(ti => ti.Permission)
                  .Include(ti => ti.RolUsers)
                  .ThenInclude(ti => ti.Company)
                 //.ThenInclude(ti => ti.PermissionPeriods)
                 //       .ThenInclude(ti => ti.CalculationType)
                 .FirstOrDefaultAsync();
        }
        public async Task<UserAuthenticationDTO?> UserAuthentication(string UserNameORUserID, string Password = "", bool newtoken = true)
        {
            UserAuthenticationDTO ret = new UserAuthenticationDTO();
            User? user = null;

            if (string.IsNullOrEmpty(Password) && int.TryParse(UserNameORUserID, out int UserID))
                user = await GetUserByUserID(UserID);
            else
                user = await GetUserByUserNameAndPassword(UserNameORUserID, Password);

            if (user == null)
                return null;

            string Jwt_Lifetime_Minutes = await GetJwt_Lifetime_Minutes();
            ret.UserName = user.Username;
            ret.Token = newtoken ? await CerateToken(user.ID, user.Username, Jwt_Lifetime_Minutes) : user.Token;
            ret.FullName = user.Fullname;
            ret.Photo = user.Photo == null ? null : Convert.ToBase64String(user.Photo);
            //foreach (var rol in user.RolUsers)
            //{
            //    if (!rol.Company.IsActive)
            //        continue;

            //    List<PermissionAuthenticationDTO> permissions = new List<PermissionAuthenticationDTO>();
            //    foreach (var per in rol.Company.PermissionPeriods)
            //    {
            //        bool _accessibility = await _checkPermission.AllowPermission(user.ID, rol.CompanyID, per.Permission.ID);

            //        #region Child

            //        List<Permission> Chidpermissions = _accessibility ? await _servPermission.GetChildPermission(per.Permission.ID):new List<Permission>();
            //        List<PermissionAuthenticationDTO> ChildpermissionAuthenticationDTOs = new List<PermissionAuthenticationDTO>();
            //        foreach (Permission childper in Chidpermissions)
            //        {
            //            bool _childaccessibility = await _checkPermission.AllowPermission(user.ID, rol.CompanyID, childper.ID);
            //            PermissionAuthenticationDTO ChildpermissionAuthenticationDTO = new PermissionAuthenticationDTO
            //            {
            //                ID = childper.ID,
            //                ParentID = childper.ParentID,
            //                Title = childper.Title,
            //                accessibility = _childaccessibility,
            //                //TODO
            //                ChildPermissions = null
            //            };
            //            ChildpermissionAuthenticationDTOs.Add(ChildpermissionAuthenticationDTO);
            //        }
            //        #endregion

            //        PermissionAuthenticationDTO permissionAuthenticationDTO = new PermissionAuthenticationDTO
            //        {
            //            ID = per.Permission.ID,
            //            ParentID = per.Permission.ParentID,
            //            Title = per.Permission.Title,
            //            accessibility = _accessibility,
            //            //TODO
            //            ChildPermissions = ChildpermissionAuthenticationDTOs

            //            //Period=new PeriodDTO() 
            //            //{ 
            //            //    CalculationTypeID = rol.Company.PermissionPeriods.Where(w => w.PermissionID == per.ID).Select(s => s.CalculationType.ID).FirstOrDefault(),
            //            //    CalculationTypeTitle = rol.Company.PermissionPeriods.Where(w => w.PermissionID == per.ID).Select(s => s.CalculationType.Title).FirstOrDefault(),
            //            //    RemainingAmount = rol.Company.PermissionPeriods.Where(w => w.PermissionID == per.ID).Select(s => s.RemainingAmount).FirstOrDefault(),
            //            //    TotalAmount = rol.Company.PermissionPeriods.Where(w => w.PermissionID == per.ID).Select(s => s.TotalAmount).FirstOrDefault()
            //            //}

            //        };
            //        permissions.Add(permissionAuthenticationDTO);
            //    }

            //    ret.Companies.Add(new CompanyAuthenticationDTO 
            //    { 
            //        ID = rol.CompanyID,
            //        Name = rol.Company.Name, 
            //        IsAdmin = rol.IsAdmin,
            //        Logo= rol.Company.Logo == null ? null : Convert.ToBase64String(rol.Company.Logo)

            //        /*, permissions = permissions*/
            //    });

            //}
            ret.Company = new CompanyAuthenticationDTO
            {
                ID = user.RolUsers.First().CompanyID,
                Name = user.RolUsers.First().Company.Name,
                IsAdmin = user.RolUsers.First().IsAdmin,
                Logo = user.RolUsers.First().Company.Logo == null ? null : Convert.ToBase64String(user.RolUsers.First().Company.Logo)

            };
            var dt = newtoken ? DateTime.Now : user.DateLastLogin.ToMiladiByTime();
            ret.enterDate = dt;
            ret.exitDate = dt.AddMinutes(Convert.ToInt32(Jwt_Lifetime_Minutes));
            return ret;
        }
        public async Task<User> AddUser(User item)
        {
            //_contextMongodb.InsertItem(new SysLog()
            //{
            //    TraceIdentifierID = _httpContextAccessor.HttpContext.TraceIdentifier,
            //    Datetime = DateTime.Now.ConvertMiladiToShamsi(),
            //    Method = $"{_httpContextAccessor.HttpContext.Request.Method}/{this.GetType().FullName}/AddUser",
            //    Value = JsonConvert.SerializeObject(item),
            //    Route = _httpContextAccessor.HttpContext.Request.Path,
            //    Type = "User"
            //});
            return await _RepoUser.AddAsync(item);
        }
        public async Task<bool> ExistUser(string UserName)
        {

            return await _RepoUser.GetAll().AnyAsync(w => w.Username == UserName);

        }
        public async Task<User> GetUserByUsername(string UserName)
        {
            return await _RepoUser.Get(w => w.Username == UserName).FirstOrDefaultAsync();
        }
        public async Task<User?> GetUserByUserID(int UserID)
        {
            return await _RepoUser.Get(w => w.ID == UserID)
                    .Include(ti => ti.RolUsers)
                  .ThenInclude(ti => ti.Company)
                .FirstOrDefaultAsync();
        }
        public async Task SetTokenAndDateLogininDB(int UserID, string Token)
        {
            var user = await GetUserByUserID(UserID);
            if (user != null)
            {
                user.Token = Token;
                user.DateLastLogin = DateTime.Now.ConvertMiladiToShamsiByTime();
                await _RepoUser.UpdateAsync(user);
            }
        }
        public async Task<bool> ChangePasswordByMobile(string mobile, string newpassword)
        {
            var user = await GetUserByUsername(mobile);
            if (user == null)
                return false;
            user.Password = newpassword.encrypted();
            return await _RepoUser.UpdateAsync(user);
        }
        public async Task<DashBoardDTO> GetDashBoard(int UserID)
        {
            var user = await GetUserByUserID(UserID);
            DashBoardDTO request = new DashBoardDTO();
            var period = await _RepoPermissionPeriod
                 .Get(w => w.CompanyID == user.RolUsers.First().CompanyID && (!w.IsLocked.HasValue || !w.IsLocked.Value))
                 .Include(inc => inc.CalculationType)
                      .Include(inc => inc.Permission)
                 .ToListAsync();
            foreach (var item in period)
            {
                request.AlistofServices.Add(new ServiceInDashBoardDTO
                {
                    PermissionID = item.PermissionID,
                    PermissionName = item.Permission.Title,
                    CalTypeID = item.CalculationTypeID,
                    CalTypeTitle = item.CalculationType.Title,
                    Total = item.CalculationTypeID == 1 ? item.TotalAmount.ToString() : "",
                    Remaining = item.CalculationTypeID == 1 ? item.RemainingAmount
                    : item.CalculationTypeID == 2 ? 0 : item.RemainingAmount
                });
            }
            request.LastLoginDate = _RepoUser.Get(w => w.ID == UserID).Select(s => s.DateLastLogin).FirstOrDefault();
            if (!string.IsNullOrEmpty(request.LastLoginDate))
                request.LastLoginDate = $"{request.LastLoginDate.Substring(8, 2)}:{request.LastLoginDate.Substring(10, 2)} {request.LastLoginDate.Substring(0, 4)}/{request.LastLoginDate.Substring(4, 2)}/{request.LastLoginDate.Substring(6, 2)}";

            //request.Warning.Add(new AlertDTO { Status = 6, Message = "تست هشدار آبی" });
            //request.Warning.Add(new AlertDTO { Status = 5, Message = "تست هشدار زرد" });

            //if (user.Mobile == user.Username)
            //    request.Warning.Add(new AlertDTO { Status = 0, Message = "موبایل و نام کاربری بهتر است شبیه هم نباشند" });
            if (user.Mobile.encrypted() == user.Password)
                request.Warning.Add(new AlertDTO { Status = 6, Message = "موبایل و کلمه عبور بهتر است شبیه هم نباشند" });
            var Company = user.RolUsers.Where(w => w.CompanyID == user.RolUsers.First().CompanyID).Select(s => s.Company).FirstOrDefault();
            if (Company != null)
            {
                if (/*string.IsNullOrEmpty(Company.Email) || string.IsNullOrEmpty(Company.Phone) || */
                    string.IsNullOrEmpty(Company.EconomicCode) || string.IsNullOrEmpty(Company.UniqeMemory)
                    || string.IsNullOrEmpty(Company.PrivateKey))
                {
                    request.Warning.Add(new AlertDTO { Status = 5, Message = "بهتر است اطلاعات شرکت بروزرسانی شود" });
                }
            }
            var nots = await _servNotification.GetNotifications(UserID);
            if (nots.Any())
                request.Notifications = nots.Select(s => new AlertDTO
                {
                    ViewSize=s.ID.ToString(),
                    Message = s.Message,
                    Status = s.Type,
                    Path = s.Title
                }).ToList();

            LastActivitySevice lastInvoice = new LastActivitySevice();
            #region  LastActivitySevice
            //invoice
            var lastinv = await _RepoInvoice.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted)
                .Include(inc => inc.Customer)
                .Select(s => new LastActivitySevice
                {
                    ID = s.ID,
                    Date = s.InvoiceDate.ShamciToFormatShamci(),
                    Name = s.Customer.FullName,
                    PermissionID = 3,
                    TypeTitle = s.invoiceType.GetEnumDisplayName()
                })
                .OrderByDescending(o => o.ID).FirstOrDefaultAsync();
            if (lastinv != null)
                request.lastActivitiesSevices.Add(lastinv);
            //cod
            var lastcod = await _RepoCODItem.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted)
              .Select(s => new LastActivitySevice
              {
                  ID = s.ID,
                  Date = "",
                  Name = s.Title,
                  PermissionID = 4,
                  TypeTitle = ""
              })
              .OrderByDescending(o => o.ID).FirstOrDefaultAsync();
            if (lastcod != null)
                request.lastActivitiesSevices.Add(lastcod);
            //cus
            var lastcus = await _RepoCus.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted)
              .Select(s => new LastActivitySevice
              {
                  ID = s.ID,
                  Date = "",
                  Name = s.FullName,
                  PermissionID = 8,
                  TypeTitle = ""
              })
              .OrderByDescending(o => o.ID).FirstOrDefaultAsync();
            if (lastcus != null)
                request.lastActivitiesSevices.Add(lastcus);
            #endregion

            List<string> dt = new List<string>();
            for (int i = -7; i < 1; i++)
                dt.Add(DateTime.Now.AddDays(i).ConvertMiladiToShamsi());

            request.MostInvoicedProduct = await _invoiceitemRepo.Get(w => w.invoice.CompanyID == user.RolUsers.First().CompanyID && !w.invoice.IsDeleted
            && ((w.invoice.invoiceType == InvoiceType.Sale || w.invoice.invoiceType == InvoiceType.Repair) && !w.invoice.BillReference.HasValue || (w.invoice.invoiceType == InvoiceType.BackFrmSale))
            && (dt.Contains(w.invoice.InvoiceDate)))
                .Include(inc => inc.cODItem)
                      .GroupBy(g => g.CODID)
                  .Select(s => new IdNameByCount<int>
                  {
                      ID = s.Key,
                      Title = s.Select(s => s.sstt).First(),
                      count = s.Sum(s => s.am)
                  }).OrderByDescending(o => o.count)
           .ToListAsync();

            request.MostInvoicedCustomer = await _RepoInvoice.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted
            && ((w.invoiceType == InvoiceType.Sale || w.invoiceType == InvoiceType.Repair) && !w.BillReference.HasValue || (w.invoiceType == InvoiceType.BackFrmSale))
            && dt.Contains(w.InvoiceDate))
                .Include(inc => inc.Customer)
                      .GroupBy(g => g.CustomerID)
                  .Select(s => new IdNameByCount<int>
                  {
                      ID = s.Key,
                      Title = s.Select(s => s.Customer.FullName).First(),
                      count = s.Count()
                  }).OrderByDescending(o => o.count)
           .ToListAsync();

            var cdate = DateTime.Now.ConvertMiladiToShamsiAndGetYearMonth();


            var sale = await _RepoInvoice.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted
            && ((w.invoiceType == InvoiceType.Sale || w.invoiceType == InvoiceType.Repair) && !w.BillReference.HasValue || (w.invoiceType == InvoiceType.BackFrmSale))
            && w.InvoiceDate.StartsWith(cdate))
                    .Include(inc => inc.invoiceDetails)
              .ThenInclude(inc => inc.cODItem)
                                   .ThenInclude(inc => inc.CODUnit)
                    .Include(inc => inc.pattern)
          .Select(s => s.tbill)
            .ToListAsync();
            request.SaleInMonth = sale.Sum();

            cdate = DateTime.Now.AddMonths(-1).ConvertMiladiToShamsiAndGetYearMonth();

            var lastonthsale = await _RepoInvoice.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted
       && ((w.invoiceType == InvoiceType.Sale || w.invoiceType == InvoiceType.Repair) && !w.BillReference.HasValue || (w.invoiceType == InvoiceType.BackFrmSale))
           && w.InvoiceDate.StartsWith(cdate))
                   .Include(inc => inc.invoiceDetails)
                  .ThenInclude(inc => inc.cODItem)
                                       .ThenInclude(inc => inc.CODUnit)
                       .Include(inc => inc.pattern)
                .Select(s => s.tbill)
               .ToListAsync();

            decimal? total = lastonthsale.Sum();
            decimal? part = request.SaleInMonth;
            //decimal? total = 17892909;
            //decimal? part = request.SaleInMonth;
            if (total.HasValue && total.Value > 0)
            {
                if (!part.HasValue) part = 0;

                request.Salepercent = decimal.Parse(((part.Value / total.Value) * 100).ToString().Split('.')[0]);
            }
           // request.ForSaleChart.AddRange(new List<IdNameByCount<int>>()
                 //{
                 //    new IdNameByCount<int>{ID=0,Title="1",count=10},
                 //       new IdNameByCount<int>{ID=1,Title="2",count=5},
                 //         new IdNameByCount<int>{ID=2,Title="3",count=12},
                 //           new IdNameByCount<int>{ID=3,Title="4",count=21}
                 //});
            var dateTime = DateTime.Now.ConvertMiladiToShamsi();
            for (int i = 1; i <= Convert.ToInt32(dateTime.Substring(6, 2)); i++)
            {
                var sumsale = await _RepoInvoice.Get(w => w.CompanyID == user.RolUsers.First().CompanyID && !w.IsDeleted
                && ((w.invoiceType == InvoiceType.Sale || w.invoiceType == InvoiceType.Repair) && !w.BillReference.HasValue || (w.invoiceType == InvoiceType.BackFrmSale))
                && w.InvoiceDate == dateTime.Substring(0, 4) + dateTime.Substring(4, 2) + i.ToString("00"))
                     .Include(inc => inc.invoiceDetails)
                  .ThenInclude(inc => inc.cODItem)
                                       .ThenInclude(inc => inc.CODUnit)
                       .Include(inc => inc.pattern)
                    .Select(s => s.tbill).ToListAsync();

                request.ForSaleChart.Add(new IdNameByCount<int>
                {
                    count = sumsale.Sum(),
                    ID = i - 1,
                    Title = i.ToString()
                });


            }
            return request;
        }
        public async Task<User> UpdateUser(User user)
        {
            //_contextMongodb.InsertItem(new SysLog()
            //{
            //    TraceIdentifierID = _httpContextAccessor.HttpContext.TraceIdentifier,
            //    Datetime = DateTime.Now.ConvertMiladiToShamsi(),
            //    Method = $"{_httpContextAccessor.HttpContext.Request.Method}/{this.GetType().FullName}/UpdateUser",
            //    Value = JsonConvert.SerializeObject(user),
            //    Route = _httpContextAccessor.HttpContext.Request.Path,
            //    Type = "User"
            //});
            return await _RepoUser.UpdateByObjAsync(user);
        }
        public async Task<bool> ChangePassword(string newPass, int UserID)
        {
            var user = await GetUserByUserID(UserID);
            if (user == null)
                return false;
            user.Password = newPass.encrypted();
            return await _RepoUser.UpdateAsync(user);
        }
        public async Task<bool> ChangeUserName(string newUserName, int UserID)
        {
            var user = await GetUserByUserID(UserID);
            if (user == null)
                return false;



            if (user != null)
            {
                using var transaction = await _RepoCompany._dbContext.Database.BeginTransactionAsync();
                var company = user.RolUsers.First().Company;
                company.Mobile = newUserName;
                if (await _RepoCompany.UpdateAsync(company))
                {
                    user.Username = newUserName;
                    user.Mobile = newUserName;
                    if (await _RepoUser.UpdateAsync(user))
                    {
                        await transaction.CommitAsync();
                        return true;
                    }
                    else
                    {
                        await transaction.RollbackAsync();
                        return false;
                    }

                }
                //  return Ok(await _servCompany.AddORUpdateCompanyBoolResult(company));
            }
            return false;



        }
        public async Task<bool> PermissionChangePassword(string oldPass, int UserID)
        {
            return await _RepoUser.GetAll().AnyAsync(w => w.ID == UserID && w.Password == oldPass.encrypted() && w.IsActive);

        }
        public async Task<bool> ExistMobileInUser(string mobile, bool IsActive = true)
        {
            var resquest = _RepoUser.Get(w => w.Mobile == mobile || w.Username == mobile);
            if (IsActive)
                resquest = resquest.Where(w => w.IsActive);

            return await resquest.AnyAsync();
        }
        //--------internal
        private async Task<string> GetJwt_Lifetime_Minutes()
        {
            string Jwt_Lifetime_Minutes = "60";
            try
            {
                Jwt_Lifetime_Minutes = _configuration["Fixedvalues:Jwt_Lifetime_Minutes"].ToString();
                if (string.IsNullOrEmpty(Jwt_Lifetime_Minutes))
                    Jwt_Lifetime_Minutes = "60";
            }
            catch (Exception ex)
            {
                //SysLog log = new SysLog()
                //{
                //    TraceIdentifierID = _httpContextAccessor.HttpContext.TraceIdentifier,
                //    Datetime = DateTime.Now.ConvertMiladiToShamsi(),
                //    Method = $"{_httpContextAccessor.HttpContext.Request.Method}/{this.GetType().FullName}/CerateToken",
                //    Value = ex.Message,
                //    Route = _httpContextAccessor.HttpContext.Request.Path,
                //    Type = "catch"
                //};
                //_contextMongodb.InsertItem(log);
                Jwt_Lifetime_Minutes = "60";
                //To DO
            }

            return Jwt_Lifetime_Minutes;
        }
        private async Task<string> CerateToken(int UserId, string UserName, string Jwt_Lifetime_Minutes)
        {
            #region CreateToken
            var securityKey = new SymmetricSecurityKey(
               Encoding.ASCII.GetBytes(Fixedvalues.SecretForKey)
               );
            var signingCredentials = new SigningCredentials(
                securityKey, SecurityAlgorithms.HmacSha256
                );
            var claimsForToken = new List<Claim>();
            claimsForToken.Add(new Claim("UserID", UserId.ToString()));
            claimsForToken.Add(new Claim(ClaimTypes.NameIdentifier, UserName));

            var jwtSecurityToke = new JwtSecurityToken(
                Fixedvalues.Issuer, Fixedvalues.Audience, claimsForToken,
                DateTime.Now, DateTime.Now.AddMinutes(Convert.ToInt32(Jwt_Lifetime_Minutes)), signingCredentials);


            string Token = new JwtSecurityTokenHandler()
                 .WriteToken(jwtSecurityToke);
            await SetTokenAndDateLogininDB(UserId, Token);
            //_contextMongodb.InsertItem(new SysLog()
            //{
            //    TraceIdentifierID = _httpContextAccessor.HttpContext.TraceIdentifier,
            //    Datetime = DateTime.Now.ConvertMiladiToShamsi(),
            //    Method = $"{_httpContextAccessor.HttpContext.Request.Method}/{this.GetType().FullName}/CerateToken",
            //    Value = UserId + " " + UserName+"=> "+Token,
            //    Route = _httpContextAccessor.HttpContext.Request.Path,
            //    Type = "User"
            //});
            return Token;
            #endregion

        }

    }
}
