namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using AutoMapper;
    using Domain.Services;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Models.Provider;
    using Newtonsoft.Json;
    using Npgsql;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.UserModels;
    using Shared.UserModels.Filters;
    using Utilities;

    using Hims.Domain.Helpers;
    using Hims.Shared.Library.Enums;
    using Hims.Shared.UserModels.Common;
    using Hims.Api.Models;
    using Hims.Shared.UserModels.Scan.ScanMachineAvailability;
    using Hims.Shared.UserModels.Scan.ScanAppointment;
    using Hims.Api.Senders;
    using Hims.Domain.Repositories.UnitOfWork;
    using Hims.Shared.UserModels.WebNotification;
    using Hims.Api.Models.Patient;
    using Hims.Domain.Entities;
    using Hims.Infrastructure.Services;
    using Dapper;
    using Hims.Shared.UserModels.Slots;
    using Hims.Shared.UserModels.Queue;
    using PatientModel = Shared.EntityModels.PatientModel;
    using Hims.Shared.UserModels.Scan.BookAppointment;
    using Hims.Domain.Entities.Enums;
    using Hims.Infrastructure.Helpers.Models;
    using Hims.Domain.Configurations;
    using System.IO;

    /// <inheritdoc />
    [Route("api/book-scan-appointment")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class BookScanAppointmentController : BaseController
    {
        /// <summary>
        /// The scan machine availability services.
        /// </summary>
        private readonly IBookScanAppointmentService bookScanAppointmentService;

        /// <summary>
        /// The patient services.
        /// </summary>
        private readonly IPatientService patientService;

        /// <summary>
        /// The AES helper.
        /// </summary>
        private readonly IAESHelper aesHelper;

        /// <summary>
        /// The scan service.
        /// </summary>
        private readonly IScanLogService scanLogService;

        /// <summary>
        /// The whats application SMS sender
        /// </summary>
        private readonly IWhatsAppSMSSender whatsAppSMSSender;

        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <summary>
        /// The web notification service
        /// </summary>
        private readonly IWebNotificationService webNotificationService;

        /// <summary>
        /// The amazon s3 helper.
        /// </summary>
        private readonly IDocumentHelper documentHelper;

        /// <summary>
        /// The ftp helper.
        /// </summary>
        private readonly IFtpUploadHelper ftpUploadHelper;

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

        /// <summary>
        /// The auditlog services.
        /// </summary>
        private readonly IAuditLogService auditLogServices;

        /// <summary>
        /// The setting service
        /// </summary>
        private readonly ISettingService settingService;

        /// <inheritdoc />
        public BookScanAppointmentController(
           IBookScanAppointmentService bookScanAppointmentService,
            IAESHelper aesHelper,
            IPatientService patientService,
            IScanLogService scanLogService,
            IWhatsAppSMSSender whatsAppSMSSender,
            IUnitOfWork unitOfWork,
            IWebNotificationService webNotificationService,
            IDocumentHelper documentHelper,
            IFtpUploadHelper ftpUploadHelper,
            IRunningEnvironment runningEnvironment,
            IAuditLogService auditLogServices, ISettingService settingService)
        {
            this.bookScanAppointmentService = bookScanAppointmentService;
            this.patientService = patientService;
            this.aesHelper = aesHelper;
            this.scanLogService = scanLogService;
            this.whatsAppSMSSender = whatsAppSMSSender;
            this.unitOfWork = unitOfWork;
            this.webNotificationService = webNotificationService;
            this.documentHelper = documentHelper;
            this.ftpUploadHelper = ftpUploadHelper;
            this.runningEnvironment = runningEnvironment;
            this.auditLogServices = auditLogServices;
            this.settingService = settingService;
        }

        /// <summary>
        /// The add scan book appointment.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - scan location added successfully.
        /// - 409 - scan location already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("add")]
        [Consumes("multipart/form-data")]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> AddAsync([FromForm] InsertBookScanAppointment request)
        {
            request = (InsertBookScanAppointment)EmptyFilter.Handler(request);
            request.AppointmentDate = request.AppointmentDate.Hour > 0 ? request.AppointmentDate : JsonConvert.DeserializeObject<DateTime>(request.ApptDate);
            if (request.PatientId <= 0)
            {
                var patientId = await this.AddPatientsAsync(request);
                if (int.Parse(patientId) > 0)
                {
                    var auditLogModel = new AuditLogModel
                    {
                        AccountId = request.CreatedBy,
                        LogTypeId = (int)LogTypes.Patient,
                        LogFrom = (short)request.LoginRoleId,
                        LogDate = DateTime.UtcNow.AddMinutes(330),
                        LogDescription = $@"{request.CreatedByName} <b>Registered</b> the new Patient: <b>{request.FirstName}</b> on {DateTime.UtcNow.AddMinutes(330).ToString("MM/dd/yyyy hh:mm tt")}",
                        LocationId = (int)request.LocationId,
                    };
                    await this.auditLogServices.LogAsync(auditLogModel);
                    var scanLogModel = new ScanLogModel
                    {
                        AccountId = request.CreatedBy,
                        ScanLogTypeId = (int)ScanLogTypes.Patient,
                        LogFrom = (short)request.LoginRoleId,
                        LogDate = DateTime.UtcNow,
                        LocationId = (int)request.LocationId,
                        LogDescription = $@"{request.CreatedByName} <b>Registered</b> the new Patient: <b>{request.FirstName}</b> on {DateTime.UtcNow.AddMinutes(330).ToString("MM/dd/yyyy hh:mm tt")}",
                    };
                    await this.scanLogService.LogAsync(scanLogModel);
                }
                request.PatientId = int.Parse(patientId);
            }

            if (request.PatientId == -1)
            {
                return this.ServerError();
            }
            var response = await this.bookScanAppointmentService.InsertAsync(request);
            var patientName = await this.unitOfWork.Patients.FindAsync(x => x.PatientId == request.PatientId);
            if (request.PaymentStatus && patientName.PaymentStatus == true)
            {
                var checkIf1 = this.unitOfWork.Current.QueryFirstOrDefault<int>($@"SELECT COUNT(""PatientId"") FROM ""BookScanAppointment"" WHERE ""PatientId"" = {request.PatientId} AND ""PaymentStatus"" IS TRUE");
                if (checkIf1 > 1)
                {
                    patientName.IsNewPatient = false;
                    await this.unitOfWork.Patients.UpdateAsync(patientName);
                }
            }
            // for existing patient if registration charges are there, we need to update payment details in patient table also.
            if ((patientName.PaymentStatus == false && request.PaymentStatus == true) || (patientName.PaymentStatus == null && request.PaymentStatus == true))
            {
                if (patientName != null)
                {
                    patientName.UMRNo = patientName.UMRNo.StartsWith('U') ? patientName.UMRNo : await this.patientService.GetLatestUMRNo();
                    patientName.ModifiedBy = (int)request.ModifiedBy;
                    patientName.ModifiedDate = DateTime.Now;
                    patientName.PaymentStatus = request.PaymentStatus;
                    patientName.PayTypeId = request.PayTypeId > 0 ? request.PayTypeId : null;
                    patientName.PaymentNumber = request.PaymentNumber != null ? request.PaymentNumber : null;
                    patientName.Amount = request.Charges != null ? request.Charges : 0;
                    patientName.Total = (decimal?)(request.PatientTotal != null ? request.PatientTotal : 0);
                    patientName.DiscountInPercentage = (decimal)(request.PatientDiscountInPercentage != null ? request.PatientDiscountInPercentage : 0);
                    patientName.DiscountInRupees = (decimal)(request.PatientDiscountInRupees != null ? request.PatientDiscountInRupees : 0);
                    await this.unitOfWork.Patients.UpdateAsync(patientName);
                }
            }
            // payLater appointments to fall into Notification table.
            if (response > 0)
            {
                var abc = new FetchBookScanAppointment();
                abc.BookScanAppointmentId = response;
                var ScanDetails = await this.bookScanAppointmentService.FetchScanReport(abc);
                var ScanAppointment = ScanDetails.First();
                var WhatsAppMessageSetting = await this.settingService.FetchAsync("WhatsAppMsgService", null, null);
                var WhatsAppMessage = WhatsAppMessageSetting.ToList();
                if ((bool)WhatsAppMessage[0].Active)
                {
                    bool ret = await this.whatsAppSMSSender.SendScanWhatsAppMessage(patientName.Mobile, patientName.FullName, request.MachineName, request.AppointmentDate.ToLongDateString(), request.AppointmentTime, "ScanBook", ScanAppointment.RequisitionNumber);
                }
                try
                {
                    if (request.PaymentStatus == false)
                    {
                        var appointment = await this.unitOfWork.BookScanAppointments.FindAsync(m => m.BookScanAppointmentId == response);
                        var findPatientName = await this.unitOfWork.Patients.FindAsync(m => m.PatientId == appointment.PatientId);
                        var findScanMachineName = await this.unitOfWork.ScanMachineMasters.FindAsync(m => m.ScanMachineMasterId == appointment.ScanMachineMasterId);
                        if (appointment == null)
                        {
                            return this.Success(response);
                        }
                        var url = "app/patient-multiple-forms/parentForm";
                        //var url = "app/scan-appointment/booking-scan-appointment";
                        var roles = (await this.webNotificationService.GetAllRoles()).ToList();
                        var getRolesToNotify = roles.Where(r => r.RoleName.ToLower().Contains("front"));
                        var getAdmins = roles.Where(r => r.RoleName.ToLower().Contains("admin"));
                        var finalRoleModel = new List<RoleModel>();
                        finalRoleModel.AddRange(getRolesToNotify.ToList());
                        finalRoleModel.AddRange(getAdmins.ToList());
                        var notification = new WebNotificationModel
                        {
                            AllowedAccounts = string.Empty,
                            AllowedRoles = string.Join(",", finalRoleModel.Select(r => r.RoleId)),
                            //CreatedDate = DateTime.Now,
                            CreatedDate = Convert.ToDateTime(request.AppointmentDate.ToString("yyyy - MM - dd")).Add(appointment.AppointmentTime),
                            IsRead = false,
                            Message = $@"{findPatientName.FullName}`s Appointment for a {findScanMachineName.MachineName} has been booked at  {Convert.ToDateTime(DateTime.Now.ToString("yyyy - MM - dd")).Add(appointment.AppointmentTime):hh:mm tt}",
                            RedirectionLink = url,
                            WebNotificationLogTypeId = (int)WebNotificationLogType.View,
                            WebNotificationPriorityId = (int)WebNotificationPriority.High,
                            PatientId = appointment.PatientId,
                            ReferenceId = appointment.BookScanAppointmentId,
                            //ModulesMasterId = request.PatientId > 0 ? (int)ModulesMasterType.ReviewPatients : (int)ModulesMasterType.NewPatients
                            ModulesMasterId = (int)ModulesMasterType.Scan
                        };
                        await this.webNotificationService.InsertAsync(notification);
                    }
                }
                catch (Exception e)
                {
                    // ignore
                }
            }
            switch (response)
            {
                case -1:
                    return this.Conflict("Given availability has already been exists with us.");
                case 0:
                    return this.ServerError();
                case -2:
                    return this.Conflict("Patient Registration Charges are not added.");

                default:
                    var scanLogModel = new ScanLogModel
                    {
                        AccountId = request.CreatedBy,
                        ScanLogTypeId = (int)ScanLogTypes.BookScanAppointment,
                        LogFrom = (short)request.LoginRoleId,
                        LogDate = DateTime.UtcNow,
                        LocationId = (int)request.LocationId,
                        LogDescription = $@"<b>{request.CreatedByName}</b> has <b>Booked</b> Scan Appointment for Patient : <b>{patientName.FullName}</b> for the Machine : <b>{request.MachineName}</b> Successfully.",
                    };
                    await this.scanLogService.LogAsync(scanLogModel);

                    return this.Success(response);
            }
        }

        /// <summary>
        /// The update of patient details and appointments.
        /// </summary>
        /// <param name="request">
        /// The update request model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Patient details has been updated successfully.
        /// - 409 - Patient has already exist with us.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("update")]
        [Consumes("multipart/form-data")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(typeof(string), 409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UpdatePatientDetailsPaymentAsync([FromForm] InsertBookScanAppointment request)
        {
            var result = -1;
            request = (InsertBookScanAppointment)EmptyFilter.Handler(request);
            var patientData = JsonConvert.DeserializeObject<ModifyPatientDetails>(request.patientDetails);
            if (request != null)
            {
                result = await this.bookScanAppointmentService.UpdatePatientDetailsPaymentAsync(request, patientData);
            }
            var appointment = await this.unitOfWork.BookScanAppointments.FindAsync(x => x.BookScanAppointmentId == request.BookScanAppointmentId);
            var scanTest = await this.unitOfWork.ScanTestMasters.FindAsync(x => x.ScanTestMasterId == appointment.ScanTestMasterId);

            switch (result)
            {
                case -1:
                    return this.Conflict("Given availability has already been exists with us.");
                case 0:
                    return this.ServerError();
                default:
                    var scanLogModel = new ScanLogModel
                    {
                        AccountId = request.CreatedBy,
                        ScanLogTypeId = (int)ScanLogTypes.BookScanAppointment,
                        LogFrom = (short)request.LoginRoleId,
                        LogDate = DateTime.UtcNow,
                        LocationId = (int)request.LocationId,
                        LogDescription = $@"Scan Pay Later Appointment's Payment Successfull for Patient : <b>{patientData.FullName}</b> for the Scan Test : <b>{scanTest.ScanTestName}.</b>",
                    };
                    await this.scanLogService.LogAsync(scanLogModel);

                    return this.Success(1);
            }
        }

        /// <summary>
        /// The update of patient details and appointments.
        /// </summary>
        /// <param name="model">
        /// The update request model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Patient details has been updated successfully.
        /// - 409 - Patient has already exist with us.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("update-book-scan-appointment")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(typeof(string), 409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UpdateBookScanAptAsync([FromBody] InsertBookScanAppointment model)
        {
            model = (InsertBookScanAppointment)EmptyFilter.Handler(model);
            var response = await this.bookScanAppointmentService.UpdateDetailsAsync(model);
            if (response > 0)
            {
                var LogDescription = "";
                if (model.IsDispatched)
                {
                    LogDescription = $@"<b>{model.CreatedByName}</b> has moved the Patient : <b>{model.PatientName}</b> to the <b>PNDT Report</b> successfully.";
                }
                else if (model.PndtReport)
                {
                    LogDescription = $@"<b>{model.CreatedByName}</b> has sent the Patient : <b>{model.PatientName}</b> for the Scan Test : <b>{model.ScanTestName}</b> to <b>Dispatch</b> successfully.";
                }

                var scanLogModel = new ScanLogModel
                {
                    AccountId = model.CreatedBy,
                    ScanLogTypeId = (int)ScanLogTypes.ScanScroll,
                    LogFrom = (short)model.LoginRoleId,
                    LogDate = DateTime.UtcNow,
                    LocationId = (int)model.LocationId,
                    LogDescription = LogDescription
                };
                await this.scanLogService.LogAsync(scanLogModel);
            }
            return this.Success(response);
        }

        /// <summary>
        /// The fetch booking scan appointment.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - scan location added successfully.
        /// - 409 - scan location already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("fetch")]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAsync([FromBody] FilterModel request)
        {
            request = (FilterModel)EmptyFilter.Handler(request);
            if (!string.IsNullOrEmpty(request.EncryptedAppointmentId))
            {
                request.BookScanAppointmentId = Convert.ToInt32(this.aesHelper.Decode(request.EncryptedAppointmentId));
                var response = await this.bookScanAppointmentService.FetchAsync(request);
                foreach (var patient in response)
                {
                    patient.EncryptedPatientId = this.aesHelper.Encode(patient.PatientId.ToString());
                    DateTime d1 = Convert.ToDateTime(patient.AppointmentTime);
                    patient.AppointmentTime = d1.ToString("hh:mm tt");
                    DateTime d2 = Convert.ToDateTime(patient.AppointmentEndTime);
                    patient.AppointmentEndTime = d2.ToString("hh:mm tt");
                }
                return this.Success(response);
            }
            if (request.Filter == null)
            {
                var response = await this.bookScanAppointmentService.FetchAsync(request);
                foreach (var item in response)
                {
                    item.DiscountAmount = item.DiscountAmount > 0 ? item.DiscountAmount : 0;
                    DateTime d1 = Convert.ToDateTime(item.AppointmentTime);
                    item.AppointmentTime = d1.ToString("hh:mm tt");
                    DateTime d2 = Convert.ToDateTime(item.AppointmentEndTime);
                    item.AppointmentEndTime = d2.ToString("hh:mm tt");
                }
                return this.Success(response);
            }
            else
            {
                var filterResponse = await this.bookScanAppointmentService.FetchFilterEvent(request);
                return this.Success(filterResponse);
            }
        }

        /// <summary>
        /// The fetch filter event patient naes.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - scan location added successfully.
        /// - 409 - scan location already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("fetch-filter-patients")]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchFilterAsync([FromBody] FilterModel request)
        {
            request = (FilterModel)EmptyFilter.Handler(request);
            var filterResponse = await this.bookScanAppointmentService.FetchFilterEventPatients(request);
            return this.Success(filterResponse);
        }

        /// <summary>
        /// The reschedule appointment.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <param name="header"></param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Appointment rescheduled successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPut]
        [Authorize]
        [Route("reschedule")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> RescheduleAsync([FromBody] RescheduleScanAppointmentRequest model, [FromHeader] LocationHeader header)
        {
            model = (RescheduleScanAppointmentRequest)EmptyFilter.Handler(model);
            model.AppointmentDate = model.AppointmentDate.Hour > 0 ? model.AppointmentDate : JsonConvert.DeserializeObject<DateTime>(model.ApptDate);
            if (model.LocationId == 0)
            {
                model.LocationId = int.Parse(header.LocationId);
            }
            var response = await this.bookScanAppointmentService.RescheduleAsync(model.BookScanAppointmentId, Convert.ToInt32(model.ModifiedBy), model.AppointmentDate, TimeSpan.Parse(model.AppointmentTime), TimeSpan.Parse(model.AppointmentEndTime), model.ScanMachineMasterId);
            if (response > 0)
            {
                var patientDetails = await this.bookScanAppointmentService.FetchScanAppointmentDetails(model.BookScanAppointmentId);
                var WhatsAppMessageSetting = await this.settingService.FetchAsync("WhatsAppMsgService", null, null);
                var WhatsAppMessage = WhatsAppMessageSetting.ToList();
                if ((bool)WhatsAppMessage[0].Active)
                {
                    bool ret = await this.whatsAppSMSSender.SendScanWhatsAppMessage(patientDetails.Mobile, patientDetails.PatientName, model.MachineName, model.AppointmentDate.ToLongDateString(), model.AppointmentTime, "ScanRescheduele", patientDetails.RequisitionNumber);
                }
                //var times = TimeSpan.Parse(model.AppointmentTime).ToString().Split(":");
                //DateTime dateTime = new DateTime(
                //                model.AppointmentDate.Year,
                //                model.AppointmentDate.Month,
                //                model.AppointmentDate.Day,
                //                Convert.ToInt32(times[0]),
                //                Convert.ToInt32(times[1]),
                //                0);
                var createdDate = new DateTime(model.AppointmentDateModel.Year, model.AppointmentDateModel.Month, model.AppointmentDateModel.Day);
                if (patientDetails != null && patientDetails.PaymentStatus == false)
                {
                    var notification = new WebNotificationModel
                    {
                        CreatedDate = createdDate,
                        Message = $@"{patientDetails.PatientName}`s Appointment for a {model.MachineName} has been booked at {TimeSpan.Parse(model.AppointmentTime)}",
                        ModulesMasterId = (int)ModulesMasterType.Scan,
                        //CreatedDate = Convert.ToDateTime(model.AppointmentDate.ToString("yyyy - MM - dd")).Add(TimeSpan.Parse(model.AppointmentTime))
                    };
                    var res = await this.webNotificationService.UpdateAsync(notification, model.BookScanAppointmentId);
                }
                var scanLogModel = new ScanLogModel
                {
                    AccountId = model.ModifiedBy,
                    ScanLogTypeId = (int)ScanLogTypes.BookScanAppointment,
                    LogFrom = (short)model.LoginRoleId,
                    LogDate = DateTime.UtcNow,
                    LocationId = model.LocationId,
                    LogDescription = $@"<b>{model.CreatedByName}</b> has <b>Rescheduled</b> Scan Appointment for Patient : <b>{patientDetails.PatientName}</b> for the Machine : <b>{model.MachineName}</b> to Date: <b>{createdDate.Add(TimeSpan.Parse(model.AppointmentTime))}</b> Successfully."
                };
                await this.scanLogService.LogAsync(scanLogModel);
            }
            return this.Success(response);
        }

        /// <summary>The bulk reschedule of scan appointments.</summary>
        /// <param name="model">The model.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Appointment rescheduled successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("bulk-reschedule")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> RescheduleAllAppointmentAsync([FromBody] RescheduleScanAppointmentRequest model)
        {
            model = (RescheduleScanAppointmentRequest)EmptyFilter.Handler(model);
            model.AppointmentDate = model.AppointmentDate.Hour > 0 ? model.AppointmentDate : JsonConvert.DeserializeObject<DateTime>(model.ApptDate);
            var response = await this.bookScanAppointmentService.BulkRescheduleAsync(model);
            if (response == 0)
            {
                return this.ServerError();
            }
            if (response > 0)
            {
                try
                {
                    var appointmentId = model.AppointmentIds.Split(",");
                    for (int i = 0; i < appointmentId.Length; i++)
                    {
                        var aptStartTimings = model.AppointmentTime.Split(",");
                        var patientDetails = await this.bookScanAppointmentService.FetchScanAppointmentDetails(int.Parse(appointmentId[i]));
                        // commenting whatsapp message as it is taking too long when used in bulk reschedule.
                        //bool ret = await this.whatsAppSMSSender.SendScanWhatsAppMessage(patientDetails.Mobile, patientDetails.PatientName, patientDetails.MachineName, model.AppointmentDate.ToLongDateString(), aptStartTimings[i], "ScanRescheduele", patientDetails.RequisitionNumber);

                        //var times = TimeSpan.Parse(aptStartTimings[i]).ToString().Split(":");
                        //DateTime dateTime = new DateTime(
                        //                model.AppointmentDate.Year,
                        //                model.AppointmentDate.Month,
                        //                model.AppointmentDate.Day,
                        //                Convert.ToInt32(times[0]),
                        //                Convert.ToInt32(times[1]),
                        //                0);
                        //DateTime aptDate = Convert.ToDateTime(model.AppointmentDate);
                        //var appointmentDate = aptDate.Date.ToString("yyyy-MM-dd");
                        var createdDate = new DateTime(model.AppointmentDateModel.Year, model.AppointmentDateModel.Month, model.AppointmentDateModel.Day);
                        if (patientDetails != null && patientDetails.PaymentStatus == false)
                        {
                            var notification = new WebNotificationModel
                            {
                                Message = $@"{patientDetails.PatientName}`s Appointment for a {model.MachineName} has been booked at {TimeSpan.Parse(aptStartTimings[i])}",
                                ModulesMasterId = (int)ModulesMasterType.Scan,
                                CreatedDate = createdDate
                            };
                            var res = await this.webNotificationService.UpdateAsync(notification, int.Parse(appointmentId[i]));
                        }
                        var scanLogModel = new ScanLogModel
                        {
                            AccountId = model.ModifiedBy,
                            ScanLogTypeId = (int)ScanLogTypes.BookScanAppointment,
                            LogFrom = (short)model.LoginRoleId,
                            LogDate = DateTime.UtcNow,
                            LocationId = model.LocationId,
                            LogDescription = $@"<b>{model.CreatedByName}</b> has <b>Rescheduled</b> Scan Appointment for Patient : <b>{patientDetails.PatientName}</b> for the Machine : <b>{model.MachineName}</b> to Date: <b>{createdDate.Add(TimeSpan.Parse(aptStartTimings[i]))}</b> Successfully."
                        };
                        await this.scanLogService.LogAsync(scanLogModel);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            return this.Success(response);
        }

        /// <summary>The cancel of scan appointment.</summary>
        /// <param name="model">The model.
        /// <returns>The <see cref="Task"/>.</returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Appointment cancelled successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Authorize]
        [Route("cancel")]
        [Consumes("multipart/form-data")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CancelAsync([FromForm] InsertBookScanAppointment model)
        {
            model = (InsertBookScanAppointment)EmptyFilter.Handler(model);
            var response = await this.bookScanAppointmentService.CancelAsync(model);
            if (response > 0)
            {
                var patientDetails = await this.bookScanAppointmentService.FetchScanAppointmentDetails((int)model.BookScanAppointmentId);
                var WhatsAppMessageSetting = await this.settingService.FetchAsync("WhatsAppMsgService", null, null);
                var WhatsAppMessage = WhatsAppMessageSetting.ToList();
                if ((bool)WhatsAppMessage[0].Active)
                {
                    bool ret = await this.whatsAppSMSSender.SendScanWhatsAppMessage(patientDetails.Mobile, patientDetails.PatientName, model.MachineName, patientDetails.AppointmentDate.ToLongDateString(), patientDetails.AppointmentTime, "ScanCancel", patientDetails.RequisitionNumber);
                }
                var scanLogModel = new ScanLogModel
                {
                    AccountId = model.CreatedBy,
                    ScanLogTypeId = (int)ScanLogTypes.BookScanAppointment,
                    LogFrom = (short)model.LoginRoleId,
                    LogDate = DateTime.UtcNow,
                    LocationId = (int)model.LocationId,
                    LogDescription = $@"<b>{model.CreatedByName}</b> has <b>Cancelled</b> Scan Appointment for Patient : <b>{patientDetails.PatientName} for the Machine : <b>{model.MachineName}</b> Successfully."
                };
                await this.scanLogService.LogAsync(scanLogModel);
            }
            if (response == 0)
            {
                return this.ServerError();
            }
            return this.Success(response);
        }

        /// <summary>
        /// The bulk cancel of scan appointments.
        /// </summary>
        /// <param name="model"></param>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Appointment cancelled successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpGet]
        [AllowAnonymous]
        [Route("bulk-cancel")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CancelAllAsync(RescheduleScanAppointmentRequest model)
        {
            var cancelAllResponse = await this.bookScanAppointmentService.CancelAllAsync(model.AppointmentIds, model.Reason);
            if (cancelAllResponse > 0)
            {
                try
                {
                    var appointmentId = model.AppointmentIds.Split(",");
                    for (int i = 0; i < appointmentId.Length; i++)
                    {
                        var patientDetails = await this.bookScanAppointmentService.FetchScanAppointmentDetails(int.Parse(appointmentId[i]));
                        // commenting whatsapp message as it is taking too long when used in bulk cancel.
                        //bool ret = await this.whatsAppSMSSender.SendScanWhatsAppMessage(patientDetails.Mobile, patientDetails.PatientName, patientDetails.MachineName, patientDetails.AppointmentDate.ToLongDateString(), patientDetails.AppointmentTime, "ScanCancel", patientDetails.RequisitionNumber);
                        var scanLogModel = new ScanLogModel
                        {
                            AccountId = model.ModifiedBy,
                            ScanLogTypeId = (int)ScanLogTypes.BookScanAppointment,
                            LogFrom = (short)model.LoginRoleId,
                            LogDate = DateTime.UtcNow,
                            LocationId = patientDetails.LocationId,
                            LogDescription = $@"<b>{model.CreatedByName}</b> has <b>Cancelled</b> Scan Appointment for Patient : <b>{patientDetails.PatientName} for the Machine : <b>{patientDetails.MachineName}</b> Successfully."
                        };
                        await this.scanLogService.LogAsync(scanLogModel);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            return this.Success(cancelAllResponse);
        }

        /// <summary>
        /// The fetch lab daily report async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <param name="header">
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>
        /// </returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch-scan-report")]
        public async Task<ActionResult> FetchScanReportAsync([FromBody] FetchBookScanAppointment model, [FromHeader] LocationHeader header)
        {
            var models = model ?? new FetchBookScanAppointment();
            model.LocationId = !string.IsNullOrEmpty(header.LocationId) ? int.Parse(header.LocationId) : (int?)null;
            var response = await this.bookScanAppointmentService.FetchScanReport(models);
            foreach (var item in response)
            {
                DateTime d1 = Convert.ToDateTime(item.AppointmentTime);
                item.AppointmentTimeString = d1.ToString("hh:mm tt");
                if (item.Status == "C") { item.Status = "Cancelled"; }
                else if (item.Status == "B") { item.Status = "Booked"; }
                else if (item.Status == "R") { item.Status = "Rescheduled"; }
            }
            return this.Success(response);
        }

        /// <summary>
        /// The fetch of patient Future Appointments async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <param name="header">
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>
        /// </returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch-patient-future-appointments")]
        public async Task<ActionResult> FetchPatientFutureAppointmentsAsync([FromBody] FetchPatientFutureAppointment model, [FromHeader] LocationHeader header)
        {
            var models = model ?? new FetchPatientFutureAppointment();
            var response = await this.bookScanAppointmentService.FetchPatientFutureAppointments(models);
            foreach (var item in response)
            {
                DateTime d1 = Convert.ToDateTime(item.AppointmentTime);
                item.AppointmentTime = d1.ToString("hh:mm tt");
            }
            return this.Success(response);
        }

        /// <summary>
        /// The fetch scan pndt report async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>
        /// </returns>
        [AllowAnonymous]
        [HttpPost]
        [Route("fetch-pndt-report")]
        public async Task<ActionResult> FetchPndtReportAsync([FromBody] FetchBookScanAppointment model)
        {
            var models = model ?? new FetchBookScanAppointment();
            //models.LocationId = Convert.ToInt32(header.LocationId);
            var response = await this.bookScanAppointmentService.FetchPndtReportAsync(models);
            foreach (var item in response)
            {
                DateTime d1 = Convert.ToDateTime(item.AppointmentTime);
                item.AppointmentTime = d1.ToString("hh:mm tt");
            }
            return this.Success(response);
        }

        /// <summary>
        /// The fetch scan scroll async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// <returns>
        /// The <see cref="Task"/>
        /// </returns>
        [AllowAnonymous]
        [HttpPost]
        [Route("fetch-scan-scroll")]
        public async Task<ActionResult> FetchScanScrollAsync([FromBody] FetchBookScanAppointment model)
        {
            var models = model ?? new FetchBookScanAppointment();
            //models.LocationId = Convert.ToInt32(header.LocationId);
            var response = await this.bookScanAppointmentService.FetchScanScroll(models);
            foreach (var item in response)
            {
                DateTime d1 = Convert.ToDateTime(item.AppointmentTime);
                item.AppointmentTime = d1.ToString("hh:mm tt");
            }
            return this.Success(response);
        }

        /// <summary>
        /// The fetch lab daily report async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// <returns>
        /// The <see cref="Task"/>
        /// </returns>
        [HttpPost]
        [Authorize]
        [Route("fetch-outpatient")]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchOutPatientAsync([FromBody] FetchBookScanAppointment model)
        {
            var models = model ?? new FetchBookScanAppointment();
            if (!string.IsNullOrEmpty(model.EncryptedPatientId))
            {
                model.PatientId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedPatientId));
            }
            var filterResponse = await this.bookScanAppointmentService.FetchScanOutPatients(models);
            foreach (var item in filterResponse)
            {
                DateTime d1 = Convert.ToDateTime(item.AppointmentTime);
                item.AppointmentTime = d1.ToString("hh:mm tt");
                DateTime d2 = Convert.ToDateTime(item.AppointmentEndTime);
                item.AppointmentEndTime = d2.ToString("hh:mm tt");
                if (item.Status == "C") { item.Status = "Cancelled"; }
                else if (item.Status == "B") { item.Status = "Booked"; }
                else if (item.Status == "R") { item.Status = "Rescheduled"; }
            }
            return this.Success(filterResponse);
        }

        /// <summary>The upload patient scan document.</summary>
        /// <param name="request">The request.</param>
        /// <param name="header">The header.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Patient scan document uploaded successfully.
        /// - 400 - We're sorry. There are no files to upload.
        /// - 400 - Maximum of 12 files can be allowed to upload.
        /// - 400 - Sorry! We don't have a patient in the system.
        /// - 417 - Invalid file type.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("upload-scan-document")]
        [Consumes("multipart/form-data")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(417)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UploadAsync([FromForm] UploadPatientScanDocument request, [FromHeader] LocationHeader header)
        {
            request = (UploadPatientScanDocument)EmptyFilter.Handler(request);
            var files = this.Request.Form.Files;
            if (files == null || files.Count == 0)
            {
                return this.BadRequest("We're sorry. There are no files to upload.");
            }
            if (files.Count > 10)
            {
                return this.BadRequest("Maximum of 10 files can be allowed to upload.");
            }
            var contentTypes = this.documentHelper.FetchContentTypes().ToList();
            if (!ListFilter.ContainsAll(contentTypes, files.Select(m => m.ContentType).Distinct()))
            {
                return this.Failed($"Only {string.Join(", ", contentTypes)} files are allowed.");
            }
            var scanDocuments = new List<ScanDocumentModel>();
            var index = 0;
            foreach (var file in files)
            {
                var model = new ScanDocumentModel
                {
                    Description = request.Description,
                    DocumentName = index == 0 ? request.DocumentName : $"{request.DocumentName}_{index}",
                    UploadedBy = request.UploadedBy,
                    BookScanAppointmentId = request.BookScanAppointmentId,
                    ContentType = file.ContentType,
                    Size = file.Length
                };
                var filePath = $@"{this.runningEnvironment.CurrentEnvironment}/ScanReports/{request.BookScanAppointmentId}";
                try
                {
                    await this.ftpUploadHelper.CreateDirectory(filePath);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                var dbPath = $@"{model.DocumentName}_{DateTime.UtcNow.Ticks}{Path.GetExtension(file.FileName)}";
                filePath += $@"/{dbPath}";
                var uploadResponse = await this.ftpUploadHelper.UploadFromFileAsync(filePath, file);
                if (uploadResponse <= 0)
                {
                    return this.BadRequest();
                }
                model.ThumbnailUrl = this.documentHelper.GetThumbnail(file.ContentType);
                model.DocumentUrl = dbPath;
                scanDocuments.Add(model);
                index++;
            }
            var response = await this.bookScanAppointmentService.AddScanDocumentAsync(scanDocuments);
            var enumerable = response.ToList();
            if (!enumerable.Any())
            {
                return this.ServerError();
            }
            var msg = $"Scan document{(files.Count > 0 ? "s" : string.Empty)} added";
            try
            {
                var scanLogModel = new ScanLogModel
                {
                    AccountId = request.UploadedBy,
                    ScanLogTypeId = (int)ScanLogTypes.ScanReport,
                    LogFrom = short.Parse(request.LoginRoleId),
                    LogDate = DateTime.UtcNow,
                    LocationId = Convert.ToInt32(header.LocationId),
                    LogDescription = $@"Uploaded Scan Document : <b>{request.DocumentName}</b> for  Patient: <b>{request.PatientName}</b>",
                };
                await this.scanLogService.LogAsync(scanLogModel);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return this.Success($"Scan document{(files.Count > 0 ? "s" : string.Empty)} has been uploaded successfully.");
        }

        /// <summary>The fetch patient scan documents.</summary>
        /// <param name="BookScanAppointmentId">The scan appointment id .</param>
        /// <returns>The list of patient scan documents.</returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of patientDocuments.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch-scan-document")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(List<PatientDocumentModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAsync([FromBody] int BookScanAppointmentId)
        {
            var scanDocuments = await this.bookScanAppointmentService.FetchScanDocumentAsync(BookScanAppointmentId);
            if (scanDocuments == null)
            {
                return this.ServerError();
            }
            return this.Success(scanDocuments);
        }

        /// <summary>The fetch patient scan documents.</summary>
        /// <param name="PatientId">The scan appointment id .</param>
        /// <returns>The list of patient scan documents.</returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of patientDocuments.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch-pacs-document")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(List<PatientDocumentModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchPacsImagesAsync([FromBody] int PatientId)
        {
            var scanDocuments = await this.bookScanAppointmentService.FetchPacsImagesAsync(PatientId);
            if (scanDocuments == null)
            {
                return this.ServerError();
            }
            return this.Success(scanDocuments);
        }

        /// <summary>
        /// The add of patient, before booking the scan appointment.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>
        /// </returns>
        private async Task<string> AddPatientsAsync(InsertBookScanAppointment model)
        {
            var singlePatient = new PatientModel();
            singlePatient.FirstName = model.FirstName;
            singlePatient.FullName = model.FirstName;
            singlePatient.Age = Convert.ToInt16(model.Age);
            singlePatient.Mobile = model.Mobile;
            singlePatient.CreatedBy = model.CreatedBy;
            singlePatient.LocationId = model.LocationId;
            singlePatient.CountryId = 1;
            singlePatient.PayTypeId = model.PayTypeId;
            singlePatient.PaymentStatus = model.PaymentStatus;
            singlePatient.PaymentNumber = model.PaymentNumber;
            singlePatient.Gender = model.Gender;
            singlePatient.AddressLine2 = model.Address;
            singlePatient.Amount = (decimal?)model.Charges;
            singlePatient.Total = (double)model.PatientTotal;
            singlePatient.DiscountInPercentage = (decimal)model.PatientDiscountInPercentage;
            singlePatient.DiscountInRupees = (decimal)model.PatientDiscountInRupees;
            model.RelationType = "";
            model.RelativeName = "";
            model.RelativeMobile = "";

            List<PatientFamilyModel> singlePatientFamily = new List<PatientFamilyModel>();
            List<PatientEmergencyModel> singleEmergencyPatientInfo = new List<PatientEmergencyModel> { new PatientEmergencyModel { FullName = model.RelativeName, Relation = model.RelationType, Mobile = model.RelativeMobile } };
            try
            {
                var (accountId, patientId, guid) = await this.patientService.AddAsync(singlePatient, singleEmergencyPatientInfo, new List<PatientInsuranceModel>(), new PatientFamilyModel(), singlePatientFamily ?? new List<PatientFamilyModel>());
                return patientId.ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
