using Asp.Versioning;
using OnlineSalesAutoCrop.CoreAPI.Configurations;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Setups;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using OnlineSalesAutoCrop.CoreAPI.SignalRHub;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace OnlineSalesAutoCrop.CoreAPI.Controllers.V1
{
	/// <summary>
	/// 
	/// </summary>
	/// <remarks>
	/// 
	/// </remarks>
	/// <param name="service"></param>
	/// <param name="appSettings"></param>
	/// <param name="cache"></param>
	/// <param name="logger"></param>
	/// <param name="hub"></param>
	[Authorize]
	[ApiController]
	[ApiVersion("1.0")]
	[Route("api/users")]
	[ValidateAntiForgeryToken]
	[Route("api/v{version:apiVersion}/users")]

	public class UserController(IUserService service, IOptions<AppSettings> appSettings, IEaseCache cache, ILogger<IntegrationAuthController> logger, IHubContext<NotificationHub, INotificationHub> hub) : ControllerBase
	{
		private readonly ILogger _logger = logger;
		private readonly IEaseCache _cache = cache;
		private readonly IUserService _service = service;
		private readonly AppSettings _appSettings = appSettings?.Value;
		private readonly DateTimeOffset _options = Helper.CreateEaseCacheOptions();
		private readonly IHubContext<NotificationHub, INotificationHub> _hub = hub;

		/// <summary>
		/// Login using your credential data retrieve from SqlServer
		/// </summary>
		/// <remarks>
		/// </remarks>
		/// <param name="request"></param>
		/// <returns>If login successful ValidUser: true</returns>
		/// <response code="200">If login successful Return ValidUser: true and UserName: not empty</response>
		[HttpPost("login")]
		[AllowAnonymous]
		[IgnoreAntiforgeryToken]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LoginResponse))]
		public async Task<IActionResult> Login([FromBody] LoginRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			LoginResponse response = new();
			if (string.IsNullOrEmpty(request.LoginId))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Login ID is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.Password))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Password is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.AppVersion))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Version is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			string ipAddress = string.Empty;
			try
			{
				#region Decrypt LoginID

				string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
				request.LoginId = Helper.DecryptData(secret: cipherSecretKey, data: request.LoginId);
				request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password);
				request.AppId = Helper.DecryptData(secret: cipherSecretKey, data: request.AppId);
				request.HostName = Helper.DecryptData(secret: cipherSecretKey, data: request.HostName);
				request.IpAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.IpAddress);
				request.MacAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.MacAddress);
				if (request.LoginId.Equals("*Key/Data Error*") || request.Password.Equals("*Key/Data Error*") || request.AppId.Equals("*Key/Data Error*"))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("Key or Data Error...!");
					return BadRequest(response);
				}

				#endregion

				bool checkPwd = true;
				#region If AD (Active Directory) authentication is enabled do validate

				if (_appSettings.ADConfig.Enabled)
				{
					checkPwd = false;
					#pragma warning disable CA1416 // Validate platform compatibility
					int adLoginStatus = GetADLoginStatus(loginId: request.LoginId, password: request.Password);
					#pragma warning restore CA1416 // Validate platform compatibility
					if (adLoginStatus != 1)
					{
						response.LoginStatus = EnumLoginStatus.Unsuccessful;
						response.ReturnStatus = StatusCodes.Status403Forbidden;
						if (adLoginStatus == 2)
							response.ReturnMessage.Add("Active Directory User is DISABLED.");
						else if (adLoginStatus == 3)
							response.ReturnMessage.Add("Active Directory User's Password has EXPIRED.");
						else
							response.ReturnMessage.Add("Active Directory User/Password is INVALID.");

						return StatusCode(StatusCodes.Status417ExpectationFailed, response);
					}
				}

				#endregion

				ipAddress = Request.HttpContext.GetIpAddress();
				User user = await _service.LoginAsync(request: request, ipAddress: ipAddress, checkPwd: checkPwd);

				if (user == null || user.UserId == 0)
				{
					response.LoginStatus = EnumLoginStatus.Error;
					response.ReturnStatus = StatusCodes.Status403Forbidden;
					response.ReturnMessage.Add(checkPwd ? "Login ID/Password is invalid." : "You are not Authorized to login into the System");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (user.LoginStatus == EnumLoginStatus.Unsuccessful)
				{
					string usm = string.Empty;
					if (user != null && !string.IsNullOrEmpty(user.UnsuccessfulMsg))
						usm = $" ({user.UnsuccessfulMsg})";

					response.LoginStatus = user.LoginStatus;
					response.ReturnStatus = StatusCodes.Status403Forbidden;
					response.ReturnMessage.Add(checkPwd ? $"Login ID/Password is invalid{usm}." : "You are not Authorized to login into the System");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (user.LoginStatus == EnumLoginStatus.VersionMismatch)
				{
					response.LoginStatus = EnumLoginStatus.Unsuccessful;
					response.ReturnStatus = StatusCodes.Status403Forbidden;
					response.ReturnMessage.Add($"Your current version: {request.AppVersion}, Please update your version: {user.UnsuccessfulMsg}<br/>You can update this version simply Reload/Refresh Browser.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (user.IsLocked)
				{
					response.LoginStatus = user.LoginStatus;
					response.ReturnStatus = StatusCodes.Status403Forbidden;
					if (!user.NextLoginTime.HasValue)
						response.ReturnMessage.Add("You are locked, please contact Head office.");
					else
						response.ReturnMessage.Add($"You can Login after {user.NextLoginTime:dd-MMM-yyyy H:mm:ss}");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (user.Status != EnumStatus.Authorized)
				{
					response.LoginStatus = user.LoginStatus;
					response.ReturnStatus = StatusCodes.Status403Forbidden;
					response.ReturnMessage.Add("You are not Authorized to Login into the System, Please contact with System Administrator.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				response.ValidUser = user.LoginStatus == EnumLoginStatus.Success;
				if (!response.ValidUser)
				{
					response.LoginStatus = user.LoginStatus;
					response.ReturnStatus = StatusCodes.Status403Forbidden;
					response.ReturnMessage.Add("Unknown error.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				response.Map(user);
				response.ValidUser = true;
				response.ReturnStatus = StatusCodes.Status200OK;
				//response.PwdChangeRequired = (user.AccessStatus == EnumAccessStatus.FirstTime) || (user.ExpireDate.HasValue && user.ExpireDate.Value.Date < DateTime.Today.Date);

				string pwdSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.PwdSecretKey);
				string userPwd = Ease.NetCore.Utility.Global.CipherFunctions.EncryptByAES(privateKey: pwdSecretKey, publicKey: pwdSecretKey, data: request.Password);
				byte[] key = Encoding.ASCII.GetBytes(_appSettings.JwtCryptoKey);
				JwtSecurityTokenHandler tokenHandler = new();
				var tokenDescriptor = new SecurityTokenDescriptor
				{
					Subject = new ClaimsIdentity(
					[
						//Helper.CreateClaim("UserId", $"{user.Id}"),
						//Helper.CreateClaim("LoginId", user.LoginId),
						//Helper.CreateClaim("UserPwd", userPwd),
						//Helper.CreateClaim("AuthKey", $"{user.AuthKey}"),
						//Helper.CreateClaim("TeamSpaceIds", $"{string.Join(',',user.TeamSpaceIds)}"),
						//Helper.CreateClaim("BatchEnabled", user.BatchEnabled ? "1" : "0"),
						//Helper.CreateClaim("BmProcessId", $"{user.BmProcessId}"),
						//Helper.CreateClaim("PrProcessId", $"{user.PrProcessId}"),
						//Helper.CreateClaim("EmployeeId", $"{user.EmployeeCode}")
					]),
					Expires = DateTime.UtcNow.AddHours(12),
					SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
				};

				SecurityToken token = tokenHandler.CreateToken(tokenDescriptor);
				string userToken = tokenHandler.WriteToken(token);

				//If token length is greater than or equal to 4096 (4KB) then return error
				//because cookie can not store more than 4KB data and we are storing this token in cookie for authentication
				if (userToken.Length >= 4096) //4Kb 
				{
					response.LoginStatus = user.LoginStatus;
					response.ReturnStatus = StatusCodes.Status431RequestHeaderFieldsTooLarge;
					response.ReturnMessage.Add("Authentication Token is too large for cookie.");
					return StatusCode(StatusCodes.Status431RequestHeaderFieldsTooLarge, response);
				}

				response.Expires = tokenDescriptor.Expires;
				response.AuthenticationToken = userToken;
				response.LoginTime = $"{DateTime.Now:dd-MM-yy H:mm:ss}";

				//await HttpContext.Session.SetModulesToSession(key: userToken, value: user.ModuleIds);
				//if (user.LoginStatus == EnumLoginStatus.Success)
				//{
				//	if (user.AuthMethod == EnumAuthenticationMethod.Email && !string.IsNullOrEmpty(user.EmailAddress) && !string.IsNullOrWhiteSpace(user.EmailAddress) && !string.IsNullOrWhiteSpace(user.AuthValue))
				//	{
				//		List<string> to = [.. user.EmailAddress.Split(separator: ';', options: StringSplitOptions.RemoveEmptyEntries)];
				//		await MailHelper.SendMailMessageAsync(settings: _appSettings, to: to,
				//			cc: null, bcc: null, attachments: null, embeddedImages: null, isHtmlBody: false, priority: System.Net.Mail.MailPriority.Normal,
				//			subject: "Your OTP", messageBody: string.Format("Your OTP: {0} and is valid for 5 minutes only", user.AuthValue));
				//	}
				//	else if (user.AuthMethod == EnumAuthenticationMethod.MobileSMS && !string.IsNullOrEmpty(user.MobileNo) && !string.IsNullOrWhiteSpace(user.MobileNo) && !string.IsNullOrWhiteSpace(user.AuthValue))
				//	{
				//		MailHelper.SendSMSOrWhatsAppMessage(settings: _appSettings, whatsAppMsg: false, msg: string.Format("Your OTP: {0} and is valid for 5 minutes only", user.AuthValue), mobileNumber: user.MobileNo);
				//	}
				//}

				//if (user.DisallowMultiLogin)
				//{
				//	await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 1, itemId: 0, ipAddress: ipAddress);
				//	if (request.AttendanceLogin)
				//		await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 6, itemId: 0, ipAddress: ipAddress);
				//}
				//else
				//{
				//	await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 2, itemId: 0, ipAddress: ipAddress);
				//	if (request.AttendanceLogin)
				//		await _hub.Clients.All.NotifySubscriber(userId: user.Id, msgType: 6, itemId: 0, ipAddress: ipAddress);
				//}
				//response.IdsValue = Ease.NetCore.Utility.Global.CipherFunctions.EncryptByAES(data: Newtonsoft.Json.JsonConvert.SerializeObject(user.ModuleIds), privateKey: cipherSecretKey, publicKey: cipherSecretKey, output: 2);

				return Ok(response);
			}
			catch (Exception ex)
			{
				string msg = $"{request?.LoginId}~{ipAddress}";
				_logger.LogError(exception: ex, message: msg);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("loadMenu")]
		public async Task<IActionResult> LoadMenu([FromBody] ByUserIdRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			try
			{
				MenuResponse response = await _service.GetUserPermissionsAsync(userId: request.UserId);
				return Ok(response.Item);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				return StatusCode(StatusCodes.Status500InternalServerError, (ex.InnerException != null ? ex.InnerException.Message : ex.Message));
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[IgnoreAntiforgeryToken]
		[HttpPost("validateOtp")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> ValidateOtp([FromBody] OtpValidationRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { Value = false, ReturnStatus = StatusCodes.Status200OK };
			if (string.IsNullOrEmpty(request.OtpCode) || request.OtpCode.Length != 6)
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Otp must be 6 digit.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				if (request.AuthMethod == EnumAuthenticationMethod.ThirdPartyAuthenticator)
				{
					string secretKey = HttpContext.User.GetClaimValue<string>("AuthKey");
					if (string.IsNullOrEmpty(secretKey))
					{
						response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
						response.ReturnMessage.Add("Authentication key is required.");
						return StatusCode(StatusCodes.Status417ExpectationFailed, response);
					}

					TOtpService otp = new();
					DateTime now = DateTime.UtcNow;
					response.Value = otp.ValidateTwoFactorPIN(secretKey, request.OtpCode, now);
				}
				else
				{
					response.Value = await _service.ValidateAuthValueAsync(request.OtpCode, request.UserId);
				}

				if (!response.Value)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("This is not a valid Otp.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}
				else
				{
					return Ok(response);
				}
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status406NotAcceptable;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("addUser")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> AddUser([FromBody] NewUserRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_1");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Add User.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				int createdBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.AddUserAsync(user: request, ipAddress: ipAddress, createdBy: createdBy);
				response.ReturnMessage.Add("User added successfully...");

				//Cache
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("editUser")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> EditUser([FromBody] UserRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_2");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Update User.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (request.AuthMethod == EnumAuthenticationMethod.ThirdPartyAuthenticator && (string.IsNullOrEmpty(request.AuthKey) || string.IsNullOrWhiteSpace(request.AuthKey) || request.AuthKey.Length <= 0))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("For third party Authenticator, Authentication key is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				int modifiedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.EditUserAsync(user: request, ipAddress: ipAddress, modifiedBy: modifiedBy);
				response.ReturnMessage.Add("User edited successfully...");

				//Cache
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("deleteUser")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> DeleteUser([FromBody] ByUserIdRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_3");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Delete User.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				int deletedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.DeleteUserAsync(userId: request.UserId, deletedBy: deletedBy);
				response.ReturnMessage.Add("User deleted successfully...");

				//Cache
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("unlockUser")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> UnlockUser([FromBody] UserUnlockRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			bool permitted = await HttpContext.IsPermitted("ELIT.1.2.2_2");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Unlock User.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				int unlockedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.UnlockUserAsync(userId: request.UserId, loginId: request.LoginId, unlockedBy: unlockedBy);
				response.ReturnMessage.Add("User Unlocked successfully...");

				//Cache
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("resetPassword")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (request.UserId == 0)
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("User is not valid.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.Password) || string.IsNullOrWhiteSpace(request.Password))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.ConfirmPassword) || string.IsNullOrWhiteSpace(request.ConfirmPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Confirm Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			#region Decrypt Password

			string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
			request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password);
			request.ConfirmPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.ConfirmPassword);

			#endregion

			if (!request.Password.Equals(request.ConfirmPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("New password and confirm password are not same.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			bool permitted = await HttpContext.IsPermitted("ELIT.1.2.3_2");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Reset Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				int changedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.ResetPasswordAsync(userId: request.UserId, newPassword: request.ConfirmPassword, ipAddress: ipAddress, changedBy: changedBy);
				await _hub.Clients.All.NotifySubscriber(userId: request.UserId, msgType: 1, itemId: 0, ipAddress: ipAddress);
				response.ReturnMessage.Add("Password Reset successfully, User must change password at next Login.");
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[IgnoreAntiforgeryToken]
		[HttpPost("changePassword")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> ChangePassword([FromBody] PasswordChangeRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (request.UserId == 0)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("Your not a valid user.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.OldPassword) || string.IsNullOrWhiteSpace(request.OldPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Old Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.Password) || string.IsNullOrWhiteSpace(request.Password))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.ConfirmPassword) || string.IsNullOrWhiteSpace(request.ConfirmPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Confirm Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			#region Decrypt Password

			string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
			request.OldPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.OldPassword);
			request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password);
			request.ConfirmPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.ConfirmPassword);

			#endregion

			if (!request.Password.Equals(request.ConfirmPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("New password and confirm password are not same.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				int changedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.ChangePasswordAsync(userId: request.UserId, oldPassword: request.OldPassword, newPassword: request.ConfirmPassword, ipAddress: ipAddress, changedBy: changedBy);
				await _hub.Clients.All.NotifySubscriber(userId: request.UserId, msgType: 1, itemId: 0, ipAddress: ipAddress);
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("updateMyPassword")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> UpdateMyPassword([FromBody] PasswordChangeRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (string.IsNullOrEmpty(request.OldPassword) || string.IsNullOrWhiteSpace(request.OldPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Old Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.Password) || string.IsNullOrWhiteSpace(request.Password))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.ConfirmPassword) || string.IsNullOrWhiteSpace(request.ConfirmPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Invalid parameter value Confirm Password.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			#region Decrypt Password

			string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
			request.OldPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.OldPassword);
			request.Password = Helper.DecryptData(secret: cipherSecretKey, data: request.Password);
			request.ConfirmPassword = Helper.DecryptData(secret: cipherSecretKey, data: request.ConfirmPassword);

			#endregion

			if (!request.Password.Equals(request.ConfirmPassword))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("New password and confirm password are not same.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.ChangePasswordAsync(userId: userId, oldPassword: request.OldPassword, newPassword: request.ConfirmPassword, ipAddress: ipAddress, changedBy: userId);
				await _hub.Clients.All.NotifySubscriber(userId: userId, msgType: 1, itemId: 0, ipAddress: ipAddress);
				response.ReturnMessage.Add("Password changed successfully.");
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("updateMyTheme")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> UpdateMyTheme([FromBody] UserThemeRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.UpdateMyThemeAsync(userId: userId, menuLayout: request.MenuLayout, themeName: request.ThemeName, schemeName: request.SchemeName);
				response.ReturnMessage.Add("Your theme set successfully. Need re-login to see the effect.");
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("updateMyInfo")]
		[AllowAnonymous, IgnoreAntiforgeryToken]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> UpdateMyInfo([FromBody] UpdateMyInfoRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				if (request.EmployeeId < 0)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("System allow to update only Your Information.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				int modifiedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.UpdateMyInfoAsync(address: request.Address, contactNo: request.ContactNo, modifiedBy: modifiedBy, emplyeeId: request.EmployeeId);
				response.ReturnMessage.Add("Your Information updated successfully.");
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getAttributes")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserAttributesResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserAttributesResponse))]
		public async Task<IActionResult> GetAttributes([FromBody] ByUserAttributesRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserAttributesResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				string key = "UserAttributes";
				string key2 = $"{request.UserId}~{request.ClientType}";
				if (!_cache.TryGetValue(key: key, key2: key2, value: out response))
				{
					response = await _service.GetAttributesAsync(userId: request.UserId, clientType: request.ClientType);

					//Cache
					_ = _cache.Set(key: key, key2: key2, value: response, options: _options);
				}
				response.ReturnStatus = StatusCodes.Status200OK;

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("saveAttributes")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> SaveAttributes([FromBody] UserAttributesRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (request.UkIds == null || request.UkIds.Count <= 0)
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("There is no data to save.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			string msg, key;
			if (request.ClientType == 2)
			{
				key = "ELIT.1.5.12_2";
				msg = "Supplier(s) to user";
			}
			else
			{
				key = "ELIT.1.5.11_2";
				msg = "Project(s) to user";
			}

			bool permitted = await HttpContext.IsPermitted(key);
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add($"You are not authorize to <b>Assign</b> {msg}.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				int attributeSetBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.SaveAttributesAsync(userId: request.UserId, clientType: request.ClientType, ipAddress: ipAddress, attributeSetBy: attributeSetBy, ukIds: request.UkIds);
				response.ReturnMessage.Add($"{msg} <b>Assigned</b> successfully...");

				//Cache
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("deleteAttributes")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> DeleteAttributes([FromBody] ByUserAttributesRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			string msg, key;
			if (request.ClientType == 2)
			{
				key = "ELIT.1.5.12_3";
				msg = "Supplier(s) from user";
			}
			else
			{
				key = "ELIT.1.5.11_3";
				msg = "Project(s) from user";
			}

			bool permitted = await HttpContext.IsPermitted(key);
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add($"You are not authorize to <b>Unassign</b> {msg}.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				int deletedBy = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.DeleteAttributesAsync(userId: request.UserId, clientType: request.ClientType, deletedBy: deletedBy);
				response.ReturnMessage.Add($"{msg} <b>Unassigned</b> successfully...");

				//Cache
				_cache.Clear("User");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// Returns users according to search criteria.
		/// </summary>
		/// <returns>
		/// top 50 users
		/// </returns>
		/// <response code="200">Top 50 users</response>
		/// <response code="204">If the item is null.</response>
		//[ValidateSession]
		//[HttpPost("getUsers")]
		//[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserSearchResponse))]
		//[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserSearchResponse))]
		//public async Task<IActionResult> GetUsers([FromBody] UserSearchRequest request)
		//{
		//	ArgumentNullException.ThrowIfNull(request);

		//	UserSearchResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
		//	try
		//	{
		//		int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
		//		request.CheckOwner = userId != Models.Objects.Systems.User.SuperUser_Id;

		//		string key = "Users";
		//		string key2 = $"{request.Criteria}~{request.Status}~{request.SortField}~{request.SortOrder}~{request.Skip}~{request.PageSize}~{request.CheckOwner}~{userId}";
		//		if (!_cache.TryGetValue(key: key, key2: key2, value: out response))
		//		{
		//			response = await _service.GetUsersAsync(request: request, userId: userId);
		//			_ = _cache.Set(key: key, key2: key2, value: response, options: _options);
		//		}

		//		response.ReturnStatus = StatusCodes.Status200OK;
		//		return Ok(response);
		//	}
		//	catch (Exception ex)
		//	{
		//		_logger.LogError(ex);
		//		response.ReturnStatus = StatusCodes.Status500InternalServerError;
		//		response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
		//		return StatusCode(StatusCodes.Status500InternalServerError, response);
		//	}
		//}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getUsersBasic")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserBasicInfoResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserBasicInfoResponse))]
		public async Task<IActionResult> GetUsersBasic([FromBody] BasicUserSearchRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserBasicInfoResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				string teamSpaceIds = request.TeamSpaceId > 0 ? $"{request.TeamSpaceId}" : HttpContext.User.GetClaimValue<string>("TeamSpaceIds");
				response = await _service.GetUsersBasicAsync(applyFilter: request.ApplyFilter, teamSpaceIds: teamSpaceIds, projectId: request.ProjectId);
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getUsersByTeamSpace")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserBasicInfoResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserBasicInfoResponse))]
		public async Task<IActionResult> GetUsersByTeamSpace([FromBody] BasicUserByTeamSpaceRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserBasicInfoResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				string teamSpaceIds = request.TeamSpaceId > 0 ? $"{request.TeamSpaceId}" : HttpContext.User.GetClaimValue<string>("TeamSpaceIds");
				response = await _service.GetUsersByTeamSpaceAsync(teamSpaceIds: teamSpaceIds, projectId: request.ProjectId, userId: request.UserId);
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getAttendanceUsers")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserBasicInfoResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserBasicInfoResponse))]
		public async Task<IActionResult> GetAttendanceUsers([FromBody] ByUserIdRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserBasicInfoResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				response = await _service.GetAttendanceUsersAsync(userId: request.UserId);
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getUsersForForceLogout")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserForceLogoutResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserForceLogoutResponse))]
		public async Task<IActionResult> GetForceLogoutUsers([FromBody] NoContentRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserForceLogoutResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response = await _service.GetForceLogoutUsersAsync(createdBy: userId);

				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("forceLogoutNow")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> ForceLogoutNow([FromBody] ForceUserLogoutRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (request.UserIds == null || request.UserIds.Count <= 0)
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Parameter value is null/no User was selected.");
				return BadRequest(response);
			}

			bool permitted = await HttpContext.IsPermitted("ELIT.1.2.4_2");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Do Force Logout.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				response.Value = await _service.ForceLogoutNowAsync(userIds: request.UserIds, ipAddress: ipAddress);
				foreach (int userId in request.UserIds)
				{
					await _hub.Clients.All.NotifySubscriber(userId: userId, msgType: 1, itemId: 0, ipAddress: ipAddress);
				}
				response.ReturnMessage.Add("Process completed successfully...");
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getUser")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserGetResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserGetResponse))]
		public async Task<IActionResult> GetUser([FromBody] ByUserIdRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserGetResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				string key = "User";
				string key2 = $"{request.UserId}";
				if (!_cache.TryGetValue(key: key, key2: key2, value: out response))
				{
					response = await _service.GetUserAsync(userId: request.UserId);
					_ = _cache.Set(key: key, key2: key2, value: response, options: _options);
				}
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);

			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getCurrentUser")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserGetResponse))]
		public async Task<IActionResult> GetCurrentUser([FromBody] NoContentRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserGetResponse response;
			try
			{
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);

				string key = "UserCurrent";
				string key2 = $"{userId}";
				if (!_cache.TryGetValue(key: key, key2: key2, value: out response))
				{
					response = await _service.GetUserAsync(userId: userId);
					response.ReturnStatus = StatusCodes.Status200OK;
					_ = _cache.Set(key: key, key2: key2, value: response, options: _options);
				}

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response = new UserGetResponse() { ReturnStatus = StatusCodes.Status500InternalServerError };
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getMyProfile")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserProfileResponse))]
		public async Task<IActionResult> GetMyProfile([FromBody] NoContentRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserProfileResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response = await _service.GetUserProfileAsync(userId: userId);
				response.HasPayslipPath = !string.IsNullOrEmpty(_appSettings.PayslipPath);
				response.HasAIApiKey = !string.IsNullOrEmpty(_appSettings.ApiKeyOpenAI) || !string.IsNullOrEmpty(_appSettings.ApiKeyGoogle);
				response.ReturnStatus = StatusCodes.Status200OK;

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getMyPayslip")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(StringResponse))]
		public IActionResult GetMyPayslip([FromBody] PayslipRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			StringResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (string.IsNullOrEmpty(_appSettings.PayslipPath))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Payslip path is not in the system.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			string employeeId = HttpContext.User.GetClaimValue<string>("EmployeeId");
			if (string.IsNullOrEmpty(employeeId))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("You are not a valid Employee to view payslip.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string path = $"{employeeId}_{request.YearMonth:yyyy}_{request.YearMonth:MM}.pdf";
				path = Path.Combine(_appSettings.PayslipPath, path);
				if (System.IO.File.Exists(path))
				{
					response.Value = Convert.ToBase64String(System.IO.File.ReadAllBytes(path: path));
					return Ok(response);
				}
				else
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add($"Payslip does not exists for the Month: {request.YearMonth:MMMM, yyyy}");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}



		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("sendQrCodeViaEmail")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public IActionResult SendQrCodeViaEmail([FromForm] QRCodeUploadRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				if (string.IsNullOrEmpty(request.EmailAddress))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no email address to send mail.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (string.IsNullOrEmpty(request.FileName))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no image to send to send mail.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (request.FileData.Length <= 0)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no image to send to send mail.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				Result result = ImageFileValidator.Validate(request.FileData);
				if (!result.Acceptable)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("This is not a valid image file.");
					return BadRequest(response);
				}

				var fileSpec = Path.Combine(_appSettings.UploadFolder, request.FileName);
				if (System.IO.File.Exists(fileSpec))
					System.IO.File.Delete(fileSpec);

				using (var stream = new FileStream(fileSpec, FileMode.Create))
				{
					request.FileData.CopyTo(stream);
				}

				bool sent = MailHelper.SendMailMessage(settings: _appSettings, to: [request.EmailAddress],
					   cc: null, bcc: null, attachments: [fileSpec], embeddedImages: null, isHtmlBody: false,
					   priority: System.Net.Mail.MailPriority.High, subject: "QR Code", messageBody: "Scan image");

				if (sent)
				{
					if (System.IO.File.Exists(fileSpec))
						System.IO.File.Delete(fileSpec);

					response.Value = sent;
					response.ReturnMessage.Add($"Successfully mail sent to {request.EmailAddress}");
					return Ok(response);
				}
				else
				{
					response.Value = sent;
					response.ReturnMessage.Add($"Cannot send mail to {request.EmailAddress}");
					return StatusCode(StatusCodes.Status422UnprocessableEntity, response);
				}
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("uploadProfileImage")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public IActionResult UploadProfileImage([FromForm] FileUploadRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				if (string.IsNullOrEmpty(request.FileName))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no Image to set Profile image.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (request.FileData.Length <= 0)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no image to set Profile image.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				Result result = ImageFileValidator.Validate(request.FileData);
				if (!result.Acceptable)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("This is not a valid image file.");
					return BadRequest(response);
				}

				long maxSz = 20 * 1024;
				if (request.FileData.Length > maxSz)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("Maximum size allowed is 20 Kb");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				var fileSpec = Path.Combine(_appSettings.ProfileImageFolder, request.FileName);
				if (System.IO.File.Exists(fileSpec))
					System.IO.File.Delete(fileSpec);

				using (var stream = new FileStream(fileSpec, FileMode.Create))
				{
					request.FileData.CopyTo(stream);
				}
				response.Value = true;
				response.ReturnMessage.Add("Refresh page to view your profile image.");
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("uploadDocument")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> UploadDocument([FromForm] UploadDocumentRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				if (string.IsNullOrEmpty(request.FileName))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no valid file to process.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				if (request.FileData.Length <= 0)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no valid data to process.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				string fileName, fileSpec;
				if (request.DocumentOf == 6)
				{
					string[] allowedExtensions = [".xlsx", ".xls"];
					string fileExtension = Path.GetExtension(request.FileName).ToLowerInvariant();
					if (!allowedExtensions.Contains(fileExtension))
					{
						response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
						response.ReturnMessage.Add("Only excel file is allowed to process.");
						return BadRequest(response);
					}

					if (fileExtension.EndsWith(".xls"))
					{
						Result result = ExcelFileValidator.Validate(request.FileData);
						if (!result.Acceptable)
						{
							response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
							response.ReturnMessage.Add("This is not a valid Excel file.");
							return BadRequest(response);
						}
					}
					else
					{
						Result result = ExcelxFileValidator.Validate(request.FileData);
						if (!result.Acceptable)
						{
							response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
							response.ReturnMessage.Add("This is not a valid Excel file.");
							return BadRequest(response);
						}
					}

					fileName = request.FileName;
					fileSpec = Path.Combine(_appSettings.UploadFolder, fileName);
					if (System.IO.File.Exists(fileSpec))
						System.IO.File.Delete(fileSpec);
				}
				else if (request.DocumentOf == 2)
				{
					if (!request.FileName.ToLower().EndsWith(".csv"))
					{
						response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
						response.ReturnMessage.Add("Only csv file is allowed to process.");
						return StatusCode(StatusCodes.Status417ExpectationFailed, response);
					}

					fileName = request.FileName;
					fileSpec = Path.Combine(_appSettings.UploadFolder, fileName);
					if (System.IO.File.Exists(fileSpec))
						System.IO.File.Delete(fileSpec);
				}
				else
				{
					if (!request.FileName.ToLower().EndsWith(".pdf"))
					{
						response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
						response.ReturnMessage.Add("Only pdf file is allowed to Process.");
						return StatusCode(StatusCodes.Status417ExpectationFailed, response);
					}

					long maxSz = 10 * 1024 * 1024;
					if (request.FileData.Length > maxSz)
					{
						response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
						response.ReturnMessage.Add("Maximum allowable size is 10 MB");
						return StatusCode(StatusCodes.Status417ExpectationFailed, response);
					}

					fileName = $"{request.Id}_{request.DocumentOf}.pdf";
					fileSpec = Path.Combine(_appSettings.UploadFolder, fileName);
					if (System.IO.File.Exists(fileSpec))
						System.IO.File.Delete(fileSpec);
				}

				using (var stream = new FileStream(fileSpec, FileMode.Create))
				{
					request.FileData.CopyTo(stream);
				}

				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				response.Value = await _service.UploadDocumentAsync(userId: userId, id: request.Id, documentOf: request.DocumentOf, orgFileName: request.FileName, fileName: fileName);
				response.ReturnMessage.Add("File Uploaded successfully.");

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getDocument")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(StringResponse))]
		public IActionResult GetDocument([FromBody] FileViewRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			StringResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				if (string.IsNullOrEmpty(request.FileName))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("There is no Image to set Profile image.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				string path = Path.Combine(_appSettings.UploadFolder, request.FileName);
				if (System.IO.File.Exists(path))
				{
					response.Value = Convert.ToBase64String(System.IO.File.ReadAllBytes(path: path));
				}
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[HttpPost("findAccount")]
		[AllowAnonymous, IgnoreAntiforgeryToken]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(FindAccountResponse))]
		public async Task<IActionResult> FindAccount([FromBody] FindAccountRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			FindAccountResponse response = new();
			if (string.IsNullOrEmpty(request.AccountId))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Login Id or Email address or Mobile number is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string key = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
				request.AccountId = Helper.DecryptData(secret: key, data: request.AccountId);
				response = await _service.FindAccountAsync(accountId: request.AccountId);

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[HttpPost("sendPassword")]
		[AllowAnonymous, IgnoreAntiforgeryToken]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> SendPassword([FromBody] SendPasswordRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			if (string.IsNullOrEmpty(request.UserId))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("User Id is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.MobileNo))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Mobile number is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			if (string.IsNullOrEmpty(request.EmailAddress))
			{
				response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
				response.ReturnMessage.Add("Email address is required.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string key = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
				string decipherValue = Helper.DecryptData(secret: key, data: request.UserId);
				if (string.IsNullOrEmpty(decipherValue))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("User Id is required.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}
				if (!int.TryParse(decipherValue, out int userId))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("User Id is required.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}
				if (userId == 0)
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("User Id is required.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				request.MobileNo = Helper.DecryptData(secret: key, data: request.MobileNo);
				request.EmailAddress = Helper.DecryptData(secret: key, data: request.EmailAddress);
				if (string.IsNullOrEmpty(request.MobileNo) && string.IsNullOrEmpty(request.EmailAddress))
				{
					response.ReturnStatus = StatusCodes.Status417ExpectationFailed;
					response.ReturnMessage.Add("Mobile number and Email address both cannot be empty.");
					return StatusCode(StatusCodes.Status417ExpectationFailed, response);
				}

				//Do reset password
				string newPassword = $"{new Random().Next(100000, 999999)}";
				string ipAddress = Request.HttpContext.GetIpAddress();
				response.Value = await _service.SendPasswordAsync(userId: userId, newPassword: newPassword, ipAddress: ipAddress);
				if (response.Value)
				{
					if (!string.IsNullOrEmpty(request.EmailAddress) && !string.IsNullOrWhiteSpace(request.EmailAddress))
					{
						List<string> to = [.. request.EmailAddress.Split(separator: ';', options: StringSplitOptions.RemoveEmptyEntries)];
						await MailHelper.SendMailMessageAsync(settings: _appSettings, to: to, cc: null, bcc: null, attachments: null, embeddedImages: null, isHtmlBody: false, priority: System.Net.Mail.MailPriority.Normal,
							subject: "One Time Password", messageBody: $"Your one time password (use as Old password): {newPassword} and must change password at next Login.");
					}
					if (!string.IsNullOrEmpty(request.MobileNo) && !string.IsNullOrWhiteSpace(request.MobileNo))
					{
						MailHelper.SendSMSOrWhatsAppMessage(settings: _appSettings, whatsAppMsg: false, msg: $"Your one time password (use as Old password): {newPassword} and must change password at next Login.", mobileNumber: request.MobileNo);
					}

					response.ReturnMessage.Add("Password sent to your Email address and/or Mobile number, User must change password at next Login.");
				}
				else
				{
					response.ReturnMessage.Add("Cannot do action on your request.");
				}
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("saveAuthorizeLimit")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> SaveAuthorizeLimit([FromBody] UserLimitAuthorizeRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			bool permitted = await HttpContext.IsPermitted("ELIT.9.1.14_1") || await HttpContext.IsPermitted("ELIT.9.1.14_2");
			if (!permitted)
			{
				response.ReturnStatus = StatusCodes.Status403Forbidden;
				response.ReturnMessage.Add("You are not authorize to Update Authorization Limit.");
				return StatusCode(StatusCodes.Status417ExpectationFailed, response);
			}

			try
			{
				string ipAddress = Request.HttpContext.GetIpAddress();
				string loginId = HttpContext.User.GetClaimValue<string>(Constants.LoginId);
				response.Value = await _service.SaveAuthorizeLimitAsync(maxAuthLimit: request.MaxAuthorizeAmount, userId: request.UserId, ipAddress: ipAddress, savedBy: loginId);
				response.ReturnMessage.Add("Process completed successfully...");
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getAuthorizeLimit")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserAuthorizeLimitResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(UserAuthorizeLimitResponse))]
		public async Task<IActionResult> GetAuthorizeLimit([FromBody] ByUserIdRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			UserAuthorizeLimitResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				response = await _service.GetAuthorizeLimitAsync(userId: request.UserId);
				response.ReturnStatus = StatusCodes.Status200OK;
				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		[HttpPost("logOut")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BooleanResponse))]
		public async Task<IActionResult> LogOut([FromBody] LogoutRequest request)
		{
			BooleanResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				string key = HttpContext.GetAuthenticationToken();
				string ipAddress = Request.HttpContext.GetIpAddress();
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				string loginId = HttpContext.User.GetClaimValue<string>(Constants.LoginId);

				if (request.AttendanceLogout)
				{
					string cipherSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.CipherSecretKey);
					request.IpAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.IpAddress);
					request.MacAddress = Helper.DecryptData(secret: cipherSecretKey, data: request.MacAddress);
					request.HostName = Helper.DecryptData(secret: cipherSecretKey, data: request.HostName);
				}
				_ = await _service.LogoutAsync(ipAddress: ipAddress, userId: userId, logId: request.LogId, attendanceLogout: request.AttendanceLogout, loginId: loginId, localIp: request.IpAddress, macAddress: request.MacAddress, hostName: request.HostName, logoutRemarks: request.LogoutRemarks);
				_cache.Clear(pattern: key);
				_ = await HttpContext.ClearSessionAsync();

				response.Value = true;
				if (request.AttendanceLogout)
				{
					await _hub.Clients.All.NotifySubscriber(userId: userId, msgType: 6, itemId: 0, ipAddress: ipAddress);
				}

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>0
		[AllowAnonymous]
		[IgnoreAntiforgeryToken]
		[HttpPost("sessionExpired")]
		[ProducesResponseType(StatusCodes.Status200OK)]
		public async Task SessionExpired([FromBody] LogoutRequest request)
		{
			try
			{
				int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				string ipAddress = Request.HttpContext.GetIpAddress();
				_ = await _service.LogoutAsync(ipAddress: ipAddress, userId: userId, logId: request.LogId, attendanceLogout: false, loginId: string.Empty, localIp: string.Empty, macAddress: string.Empty, hostName: string.Empty, logoutRemarks: string.Empty);
				_ = await HttpContext.ClearSessionAsync();
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="loginId"></param>
		/// <param name="password"></param>
		/// <returns></returns>
		[SupportedOSPlatform("windows")]
		private int GetADLoginStatus(string loginId, string password)
		{
			try
			{
				const string displayNameAttribute = "DisplayName";
				const string samAccountNameAttribute = "SAMAccountName";
				const string userAccountControlAttribute = "useraccountcontrol";

				string username = (string.IsNullOrEmpty(_appSettings.ADConfig.Domain) || string.IsNullOrWhiteSpace(_appSettings.ADConfig.Domain)) ? loginId : $"{loginId}@{_appSettings.ADConfig.Domain}";
				using DirectoryEntry entry = new(path: _appSettings.ADConfig.Path, username: username, password: password);
				using DirectorySearcher searcher = new(searchRoot: entry);
				searcher.Filter = $"({samAccountNameAttribute}={loginId})";
				searcher.PropertiesToLoad.Add(value: displayNameAttribute);
				searcher.PropertiesToLoad.Add(value: samAccountNameAttribute);
				searcher.PropertiesToLoad.Add(value: userAccountControlAttribute);
				var result = searcher.FindOne();
				if (result == null)
					return 0;

				ResultPropertyValueCollection displayName = result.Properties[name: displayNameAttribute];
				ResultPropertyValueCollection samAccountName = result.Properties[name: samAccountNameAttribute];
				ResultPropertyValueCollection userAccountControl = result.Properties[name: userAccountControlAttribute];
				int uacFlag = (userAccountControl != null && userAccountControl.Count > 0) ? Convert.ToInt32(userAccountControl[0]) : 0;
				if ((uacFlag & 0x000002) == 0x000002)       //Disabled
					return 2;
				else if ((uacFlag & 0x800000) == 0x800000)  //Password expired
					return 3;

				if (displayName != null && displayName.Count > 0 && samAccountName != null && samAccountName.Count > 0)
					return 1;
				else
					return 0;
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				return 0;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("getAccessLog")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AccessLogResponse))]
		[ProducesResponseType(StatusCodes.Status204NoContent, Type = typeof(AccessLogResponse))]
		public async Task<IActionResult> GetAccessLog([FromBody] AccessLogSearchRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);

			AccessLogResponse response = new() { ReturnStatus = StatusCodes.Status200OK };
			try
			{
				string loginId = HttpContext.User.GetClaimValue<string>(Constants.LoginId);
				bool permitted = await HttpContext.IsPermitted("ELIT.1.2.5_2");
				if (!(permitted || loginId.Equals(value: Models.Objects.Systems.User.SuperUser_LoginId, comparisonType: StringComparison.OrdinalIgnoreCase)))
				{
					request.LoginId = loginId;
				}
				response = await _service.GetAccessLogAsync(accessType: request.AccessType, loginId: request.LoginId, startDate: request.StartDate, endDate: request.EndDate);
				response.ReturnStatus = StatusCodes.Status200OK;

				return Ok(response);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				response.ReturnStatus = StatusCodes.Status500InternalServerError;
				response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
				return StatusCode(StatusCodes.Status500InternalServerError, response);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="request"></param>
		/// <returns></returns>
		[ValidateSession]
		[HttpPost("loadNotificationCount")]
		[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
		public async Task<IActionResult> LoadNotificationCount([FromBody] NoContentRequest request)
		{
			ArgumentNullException.ThrowIfNull(request);
			try
			{
				int value = HttpContext.User.GetClaimValue<int>(Constants.UserId);
				value = await _service.LoadNotificationCountAsync(userId: value);

				return Ok(value);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex);
				return StatusCode(StatusCodes.Status500InternalServerError);
			}
		}
	}
}