using Dapper;
using Hangfire;
using Hims.Api.Senders;
using Hims.Domain.Helpers;
using Hims.Domain.Repositories.UnitOfWork;
using Hims.Domain.Services;
using Hims.Shared.EntityModels;
using Hims.Shared.Library.Enums;
using Hims.Shared.UserModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Hims.Api.Helper
{
    public class AppointmentHelper
    {
        public static AppointmentHelper appointmentHelper;

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

        /// <summary>
        /// The appointment services.
        /// </summary>
        private readonly IAppointmentService appointmentsServices;


        private readonly ISettingService settingService;

        /// <summary>
        /// The SMS sender.
        /// </summary>
        private readonly ISMSSender smsSender;

        /// <summary>
        /// The push notification helper.
        /// </summary>
        private readonly IPushNotificationHelper pushNotificationHelper;

        /// <summary>
        /// The email sender.
        /// </summary>
        private readonly IEmailSender emailSender;

        /// <summary>
        /// The account session services.
        /// </summary>
        private readonly IAccountSessionService accountSessionServices;

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

        /// <summary>
        /// The country services.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        public AppointmentHelper(IAuditLogService auditLogServices,
            IAppointmentService appointmentsServices,
            ISettingService settingService,
            IPushNotificationHelper pushNotificationHelper,
            ISMSSender smsSender,
            IEmailSender emailSender,
            IAccountSessionService accountSessionServices,
            IWhatsAppSMSSender whatsAppSMSSender,
            IUnitOfWork unitOfWork
            )
        {
            this.auditLogServices = auditLogServices;
            this.appointmentsServices = appointmentsServices;
            this.settingService = settingService;
            this.pushNotificationHelper = pushNotificationHelper;
            this.accountSessionServices = accountSessionServices;
            this.smsSender = smsSender;
            this.emailSender = emailSender;
            this.whatsAppSMSSender = whatsAppSMSSender;
            this.unitOfWork = unitOfWork;
        }

        public async Task<bool> CancelAppointmentAsync(AppointmentModel model, string type, string transaction, string transactionId, int? salucroStatusCode, string salucroTransactionId, int? modifiedBy, string reason)
        {
            try
            {
                var res = await this.appointmentsServices.CancelAppointmentAsync(model, type, transaction, transactionId, salucroStatusCode, salucroTransactionId, modifiedBy, reason);
                if (res == 0)
                {
                    return false;
                }
                var appointmentHangfireMapModel = new AppointmentHangfireMapModel()
                {
                    AppointmentId = model.AppointmentId,
                    AppointmentDate = model.AppointmentDate,
                    Status = "C",
                };
                await this.appointmentsServices.AddAppointmentHangfireAsync(appointmentHangfireMapModel);

                if (model != null)
                {
                    var timespan = new TimeSpan(model.AppointmentTime.Hours, model.AppointmentTime.Minutes, model.AppointmentTime.Seconds);
                    var time = DateTime.Today.Add(timespan);
                    var displayTime = time.ToString("hh:mm tt");
                    var messageType = "has been cancelled";

                    BackgroundJob.Enqueue(() => this.AppointmentActions(model, "Cancel", true));
                }
                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.Appointments,
                    LogFrom = model.LogFrom,
                    LogDate = DateTime.UtcNow,
                    LogDescription = $@"Patient: <b>{model.PatientName}</b>, Appointment on <b>{model?.AppointmentDate.ToString("MM/dd/yyyy")} {model?.AppointmentDate.Add(model.AppointmentTime).ToString("hh:mm tt")}</b>
                <br> has been <b>Cancelled</b> with Doctor: <b>Dr. {model.ProviderName}</b>.",
                    LocationId = model.LocationId
                };
                await this.auditLogServices.LogAsync(auditLogModel);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary> The appointment actions.</summary>
        /// <param name="appointment"> The appointment.</param>
        /// <param name="type"> The type.</param>
        /// <param name="toProvider"></param>
        /// <returns>The <see cref="Task"/>.</returns>
        public async Task<bool> AppointmentActions(AppointmentModel appointment, string type, bool toProvider = true)
        {
            if (appointment != null)
            {
                TimeSpan timespan = new TimeSpan(
                    appointment.AppointmentTime.Hours,
                    appointment.AppointmentTime.Minutes,
                    appointment.AppointmentTime.Seconds);
                DateTime time = DateTime.Today.Add(timespan);
                string displayTime = time.ToString("hh:mm tt");

                var prevDisplayTime = string.Empty;
                if (type == "ReSchedule" && appointment.AppointmentPreviousDate != null && appointment.AppointmentPreviousTime != null)
                {
                    TimeSpan prevTimespan = new TimeSpan(
                    appointment.AppointmentPreviousTime?.Hours ?? 0,
                    appointment.AppointmentPreviousTime?.Minutes ?? 0,
                    appointment.AppointmentPreviousTime?.Seconds ?? 0);
                    DateTime prevTime = DateTime.Today.Add(prevTimespan);
                    prevDisplayTime = prevTime.ToString("hh:mm tt");
                }

                string messageType = null;

                switch (type)
                {
                    case "Book":
                        messageType = "booked";
                        break;
                    case "ReSchedule":
                        messageType = "has been rescheduled successfully";
                        break;
                    case "Cancel":
                        messageType = "has been cancelled";
                        break;
                    case "Confirm":
                        messageType = "confirmed";
                        break;
                    case "Delay":
                        messageType = "delayed";
                        break;
                }
                var patientMessage = (type == "Confirm"
                            ? ("Your appointment with DR. " + appointment.ProviderName.ToUpper() + " will be started soon")
                            : type == "Delay"
                            ? ("Your appointment with DR. " + appointment.ProviderName.ToUpper() + " has been delayed by " + appointment.Delay)
                            : (appointment.PatientName + " patient " + messageType + " an Appointment with you on Date:" + appointment.AppointmentDate.ToString("dd MMMM yyyy") + " Time: " + displayTime + " "));

                var providerMessage = (type == "Confirm"
                            ? ("Your appointment with DR. " + appointment.ProviderName.ToUpper() + " will be started soon")
                            : type == "Delay"
                            ? ("Your appointment with DR. " + appointment.ProviderName.ToUpper() + " has been delayed by " + appointment.Delay)
                            : ("Appointment " + messageType + " with DR. " + appointment.ProviderName.ToUpper() + " on Date:" + appointment.AppointmentDate.ToString("dd MMMM yyyy") + " Time: " + displayTime + " "));
                bool isSMSSettingEnabled = await this.settingService.CheckSmsEnabled();
                if (isSMSSettingEnabled == false)
                {
                    // SMS to Doctor and Patient

                    await this.smsSender.SendAppointmentMessageAsync(appointment, messageType, displayTime);
                    // end
                }
                var WhatsAppMessageSetting = await this.settingService.FetchAsync("WhatsAppMsgService", null, null);
                var WhatsAppMessage = WhatsAppMessageSetting.ToList();
                if ((bool)WhatsAppMessage[0].Active)
                {
                    var LocationQuery = $@"select l.""LocationId"",l.""Name"",l.""PracticeId"",p.""FullName"" as ""PracticeName"" from ""Location"" l
                           join ""Practice"" p on p.""PracticeId"" = l.""PracticeId"" where ""LocationId"" = {appointment.LocationId} limit 1";

                    var Location = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<LocationModel>(LocationQuery);
                    bool ret = await this.whatsAppSMSSender.SendWhatsAppMessageAsync(appointment, displayTime, messageType, Location.Name);
                }

                // Sending push notifications to Doctor and Patient
                if (appointment.EnableMobileNotifications == true && toProvider)
                {
                    var accountSessionProviderModel = await this.accountSessionServices.FetchDeviceTokenAsync(appointment.ProviderId, Roles.Provider);
                    var sessionProviderModel = accountSessionProviderModel as AccountSessionModel[] ?? accountSessionProviderModel.ToArray();
                    if (sessionProviderModel.Any())
                    {
                        var deviceTokenForProviderAndroid = sessionProviderModel.Where(d => d.DeviceType == 2).Select(s => s.DeviceToken).ToList();
                        var deviceTokenForProviderIOS = sessionProviderModel.Where(d => d.DeviceType == 3).Select(s => s.DeviceToken).ToList();

                        if (deviceTokenForProviderAndroid.Any())
                        {
                            await this.pushNotificationHelper.SendAltAsync(
                                "Hims",
                                patientMessage,
                                (type == "Confirm" ? "Confirmed" : type == "Delay" ? "Delayed" : "Appointment"),
                                deviceTokenForProviderAndroid,
                                new List<string>(),
                                appointment.ProviderName,
                                (appointment.AppointmentDate.ToString("MM/dd/yyyy") + " " + displayTime),
                                (appointment.AppointmentPreviousDate != null ? (appointment.AppointmentPreviousDate?.ToString("MM/dd/yyyy") + " " + prevDisplayTime) : null));
                        }

                        if (deviceTokenForProviderIOS.Any())
                        {
                            await this.pushNotificationHelper.SendAltAsync(
                                "Hims",
                                patientMessage,
                                (type == "Confirm" ? "Confirmed" : type == "Delay" ? "Delayed" : "Appointment"),
                                new List<string>(),
                                deviceTokenForProviderIOS,
                                appointment.ProviderName,
                                (appointment.AppointmentDate.ToString("MM/dd/yyyy") + " " + displayTime),
                                (appointment.AppointmentPreviousDate != null ? (appointment.AppointmentPreviousDate?.ToString("MM/dd/yyyy") + " " + prevDisplayTime) : null));
                        }
                    }
                }

                var accountSessionPatientModel = await this.accountSessionServices.FetchDeviceTokenAsync(appointment.PatientId, Roles.Patient);
                var accountSessionModels = accountSessionPatientModel as AccountSessionModel[] ?? accountSessionPatientModel.ToArray();
                if (accountSessionModels.Any())
                {
                    var deviceTokenForProviderAndroid = accountSessionModels.Where(d => d.DeviceType == 2).Select(s => s.DeviceToken).ToList();
                    var deviceTokenForProviderIOS = accountSessionModels.Where(d => d.DeviceType == 3).Select(s => s.DeviceToken).ToList();
                    if (appointment.ProviderName != null)
                    {
                        if (deviceTokenForProviderAndroid.Any())
                        {
                            await this.pushNotificationHelper.SendAltAsync(
                                "Hims",
                                providerMessage,
                                (type == "Confirm" ? "Confirmed" : type == "Delay" ? "Delayed" : "Appointment"),
                                deviceTokenForProviderAndroid,
                                new List<string>(),
                                appointment.ProviderName,
                                (appointment.AppointmentDate.ToString("MM/dd/yyyy") + " " + displayTime),
                                (appointment.AppointmentPreviousDate != null ? (appointment.AppointmentPreviousDate?.ToString("MM/dd/yyyy") + " " + prevDisplayTime) : null));
                        }
                        if (deviceTokenForProviderIOS.Any())
                        {
                            await this.pushNotificationHelper.SendAltAsync(
                                "Hims",
                                providerMessage,
                                (type == "Confirm" ? "Confirmed" : type == "Delay" ? "Delayed" : "Appointment"),
                                new List<string>(),
                                deviceTokenForProviderIOS,
                                appointment.ProviderName,
                                (appointment.AppointmentDate.ToString("MM/dd/yyyy") + " " + displayTime),
                                (appointment.AppointmentPreviousDate != null ? (appointment.AppointmentPreviousDate?.ToString("MM/dd/yyyy") + " " + prevDisplayTime) : null));
                        }
                    }
                }

                // end

                bool isEmailSettingEnabled = await this.settingService.CheckEmailEnabled();
                if (isEmailSettingEnabled == false)
                {
                    if (!string.IsNullOrEmpty(appointment.PatientEmail))
                    {
                        await this.emailSender.SendBookAppointmentMailAsync(
                            appointment.PatientEmail,
                            appointment.PatientName,
                            "Patient",
                            appointment.ProviderName,
                            appointment.AppointmentDate.ToString("dd MMMM yyyy") + ", " + displayTime,
                            messageType);
                    }

                    if (!string.IsNullOrEmpty(appointment.ProviderEmail) && appointment.EnableEmailAlerts == true)
                    {
                        await this.emailSender.SendBookAppointmentMailAsync(
                            appointment.ProviderEmail,
                            appointment.PatientName,
                            "Doctor",
                            appointment.ProviderName,
                            appointment.AppointmentDate.ToString("dd MMMM yyyy") + ", " + displayTime,
                            messageType);
                    }
                }
            }

            return true;
        }

        public static AppointmentHelper GetInstance(IAuditLogService auditLogServices,
            IAppointmentService appointmentsServices,           
            ISettingService settingService,
            IPushNotificationHelper pushNotificationHelper,
            ISMSSender smsSender,
            IEmailSender emailSender,
            IAccountSessionService accountSessionServices,
            IWhatsAppSMSSender whatsAppSMSSender,
            IUnitOfWork unitOfWork)
        {
            return new AppointmentHelper(auditLogServices, appointmentsServices, settingService, pushNotificationHelper, smsSender, emailSender, accountSessionServices,whatsAppSMSSender,unitOfWork);
        }
    }
}
