namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Dapper;
    using Domain.Configurations;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.Library.Enums;
    using Shared.UserModels.Filters;
    using Hims.Domain.Helpers;
    using System.Linq;

    /// <inheritdoc />
    public class UserServices : IUserService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <summary>
        /// The sha helper.
        /// </summary>
        private readonly IShaHelper shaHelper;

        /// <summary>
        /// The running environment.
        /// </summary>
        private readonly IRunningEnvironment runningEnvironment;

        /// <inheritdoc cref="IAmazonS3Configuration" />
        public UserServices(IUnitOfWork unitOfWork, IShaHelper shaHelper, IRunningEnvironment runningEnvironment)
        {
            this.unitOfWork = unitOfWork;
            this.shaHelper = shaHelper;
            this.runningEnvironment = runningEnvironment;   
        }

        /// <inheritdoc />
        public Task<UserModel> FindAsync(int userId)
        {
            var where = $@" WHERE usr.""UserId"" = '{userId}'";

            var query = $@"SELECT usr.""UserId"", usr.""FullName"", usr.""CreatedDate"", usr.""CountryId"", usr.""DateOfBirth"", usr.""Gender"", usr.""Email"", usr.""Active"", usr.""Mobile"",usr.""RoleId"", rl.""RoleName"", usr.""FirstName"", usr.""MiddleName"", usr.""LastName"", usr.""ProviderLocationId"", pra.""FullName"" AS ""PracticeName"",acc.""UserName"",
                                (CASE WHEN usr.""ThumbnailUrl"" IS NOT NULL THEN CONCAT('{this.runningEnvironment.CurrentEnvironment}','/',  usr.""Guid"", '/', usr.""ThumbnailUrl"") ELSE NULL END) AS ""ThumbnailUrl"",
                                (CASE WHEN usr.""ProfileImageUrl"" IS NOT NULL THEN CONCAT('{this.runningEnvironment.CurrentEnvironment}', '/', usr.""Guid"", '/', usr.""ProfileImageUrl"") ELSE NULL END) AS ""ProfileImageUrl"",
                                cun.""CountryName"", cun.""CountryCode"", cun.""ISOCode"",
                                pra.""FullName"" AS ""PracticeName"", CONCAT(c.""CityName"" , ', ', s.""StateName"", ' ', pral.""Zipcode"") AS ""ProviderLocation"", pr.""FullName"" AS ""ProviderName""
                                FROM ""User"" usr
                                LEFT JOIN ""Account"" acc ON acc.""ReferenceId"" = usr.""UserId""
                                LEFT JOIN ""Country"" cun ON cun.""CountryId"" = usr.""CountryId"" AND cun.""Active"" IS TRUE
                                LEFT JOIN ""ProviderLocation"" prl ON prl.""ProviderLocationId"" = usr.""ProviderLocationId"" AND prl.""Active"" IS TRUE
                                LEFT JOIN ""PracticeLocation"" pral ON pral.""LocationId"" = prl.""LocationId"" AND pral.""Active"" IS true
                                left join ""City"" c on c.""CityId"" = pral.""CityId"" 
                                left join ""State"" s on s.""StateId"" = pral.""StateId"" 
                                LEFT JOIN ""Practice"" pra ON pra.""PracticeId"" = pral.""PracticeId"" AND pra.""Active"" IS TRUE
								LEFT JOIN ""Provider"" pr ON pr.""ProviderId"" = prl.""ProviderId"" AND pr.""Active"" IS TRUE
								LEFT JOIN ""Role"" rl ON rl.""RoleId"" = usr.""RoleId"" AND rl.""Active"" IS TRUE
                                {where}";

            return this.unitOfWork.Current.QueryFirstOrDefaultAsync<UserModel>(query);
        }

        /// <inheritdoc />
        public async Task<IEnumerable<UserModel>> FetchAsync(UserFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (!string.IsNullOrEmpty(model.FullName))
            {
                where += $@" AND usr.""FullName"" ILIKE '%{model.FullName}%'";
            }

            if (model.ProviderId != null)
            {
                where += $@" AND pr.""ProviderId"" = {model.ProviderId}";
            }

            if (!string.IsNullOrEmpty(model.Email))
            {
                where += $@" AND usr.""Email"" ILIKE '%{model.Email}%'";
            }

            if (!string.IsNullOrEmpty(model.Mobile))
            {
                where += $@" AND usr.""Mobile"" ILIKE '%{model.Mobile}%'";
            }
            if (!string.IsNullOrEmpty(model.Username))
            {
                where += $@" AND act.""UserName"" ILIKE '%{model.Username}%'";
            }
            if (!string.IsNullOrEmpty(model.CreatedDate))
            {
                where += $@" AND usr.""CreatedDate""::DATE = '{model.CreatedDate}'::DATE ";
            }

            if (model.ProviderLocationId != null)
            {
                where += $@" AND usr.""ProviderLocationId"" = '{model.ProviderLocationId}'";
            }

            if (model.RoleId != null)
            {
                where += $@" AND usr.""RoleId"" = '{model.RoleId}'";
            }

            if (model.CountryId != null)
            {
                where += $@" AND usr.""CountryId"" = '{model.CountryId}'";
            }

            if (model.Active != null)
            {
                where += $@" AND usr.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")}";
            }
            if (model.LocationIds != null)
            {
                where += $@" AND LAM.""LocationId"" = {model.LocationIds}";
            }
            if (!string.IsNullOrEmpty(model.BulkUserIds))
            {
                where += $@" and usr.""UserId"" in ({model.BulkUserIds})";
            }
                        var query = $@"Select COUNT(*) OVER () AS ""TotalItems"",* from (SELECT distinct usr.""UserId"",act.""UserName"", usr.""FullName"", usr.""CreatedDate"",usr.""ModifiedDate"", usr.""CountryId"", 
                                usr.""DateOfBirth"", usr.""Gender"", usr.""Email"", usr.""Active"", usr.""Mobile"",usr.""RoleId"", rl.""RoleName"", usr.""FirstName"", usr.""MiddleName"", usr.""LastName"",
                                usr.""AddressLine"",act.""FinanceBackground"",act.""ExcelDownload"",act.""RestrictUserLogin"",usr.""EmployeeCode"",
                                usr.""ProviderLocationId"", pra.""FullName"" AS ""PracticeName"",
                                (CASE WHEN usr.""ThumbnailUrl"" IS NOT NULL THEN CONCAT('{this.runningEnvironment.CurrentEnvironment}','/',  usr.""Guid"", '/', usr.""ThumbnailUrl"") ELSE NULL END) AS ""ThumbnailUrl"",
                                (CASE WHEN usr.""ProfileImageUrl"" IS NOT NULL THEN CONCAT('{this.runningEnvironment.CurrentEnvironment}', '/', usr.""Guid"", '/', usr.""ProfileImageUrl"") ELSE NULL END) AS ""ProfileImageUrl"",
                                cun.""CountryName"", cun.""CountryCode"", cun.""ISOCode"", act.""IsLocked"",act.""AccountId"",
                                pra.""FullName"" AS ""PracticeName"", L.""NameLoc"" AS ""ProviderLocation"", pr.""FullName"" AS ""ProviderName""
                                    , CA.""FullName"" ""CreatedByName"",CR.""RoleName"" ""CreatedByRole"",
		                             MA.""FullName"" ""ModifiedByName"",MR.""RoleName"" ""ModifiedByRole"",
                                    (Select string_agg(R.""RetailPharmacyId""::character varying, ',') as ""RetailIds"" 
                                       from ""RetailPharmacy"" S
                                         join ""PharmacyRetailUser"" R ON R.""RetailPharmacyId"" = S.""RetailPharmacyId"" and R.""AccountId"" = act.""AccountId"")
	                                ,(Select  string_agg(S.""RetailName"", ',') as ""RetailNames""
                                        from ""RetailPharmacy"" S
                                        join ""PharmacyRetailUser"" R ON R.""RetailPharmacyId"" = S.""RetailPharmacyId"" and R.""AccountId"" = act.""AccountId"")
                                    ,(Select  string_agg(l.""NameLoc"", ', ') as ""LocationNames""
                                        from ""LocationAccountMap"" la
                                        join ""Location"" l ON l.""LocationId"" = la.""LocationId"" WHERE la.""AccountId"" = act.""AccountId""),
	                                (Select  string_agg(l.""LocationId""::text, ', ') as ""LocationIdentifiers""
                                        from ""LocationAccountMap"" la
                                        join ""Location"" l ON l.""LocationId"" = la.""LocationId"" WHERE la.""AccountId"" = act.""AccountId"")	                             
                                FROM ""User"" usr
                                JOIN ""Account"" act ON act.""ReferenceId"" = usr.""UserId"" AND act.""RoleId"" IN ( select ""RoleId"" from ""Role"" where ""RoleId"" not in ({(int)Roles.Provider},{(int)Roles.Patient}))   
                                JOIN ""LocationAccountMap"" LAM  ON LAM.""AccountId"" = act.""AccountId""
                                LEFT JOIN ""Country"" cun ON cun.""CountryId"" = usr.""CountryId"" AND cun.""Active"" IS TRUE
                                LEFT JOIN ""ProviderLocation"" prl ON prl.""ProviderLocationId"" = usr.""ProviderLocationId"" AND prl.""Active"" IS true
                                left join ""Location"" L on L.""LocationId"" = prl.""LocationId""                                
                                LEFT JOIN ""Practice"" pra ON pra.""PracticeId"" = L.""PracticeId"" AND pra.""Active"" IS TRUE                                
                                LEFT JOIN ""Provider"" pr ON pr.""ProviderId"" = prl.""ProviderId"" AND pr.""Active"" IS TRUE
								LEFT JOIN ""Role"" rl ON rl.""RoleId"" = usr.""RoleId"" AND rl.""Active"" IS TRUE
                                JOIN ""Account"" CA on CA.""AccountId"" = usr.""CreatedBy""
                                JOIN ""Role"" CR on CR.""RoleId"" = CA.""RoleId""
                                LEFT JOIN ""Account"" MA on MA.""AccountId"" = usr.""ModifiedBy""
                                LEFT JOIN ""Role"" MR on MR.""RoleId"" = MA.""RoleId""    
                                {where} Order by usr.""CreatedDate"" DESC) A";

            if (model.PageIndex <= 0)
            {
                return await this.unitOfWork.Current.QueryAsync<UserModel>(query);
            }

            model.PageIndex -= 1;
            query += " LIMIT " + model.PageSize + " offset " + (model.PageIndex * model.PageSize);
            var users = await this.unitOfWork.Current.QueryAsync<UserModel>(query);
            //var userModels = users.ToList();
            //try
            //{

            //    foreach (var user in userModels.OrderBy(m => m.UserId))
            //    {
            //        if ((!string.IsNullOrEmpty(user.ProfileImageUrl)) && (!string.IsNullOrEmpty(user.ThumbnailUrl)))
            //        {

            //            try
            //            {
            //                if ((user.ProfileImageUrl.StartsWith("ftp")) && (user.ThumbnailUrl.StartsWith("ftp")))
            //                {
            //                    user.ProfileImageUrl = await this.documentHelper.FetchImageBase64(user.UserId, user.ProfileImageUrl);
            //                    user.ThumbnailUrl = await this.documentHelper.FetchImageBase64(user.UserId, user.ThumbnailUrl);                              
            //                }
            //            }
            //            catch (Exception)
            //            {
            //                // ignore
            //            }
            //        }
            //    }
            //}
            //catch (Exception)
            //{
            //    // ignore
            //}

            return users;
        }

        /// <inheritdoc />
        public async Task<Tuple<int, Guid?>> AddAsync(UserModel model)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
            {
                int checkIf;
                if (!string.IsNullOrEmpty(model.Email))
                {
                    checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""UserId"") FROM ""User"" WHERE UPPER(""Email"") = '{model.Email.ToUpper()}'", null, transaction);
                    if (checkIf > 0)
                    {
                        return new Tuple<int, Guid?>(-1, null);
                    }

                    if (model.ProviderLocationId != null && model.ProviderLocationId > 0)
                    {
                        checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderId"") FROM ""Provider"" WHERE UPPER(""Email"") = '{model.Email.ToUpper()}'", null, transaction);
                        if (checkIf > 0)
                        {
                            return new Tuple<int, Guid?>(-1, null);
                        }
                    }
                }

                if (!string.IsNullOrEmpty(model.Mobile))
                {
                    checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""UserId"") FROM ""User"" WHERE ""Mobile"" = '{model.Mobile}'", null, transaction);
                    if (checkIf > 0)
                    {
                        return new Tuple<int, Guid?>(-2, null);
                    }

                    if (model.ProviderLocationId != null && model.ProviderLocationId > 0)
                    {
                        checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderId"") FROM ""Provider"" WHERE ""Mobile"" = '{model.Mobile}'", null, transaction);
                        if (checkIf > 0)
                        {
                            return new Tuple<int, Guid?>(-2, null);
                        }
                    }
                }

                var user = new User
                {

                    Active = true,
                    RoleId = model.RoleId,
                    ProviderLocationId = model.ProviderLocationId,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.UtcNow,
                    Email = model.Email,
                    FirstName = model.FirstName,
                    LastName = model.LastName,
                    FullName = model.FullName,
                    MiddleName = model.MiddleName,
                    Mobile = model.Mobile,
                    CountryId = model.CountryId,
                    DateOfBirth = model.DateOfBirth,
                    Gender = model.Gender,
                    ModifiedBy = null,
                    ModifiedDate = null,
                    Guid = Guid.NewGuid()
                };


                user.UserId = await this.unitOfWork.Users.InsertAsync(user, transaction);
                if (user.UserId == 0)
                {
                    transaction.Rollback();
                    return new Tuple<int, Guid?>(0, null);
                }

                var passwordKey = CoreFilter.Random(10);
                var existingAccount = await this.unitOfWork.Accounts.FindAsync(m => m.Email == model.Email && m.Mobile == model.Mobile && m.CountryId == model.CountryId, transaction);
                if (existingAccount != null && existingAccount.AccountId > 0)
                {
                    passwordKey = existingAccount.SaltKey;
                }



                var account = new Account
                {
                    RoleId = model.RoleId,
                    ReferenceId = user.UserId,
                    SaltKey = passwordKey,
                    CreatedDate = DateTime.UtcNow,
                    FullName = user.FullName,
                    CountryId = user.CountryId,
                    Email = user.Email,
                    Mobile = user.Mobile,
                    Active = true,
                    Guid = user.Guid,
                    CreatedBy = model.CreatedBy,
                    FailedLoginAttempts = 0,
                    IsAgreed = null,
                    IsLocked = false,
                    LastFailedLoginDate = null,
                    LastLoginDate = null,
                    ModifiedBy = null,
                    ModifiedDate = null,
                    UserName = model.UserName
                };
                account.AccountId = await this.unitOfWork.Accounts.InsertAsync(account, transaction);
                if (account.AccountId == 0)
                {
                    transaction.Rollback();
                    return new Tuple<int, Guid?>(0, null);
                }

                foreach (var locationId in model.LocationIds)
                {
                    var location = new LocationAccountMap
                    {
                        AccountId = account.AccountId,
                        LocationId = locationId
                    };
                    var locationInsertId = await this.unitOfWork.LocationAccountMap.InsertAsync(location, transaction);
                    if (locationInsertId == 0)
                    {
                        transaction.Rollback();
                        return new Tuple<int, Guid?>(0, null);
                    }
                }

                if (!string.IsNullOrEmpty(model.Password))
                {
                    var passwordHash = this.shaHelper.GenerateHash(model.Password, account.SaltKey);
                    var query = new AccountsCredential(account, passwordHash).Query;

                    var inserted = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                    if (inserted == 0)
                    {
                        transaction.Rollback();
                        return new Tuple<int, Guid?>(0, null);
                    }
                }

                //if (model.RetailIds != null)
                //{
                //    var ids = model.RetailIds.Split(',');

                //    if (account.AccountId > 0)
                //    {
                //        foreach (var id in ids)
                //        {
                //            var retailUser = new PharmacyRetailUser
                //            {
                //                Active = true,
                //                CreatedDate = DateTime.UtcNow.AddMinutes(330),
                //                CreatedBy = (int)model.CreatedBy,
                //                AccountId = account.AccountId,
                //                RetailPharmacyId = Convert.ToInt32(id)
                //            };
                //            await this.unitOfWork.PharmacyRetailUsers.InsertAsync(retailUser, transaction);
                //        }
                //    }
                //}
                transaction.Commit();
                return new Tuple<int, Guid?>(account.AccountId, account.Guid);
            }
        }

        /// <inheritdoc />
        public async Task<int> UpdateAsync(UserModel model)
        {
            try
            {
                using var transaction = this.unitOfWork.BeginTransaction();
                int checkIf;
                if (!string.IsNullOrEmpty(model.Email))
                {
                    checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""UserId"") FROM ""User"" WHERE UPPER(""Email"") = '{model.Email.ToUpper()}' AND ""UserId"" <> '{model.UserId}'", null, transaction);
                    if (checkIf > 0)
                    {
                        return -1;
                    }

                    if (model.ProviderLocationId != null && model.ProviderLocationId > 0)
                    {
                        checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderId"") FROM ""Provider"" WHERE UPPER(""Email"") = '{model.Email.ToUpper()}'", null, transaction);
                        if (checkIf > 0)
                        {
                            return -1;
                        }
                    }
                }

                if (!string.IsNullOrEmpty(model.Mobile))
                {
                    checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""UserId"") FROM ""User"" WHERE ""Mobile"" = '{model.Mobile}' AND ""UserId"" <> '{model.UserId}'", null, transaction);
                    if (checkIf > 0)
                    {
                        return -2;
                    }

                    if (model.ProviderLocationId != null && model.ProviderLocationId > 0)
                    {
                        checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderId"") FROM ""Provider"" WHERE ""Mobile"" = '{model.Mobile}'", null, transaction);
                        if (checkIf > 0)
                        {
                            return -2;
                        }
                    }
                }

                var user = await this.unitOfWork.Users.FindAsync(m => m.UserId == model.UserId);
                var roleId = user.RoleId;

                user.RoleId = model.RoleId;
                user.ProviderLocationId = model.ProviderLocationId;
                user.ModifiedBy = model.ModifiedBy;
                user.ModifiedDate = DateTime.UtcNow.AddMinutes(330);
                user.FirstName = model.FirstName;
                user.MiddleName = model.MiddleName;
                user.LastName = model.LastName;
                user.FullName = model.FullName;
                user.Email = model.Email;
                user.Mobile = model.Mobile;
                user.Gender = model.Gender;
                user.DateOfBirth = model.DateOfBirth;
                user.EmployeeCode = model.EmployeeCode;
                
                if (!string.IsNullOrEmpty(model.Base64ProfileImage))
                {
                    user.ProfileImageUrl = model.ProfileImageUrl;
                    user.ThumbnailUrl = model.ThumbnailUrl;
                }
                               
                var updated = await this.unitOfWork.Users.UpdateAsync(user, transaction);
                if (updated == 0)
                {
                    transaction.Rollback();
                    return 0;
                }

                var account = await this.unitOfWork.Accounts.FindAsync(m => m.RoleId == roleId && m.ReferenceId == user.UserId, transaction);
                if (account == null || account.AccountId == 0)
                {
                    transaction.Rollback();
                    return 0;
                }

                if (account.Email != user.Email || account.Mobile != user.Mobile || account.CountryId != user.CountryId || account.FullName != user.FullName || account.RoleId != user.RoleId || account.UserName != model.UserName || account.ExcelDownload != model.ExcelDownload || account.RestrictUserLogin != model.RestrictUserLogin || account.FinanceBackground != model.FinanceBackground)
                {
                    account.Email = user.Email;
                    account.Mobile = user.Mobile;
                    account.CountryId = user.CountryId;
                    account.FullName = user.FullName;
                    account.RoleId = user.RoleId;
                    account.UserName = model.UserName;
                    account.ExcelDownload = model.ExcelDownload;
                    account.RestrictUserLogin = model.RestrictUserLogin;
                    account.FinanceBackground = model.FinanceBackground;
                    updated = await this.unitOfWork.Accounts.UpdateAsync(account, transaction);
                    if (updated == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }

                if (model.RetailIds != null)
                {
                    var receivedRetailIds = model.RetailIds.Split(',');

                    var query = $@"DELETE from ""PharmacyRetailUser"" where ""AccountId""= {account.AccountId} ";

                    var res = await this.unitOfWork.Current.QuerySingleOrDefaultAsync(query, transaction);
                    if (!String.IsNullOrEmpty(receivedRetailIds[0]))
                    {
                        if (account.AccountId > 0)
                        {
                            foreach (var id in receivedRetailIds)
                            {

                                var retailUser = new PharmacyRetailUser
                                {
                                    Active = true,
                                    CreatedDate = DateTime.UtcNow.AddMinutes(330),
                                    CreatedBy = (int)model.ModifiedBy,
                                    AccountId = account.AccountId,
                                    RetailPharmacyId = Convert.ToInt32(id)
                                };
                                await this.unitOfWork.PharmacyRetailUsers.InsertAsync(retailUser, transaction);
                            }
                        }
                    }
                }

                try
                {
                    var deleteQuerry = $@" Delete from ""LocationAccountMap"" where ""AccountId""= {account.AccountId}";
                    var res = await this.unitOfWork.Current.ExecuteAsync(deleteQuerry, transaction);
                    if (res < 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }
                catch (Exception)
                {
                    transaction.Rollback();
                    return -3;

                }
                foreach (var locationId in model.LocationIds)
                {
                    var location = new LocationAccountMap
                    {
                        AccountId = account.AccountId,
                        LocationId = locationId
                    };
                    var locationInsertId = await this.unitOfWork.LocationAccountMap.InsertAsync(location, transaction);
                    if (locationInsertId == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }

                transaction.Commit();
                return updated;
            }
            catch (Exception e)
            {

            }
            return 0;
        }

        /// <inheritdoc />
        public Task<int> UpdateImageUrlsAsync(UserModel model, Guid guid)
        {
            var query = $@"UPDATE ""User"" SET ""ProfileImageUrl"" = '{model.ProfileImageUrl}', ""ThumbnailUrl"" = '{model.ThumbnailUrl}' WHERE ""Guid""= '{guid}'";
            return this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public async Task<int> ModifyStatusAsync(int userId, bool status, int modifiedBy)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
            {
                var query = $@"UPDATE ""User"" SET ""Active"" = {status}, ""ModifiedBy"" = {modifiedBy}, ""ModifiedDate"" = NOW() AT TIME ZONE 'UTC'  WHERE ""UserId"" = {userId}";
                var updated = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                if (updated == 0)
                {
                    transaction.Rollback();
                    return 0;
                }

                query = $@"UPDATE ""Account"" SET ""Active"" = {status}, ""ModifiedBy"" = {modifiedBy}, ""ModifiedDate"" = NOW() AT TIME ZONE 'UTC'  WHERE ""ReferenceId"" = {userId} AND ""RoleId"" Not IN ({(int)Roles.Patient}) ;";
                updated = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                if (updated == 0)
                {
                    transaction.Rollback();
                    return 0;
                }

                transaction.Commit();
                return updated;
            }
        }

        /// <inheritdoc />
        public async Task<int> LockedStatusAsync(UserModel model)
        {
            try
            {
                using (var transaction = this.unitOfWork.BeginTransaction())
                {
                    var query = $@"SELECT * FROM ""Account"" WHERE ""RoleId"" IN (Select ""RoleId"" from ""Role"" where ""RoleId"" not in ({(int)Roles.Provider},{(int)Roles.Patient})) AND ""ReferenceId"" = {model.UserId} ";
                    var account = await this.unitOfWork.Current.QueryFirstAsync<Account>(query);

                    if (account == null || account.AccountId == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }

                    account.IsLocked = Convert.ToBoolean(model.IsLocked);
                    account.ModifiedBy = model.ModifiedBy;
                    account.ModifiedDate = DateTime.UtcNow;

                    var updated = await this.unitOfWork.Accounts.UpdateAsync(account, transaction);
                    if (updated == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }

                    transaction.Commit();
                    return updated;
                }
            }
            catch (Exception e)
            {
                throw;
            }

        }

        public async Task<string> FindFullNameByUserId(int userId)
        {
            var query = $@"SELECT ""FullName"" FROM ""User"" WHERE ""UserId"" = {userId}";
            var response = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(query);
            return response;
        }


        /// <inheritdoc />
        public async Task<Tuple<int, int, Guid?>> AddFtpAsync(UserModel model)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
            {
                int checkIf;
                if (!string.IsNullOrEmpty(model.Email))
                {
                    checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""UserId"") FROM ""User"" WHERE UPPER(""Email"") = '{model.Email.ToUpper()}'", null, transaction);
                    if (checkIf > 0)
                    {
                        return new Tuple<int, int, Guid?>(-1, checkIf, null);
                    }

                    if (model.ProviderLocationId != null && model.ProviderLocationId > 0)
                    {
                        checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderId"") FROM ""Provider"" WHERE UPPER(""Email"") = '{model.Email.ToUpper()}'", null, transaction);
                        if (checkIf > 0)
                        {
                            return new Tuple<int, int, Guid?>(-1, checkIf, null);
                        }
                    }
                }

                if (!string.IsNullOrEmpty(model.Mobile))
                {
                    checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""UserId"") FROM ""User"" WHERE ""Mobile"" = '{model.Mobile}'", null, transaction);
                    if (checkIf > 0)
                    {
                        return new Tuple<int, int, Guid?>(-2, checkIf, null);
                    }

                    if (model.ProviderLocationId != null && model.ProviderLocationId > 0)
                    {
                        checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ProviderId"") FROM ""Provider"" WHERE ""Mobile"" = '{model.Mobile}'", null, transaction);
                        if (checkIf > 0)
                        {
                            return new Tuple<int, int, Guid?>(-2, checkIf, null);
                        }
                    }
                }
               
                    var user = new User
                    {
                        Active = true,
                        RoleId = model.RoleId,
                        ProviderLocationId = model.ProviderLocationId,
                        CreatedBy = model.CreatedBy,
                        CreatedDate = DateTime.Now,
                        Email = model.Email,
                        FirstName = model.FirstName,
                        LastName = model.LastName,
                        FullName = model.FullName,
                        MiddleName = model.MiddleName,
                        Mobile = model.Mobile,
                        CountryId = model.CountryId,
                        DateOfBirth = model.DateOfBirth,
                        Gender = model.Gender,
                        ModifiedBy = null,
                        ModifiedDate = null,
                        Guid = Guid.NewGuid(),
                        AddressLine = model.AddressLine,
                        EmployeeCode = model.EmployeeCode
                    };
                    user.UserId = await this.unitOfWork.Users.InsertAsync(user, transaction);
                    if (user.UserId == 0)
                    {
                        transaction.Rollback();
                        return new Tuple<int, int, Guid?>(0, 0, null);
                    }
                    var passwordKey = CoreFilter.Random(10);
                    var existingAccount = await this.unitOfWork.Accounts.FindAsync(m => m.Email == model.Email && m.Mobile == model.Mobile && m.CountryId == model.CountryId, transaction);
                    if (existingAccount != null && existingAccount.AccountId > 0)
                    {
                        passwordKey = existingAccount.SaltKey;
                    }
                    var account = new Account
                    {
                        RoleId = model.RoleId,
                        ReferenceId = user.UserId,
                        SaltKey = passwordKey,
                        CreatedDate = DateTime.Now,
                        FullName = user.FullName,
                        CountryId = user.CountryId,
                        Email = user.Email,
                        Mobile = user.Mobile,
                        Active = true,
                        Guid = user.Guid,
                        CreatedBy = model.CreatedBy,
                        FailedLoginAttempts = 0,
                        IsAgreed = null,
                        IsLocked = false,
                        LastFailedLoginDate = null,
                        LastLoginDate = null,
                        ModifiedBy = null,
                        ModifiedDate = null,
                        UserName = model.UserName,
                        ExcelDownload = model.ExcelDownload,
                        RestrictUserLogin = model.RestrictUserLogin,
                        FinanceBackground = model.FinanceBackground,
                    };
                    account.AccountId = await this.unitOfWork.Accounts.InsertAsync(account, transaction);
                    if (account.AccountId == 0)
                    {
                        transaction.Rollback();
                        return new Tuple<int, int, Guid?>(0, 0, null);
                    }

                    foreach (var locationId in model.LocationIds)
                    {
                        var location = new LocationAccountMap
                        {
                            AccountId = account.AccountId,
                            LocationId = locationId
                        };
                        var locationInsertId = await this.unitOfWork.LocationAccountMap.InsertAsync(location, transaction);
                        if (locationInsertId == 0)
                        {
                            transaction.Rollback();
                            return new Tuple<int, int, Guid?>(0, 0, null);
                        }
                    }

                    if (!string.IsNullOrEmpty(model.Password))
                    {
                        var passwordHash = this.shaHelper.GenerateHash(model.Password, account.SaltKey);
                        var query = new AccountsCredential(account, passwordHash).Query;

                        var inserted = await this.unitOfWork.Current.ExecuteAsync(query, transaction);
                        if (inserted == 0)
                        {
                            transaction.Rollback();
                            return new Tuple<int, int, Guid?>(0, 0, null);
                        }
                    }
                    transaction.Commit();
                    return new Tuple<int, int, Guid?>(account.AccountId, user.UserId, account.Guid);              
            }
        }
        /// <inheritdoc />
        public async Task<IEnumerable<UserModel>> FetchAllAsync(UserFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            
            if (!string.IsNullOrEmpty(model.BulkUserIds))
            {
                where += $@" and usr.""UserId"" in ({model.BulkUserIds})";
            }
            
            var query = $@"Select COUNT(*) OVER () AS ""TotalItems"",* from (SELECT distinct usr.""UserId"",act.""UserName"", usr.""FullName"", usr.""CreatedDate"",usr.""ModifiedDate"", usr.""CountryId"", 
                                usr.""DateOfBirth"", usr.""Gender"", usr.""Email"", usr.""Active"", usr.""Mobile"",usr.""RoleId"", rl.""RoleName"", usr.""FirstName"", usr.""MiddleName"", usr.""LastName"",
                                usr.""AddressLine"",act.""FinanceBackground"",act.""ExcelDownload"",act.""RestrictUserLogin"",usr.""EmployeeCode"",
                                usr.""ProviderLocationId"", pra.""FullName"" AS ""PracticeName"",
                                (CASE WHEN usr.""ThumbnailUrl"" IS NOT NULL THEN CONCAT('{this.runningEnvironment.CurrentEnvironment}','/',  usr.""Guid"", '/', usr.""ThumbnailUrl"") ELSE NULL END) AS ""ThumbnailUrl"",
                                (CASE WHEN usr.""ProfileImageUrl"" IS NOT NULL THEN CONCAT('{this.runningEnvironment.CurrentEnvironment}', '/', usr.""Guid"", '/', usr.""ProfileImageUrl"") ELSE NULL END) AS ""ProfileImageUrl"",
                                cun.""CountryName"", cun.""CountryCode"", cun.""ISOCode"", act.""IsLocked"",act.""AccountId"",
                                pra.""FullName"" AS ""PracticeName"", L.""NameLoc"" AS ""ProviderLocation"", pr.""FullName"" AS ""ProviderName""
                                    , CA.""FullName"" ""CreatedByName"",CR.""RoleName"" ""CreatedByRole"",
		                             MA.""FullName"" ""ModifiedByName"",MR.""RoleName"" ""ModifiedByRole"",
                                    (Select string_agg(R.""RetailPharmacyId""::character varying, ',') as ""RetailIds"" 
                                       from ""RetailPharmacy"" S
                                         join ""PharmacyRetailUser"" R ON R.""RetailPharmacyId"" = S.""RetailPharmacyId"" and R.""AccountId"" = act.""AccountId"")
	                                ,(Select  string_agg(S.""RetailName"", ',') as ""RetailNames""
                                        from ""RetailPharmacy"" S
                                        join ""PharmacyRetailUser"" R ON R.""RetailPharmacyId"" = S.""RetailPharmacyId"" and R.""AccountId"" = act.""AccountId"")
                                    ,(Select  string_agg(l.""NameLoc"", ', ') as ""LocationNames""
                                        from ""LocationAccountMap"" la
                                        join ""Location"" l ON l.""LocationId"" = la.""LocationId"" WHERE la.""AccountId"" = act.""AccountId""),
	                                (Select  string_agg(l.""LocationId""::text, ', ') as ""LocationIdentifiers""
                                        from ""LocationAccountMap"" la
                                        join ""Location"" l ON l.""LocationId"" = la.""LocationId"" WHERE la.""AccountId"" = act.""AccountId"")	                             
                                FROM ""User"" usr
                                JOIN ""Account"" act ON act.""ReferenceId"" = usr.""UserId""   
                                JOIN ""LocationAccountMap"" LAM  ON LAM.""AccountId"" = act.""AccountId""
                                LEFT JOIN ""Country"" cun ON cun.""CountryId"" = usr.""CountryId"" AND cun.""Active"" IS TRUE
                                LEFT JOIN ""ProviderLocation"" prl ON prl.""ProviderLocationId"" = usr.""ProviderLocationId"" AND prl.""Active"" IS true
                                left join ""Location"" L on L.""LocationId"" = prl.""LocationId""                                
                                LEFT JOIN ""Practice"" pra ON pra.""PracticeId"" = L.""PracticeId"" AND pra.""Active"" IS TRUE                                
                                LEFT JOIN ""Provider"" pr ON pr.""ProviderId"" = prl.""ProviderId"" AND pr.""Active"" IS TRUE
								LEFT JOIN ""Role"" rl ON rl.""RoleId"" = usr.""RoleId"" AND rl.""Active"" IS TRUE
                                JOIN ""Account"" CA on CA.""AccountId"" = usr.""CreatedBy""
                                JOIN ""Role"" CR on CR.""RoleId"" = CA.""RoleId""
                                LEFT JOIN ""Account"" MA on MA.""AccountId"" = usr.""ModifiedBy""
                                LEFT JOIN ""Role"" MR on MR.""RoleId"" = MA.""RoleId""    
                                {where} Order by usr.""CreatedDate"" DESC) A";

            if (model.PageIndex <= 0)
            {
                return await this.unitOfWork.Current.QueryAsync<UserModel>(query);
            }

            model.PageIndex -= 1;
            query += " LIMIT " + model.PageSize + " offset " + (model.PageIndex * model.PageSize);
            var users = await this.unitOfWork.Current.QueryAsync<UserModel>(query);
            //var userModels = users.ToList();
            //try
            //{

            //    foreach (var user in userModels.OrderBy(m => m.UserId))
            //    {
            //        if ((!string.IsNullOrEmpty(user.ProfileImageUrl)) && (!string.IsNullOrEmpty(user.ThumbnailUrl)))
            //        {

            //            try
            //            {
            //                if ((user.ProfileImageUrl.StartsWith("ftp")) && (user.ThumbnailUrl.StartsWith("ftp")))
            //                {
            //                    user.ProfileImageUrl = await this.documentHelper.FetchImageBase64(user.UserId, user.ProfileImageUrl);
            //                    user.ThumbnailUrl = await this.documentHelper.FetchImageBase64(user.UserId, user.ThumbnailUrl);                              
            //                }
            //            }
            //            catch (Exception)
            //            {
            //                // ignore
            //            }
            //        }
            //    }
            //}
            //catch (Exception)
            //{
            //    // ignore
            //}

            return users;
        }


    }



}