using Hims.Domain.Entities;
using Hims.Shared.UserModels.Telemedicine;

namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading.Tasks;
    using Domain.Configurations;
    using Domain.Helpers;
    using Domain.Services;
    using Hubs;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.SignalR;
    using Microsoft.IdentityModel.Tokens;
    using Models.Appointment;
    using Models.WebTelemedicine;
    using Newtonsoft.Json;
    using Senders;
    using Shared.EntityModels;
    using Shared.Library.Enums;
    using Shared.UserModels;
    using Utilities;

    /// <summary>
    /// The common response.
    /// </summary>
    public class CommonResponse
    {
        /// <summary>
        /// Gets or sets the content.
        /// </summary>
        public string Content { get; set; }

        /// <summary>
        /// Gets or sets the response.
        /// </summary>
        public string Response { get; set; }

        /// <summary>
        /// Gets or sets the telemedicine id.
        /// </summary>
        public int TelemedicineId { get; set; }

        /// <summary>
        /// Gets or sets the name of the template.
        /// </summary>
        /// <value>
        /// The name of the template.
        /// </value>
        public string TemplateName { get; set; }
    }

    /// <inheritdoc />
    /// <summary>
    /// The web telemedicine controller.
    /// </summary>
    [Route("api/web-telemedicine")]
    [Consumes("application/json")]
    [Produces("application/json")]
    // ReSharper disable once StyleCop.SA1402
    public class WebTelemedicineController : BaseController
    {
        /// <summary>
        /// The Web Telemedicine Service.
        /// </summary>
        private readonly IWebTelemedicineService webTelemedicineService;

        /// <summary>
        /// The hub context.
        /// </summary>
        private readonly IHubContext<CommunicationHub> hubContext;

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

        /// <summary>
        /// The application configuration.
        /// </summary>
        private readonly IApplicationConfiguration applicationConfiguration;

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

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

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

        /// <summary>
        /// The account services.
        /// </summary>
        private readonly IAccountService accountServices;

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

        /// <summary>
        /// The timeline service.
        /// </summary>
        private readonly ITimelineService timelineService;

        /// <summary>
        /// The timeline service.
        /// </summary>
        private readonly IURLShortnerHelper uRLShortnerHelper;

        /// <inheritdoc />
        public WebTelemedicineController(
            IPushNotificationHelper pushNotificationHelper,
            IWebTelemedicineService webTelemedicineService,
            IHubContext<CommunicationHub> hubContext,
            IAESHelper aesHelper, IAccountService accountServices,
            ISMSSender smsSender,
            IApplicationConfiguration applicationConfiguration,
            IEmailSender emailSender,
            IAppointmentService appointmentServices,
            ITimelineService timelineService,
            IURLShortnerHelper uRLShortnerHelper)
        {
            this.webTelemedicineService = webTelemedicineService;
            this.hubContext = hubContext;
            this.aesHelper = aesHelper;
            this.accountServices = accountServices;
            this.smsSender = smsSender;
            this.applicationConfiguration = applicationConfiguration;
            this.emailSender = emailSender;
            this.pushNotificationHelper = pushNotificationHelper;
            this.appointmentServices = appointmentServices;
            this.timelineService = timelineService;
            this.uRLShortnerHelper = uRLShortnerHelper;
        }

        /// <summary>
        /// Notifies the helper.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="callLog">The call log.</param>
        /// <returns></returns>
        public async Task<CommonResponse> NotifyHelper(CommunicationMessage model, CallLog callLog)
        {
            try
            {
                NotificationType? notificationType = null;
                var subResponse = string.Empty;
                var telemedicineId = model.TelemedicineId;
                var templateName = string.Empty;
                var subTitle = string.Empty;
                int? opponentId = null;
                bool sendNotificationForOtherUsers = false;

                IEnumerable<AccountSessionModel> deviceTokens = new List<AccountSessionModel>();
                var allDeviceTokens = new List<AccountSessionModel>();
                if (model.Type == CommunicationType.Call)
                {
                    notificationType = NotificationType.Call;
                    subTitle = "Incoming call from " + model.Message;
                    deviceTokens = await this.webTelemedicineService.GetPatientDeviceTokens(model.Content);
                    allDeviceTokens.AddRange(deviceTokens);
                    var patientAccountId = await this.webTelemedicineService.GetPatientAccountId(model.Content);
                    opponentId = patientAccountId;
                    if (telemedicineId <= 0)
                    {
                        telemedicineId = await this.webTelemedicineService.CreateTelemedicineHistory(model.Content, model.AccountId, model.StartAudioOnly ? 2 : 1);
                        model.TelemedicineId = telemedicineId;
                        telemedicineId = model.TelemedicineId;

                        var response = await this.webTelemedicineService.PatientCallIntimateAsync(telemedicineId, patientAccountId, model.AccountId);
                        subResponse += response > 0 ? "success " : "Failed ";
                    }
                    else
                    {
                        var initializeCallResponse = await this.webTelemedicineService.InitializeCallAsync(telemedicineId, model.AccountId);
                        subResponse += initializeCallResponse > 0 ? "success " : "Failed ";
                        var opponentResponse = await this.webTelemedicineService.PatientCallIntimateAsync(telemedicineId, patientAccountId, model.AccountId);
                        subResponse += opponentResponse > 0 ? "success " : "Failed ";
                    }
                }
                else if (model.Type == CommunicationType.Initiate)
                {
                    notificationType = (model.StartAudioOnly ? NotificationType.AudioCallInitiate : NotificationType.VideoCallInitiate);
                    subTitle = model.Message + " has initiated " + (model.StartAudioOnly ? "Audio" : "Video") + " call, you can join now.";
                    deviceTokens = await this.webTelemedicineService.GetPatientDeviceTokens(model.Content);
                    allDeviceTokens.AddRange(deviceTokens);
                    var patientAccountId = await this.webTelemedicineService.GetPatientAccountId(model.Content);
                    opponentId = patientAccountId;
                    if (telemedicineId <= 0)
                    {
                        telemedicineId = await this.webTelemedicineService.CreateTelemedicineHistory(model.Content, model.AccountId, model.StartAudioOnly ? 2 : 1);
                        model.TelemedicineId = telemedicineId;
                        telemedicineId = model.TelemedicineId;

                        var response = await this.webTelemedicineService.InitiatePatientCallIntimateAsync(telemedicineId, patientAccountId, model.AccountId);
                        subResponse += response > 0 ? "success " : "Failed ";
                    }
                }
                else if (model.Type == CommunicationType.Notify)
                {
                    notificationType = NotificationType.Notify;
                    subTitle = "Incoming call from " + model.Message;
                    if (model.RoleId == 3)
                    {
                        var providerAccountId = await this.webTelemedicineService.GetDoctorAccountId(model.Content);
                        opponentId = providerAccountId;
                        if (telemedicineId <= 0)
                        {
                            telemedicineId = await this.webTelemedicineService.CreateTelemedicineHistory(model.Content, model.AccountId, model.StartAudioOnly ? 2 : 1);
                            model.TelemedicineId = telemedicineId;
                            telemedicineId = model.TelemedicineId;

                            // Technically DoctorCallIntimateAsync
                            var response = await this.webTelemedicineService.PatientCallIntimateAsync(telemedicineId, providerAccountId, model.AccountId);
                            subResponse += response > 0 ? "success " : "Failed ";
                        }
                        else
                        {
                            // Technically DoctorCallIntimateAsync
                            var response = await this.webTelemedicineService.PatientCallIntimateAsync(telemedicineId, providerAccountId, model.AccountId);
                            subResponse = response > 0 ? "success" : "Failed";
                        }

                        deviceTokens = model.NotifyToId > 0
                            ? await this.webTelemedicineService.GetNurseOrReceptionistDeviceTokensAlt(model.RoleId, model.NotifyToId)
                            : await this.webTelemedicineService.GetDoctorDeviceTokens(model.Content);
                        allDeviceTokens.AddRange(deviceTokens);
                    }
                    else
                    {
                        if (model.NotifyToId > 0)
                        {
                            opponentId = model.NotifyToId;
                            if (telemedicineId <= 0)
                            {
                                telemedicineId = await this.webTelemedicineService.CreateTelemedicineHistory(model.Content, model.AccountId, model.StartAudioOnly ? 2 : 1);
                                model.TelemedicineId = telemedicineId;
                                telemedicineId = model.TelemedicineId;

                                // Technically Nurse or Receptionist CallIntimateAsync
                                var response = await this.webTelemedicineService.PatientCallIntimateAsync(telemedicineId, model.NotifyToId, model.AccountId);
                                subResponse += response > 0 ? "success " : "Failed ";
                            }
                            else
                            {
                                // Technically Nurse or Receptionist CallIntimateAsync
                                var response = await this.webTelemedicineService.PatientCallIntimateAsync(telemedicineId, model.NotifyToId, model.AccountId);
                                subResponse = response > 0 ? "success" : "Failed";
                            }

                            deviceTokens = await this.webTelemedicineService.GetNurseOrReceptionistDeviceTokensAlt(model.RoleId, model.NotifyToId);
                            allDeviceTokens.AddRange(deviceTokens);
                        }
                    }
                }
                else if (model.Type == CommunicationType.Join)
                {
                    notificationType = NotificationType.Join;
                    subTitle = model.Message + " has joined the call";
                    if (telemedicineId == 0)
                    {
                        var id = await this.webTelemedicineService.GetReceiverTelemedicineId(model.Content, model.AccountId);
                        if (id > 0)
                        {
                            telemedicineId = id;
                        }
                    }

                    var subTelemedicineId = await this.webTelemedicineService.CallJoinAsync(telemedicineId, model.AccountId);
                    subResponse = subTelemedicineId > 0 ? "success" : "Failed";

                    deviceTokens = await this.webTelemedicineService.GetJoinDeviceTokensAsync(telemedicineId, model.AccountId);
                    allDeviceTokens.AddRange(deviceTokens);

                    sendNotificationForOtherUsers = true;
                }
                else if (model.Type == CommunicationType.ExternalJoin)
                {
                    notificationType = NotificationType.Join;
                    subTitle = model.Message + " has joined the call";
                    if (telemedicineId == 0)
                    {
                        if (model.AccountId == 0)
                        {
                            model.AccountId = await this.webTelemedicineService.GetPatientAccountId(model.Content);
                        }

                        var id = await this.webTelemedicineService.GetReceiverTelemedicineId(model.Content, model.AccountId);
                        if (id > 0)
                        {
                            telemedicineId = id;
                        }
                        else
                        {
                            telemedicineId = await this.webTelemedicineService.CreateTelemedicineHistory(model.Content, model.AccountId, model.StartAudioOnly ? 2 : 1);
                            model.TelemedicineId = telemedicineId;
                            telemedicineId = model.TelemedicineId;
                        }
                    }

                    var subTelemedicineId = await this.webTelemedicineService.CallJoinAsync(telemedicineId, model.AccountId);
                    subResponse = subTelemedicineId > 0 ? "success" : "Failed";

                    deviceTokens = await this.webTelemedicineService.GetJoinDeviceTokensAsync(telemedicineId, model.AccountId);
                    allDeviceTokens.AddRange(deviceTokens);
                }
                else if (model.Type == CommunicationType.Reject)
                {
                    notificationType = NotificationType.Reject;
                    subTitle = model.Message + " rejected your call";
                    if (telemedicineId == 0)
                    {
                        var id = await this.webTelemedicineService.GetReceiverTelemedicineId(model.Content, model.AccountId);
                        if (id > 0)
                        {
                            telemedicineId = id;
                        }
                    }

                    var response = await this.webTelemedicineService.CallRejectAsync(telemedicineId, model.AccountId);
                    subResponse = response > 0 ? "success" : "Failed";

                    deviceTokens = await this.webTelemedicineService.GetJoinDeviceTokensAsync(telemedicineId, model.AccountId);
                    allDeviceTokens.AddRange(deviceTokens);

                    sendNotificationForOtherUsers = true;
                    // deviceTokens = await this.webTelemedicineService.GetDeviceTokensByAccount(model.AccountId);
                }
                else if (model.Type == CommunicationType.Cancel)
                {
                    notificationType = NotificationType.Cancel;
                    subTitle = model.Message + " cancelled your call";
                    var response = await this.webTelemedicineService.CallCancelAsync(telemedicineId);
                    subResponse = response > 0 ? "success" : "Failed";

                    deviceTokens = await this.webTelemedicineService.GetPatientDeviceTokens(model.Content);
                    allDeviceTokens.AddRange(deviceTokens);
                    sendNotificationForOtherUsers = true;
                }
                else if (model.Type == CommunicationType.Close)
                {
                    notificationType = NotificationType.Close;
                    subTitle = model.Message + " left from the call";
                    if (telemedicineId == 0)
                    {
                        var id = model.RoleId == 3
                            ? await this.webTelemedicineService.GetCallerTelemedicineId(model.Content, model.AccountId)
                            : await this.webTelemedicineService.GetReceiverTelemedicineId(model.Content, model.AccountId);

                        if (id > 0)
                        {
                            telemedicineId = id;
                        }
                    }

                    //var subTelemedicineId = model.RoleId == 3
                    //    ? await this.webTelemedicineService.DoctorCallCloseAsync(telemedicineId, model.AccountId)
                    //    : await this.webTelemedicineService.OthersCallCloseAsync(telemedicineId, model.AccountId);

                    var subTelemedicineId = await this.webTelemedicineService.OthersCallCloseAsync(telemedicineId, model.AccountId);
                    //if (model.TemplateId > 0)
                    //{
                    //    templateName = await this.webTelemedicineService.SetTemplateAsync(telemedicineId, model.TemplateId ?? 0, model.AccountId);
                    //}

                    deviceTokens = await this.webTelemedicineService.GetJoinDeviceTokensAsync(telemedicineId, model.AccountId);
                    allDeviceTokens.AddRange(deviceTokens);
                    subResponse = subTelemedicineId > 0 ? "success" : "Failed";
                }
                else if (model.Type == CommunicationType.CloseAll)
                {
                    notificationType = NotificationType.CloseAll;
                    subTitle = model.Message + $" ended the {(model.StartAudioOnly ? "Audio" : "Video")} call";
                    if (telemedicineId == 0)
                    {
                        var id = await this.webTelemedicineService.GetCallerTelemedicineId(model.Content, model.AccountId);
                        if (id > 0)
                        {
                            telemedicineId = id;
                        }
                    }

                    deviceTokens = await this.webTelemedicineService.GetCallCloseAllDeviceTokensAsync(telemedicineId, model.AccountId);
                    allDeviceTokens.AddRange(deviceTokens);

                    var response = await this.webTelemedicineService.CallCloseAllAsync(telemedicineId);
                    subResponse = response > 0 ? "success" : "Failed";

                    if (response == -1)
                    {
                        subTitle = $"You've missed the {(model.StartAudioOnly ? "Audio" : "Video")} call from {model.Message}";
                    }

                    var appointment = await this.appointmentServices.FindByAppointmentNoAsync(model.Content);
                    if (appointment != null)
                    {
                        await this.timelineService.LogAsync(new TimelineModel
                        {
                            PatientId = appointment.PatientId,
                            AppointmentId = appointment.AppointmentId,
                            TimelineActionId = TimelineAction.AppointmentAdded,
                            CreatedBy = model.AccountId,
                            Description = $"{(model.StartAudioOnly ? "Audio" : "Video")} call Completed"
                        });
                    }
                    sendNotificationForOtherUsers = true;
                }

                if (sendNotificationForOtherUsers)
                {
                    var teleMedicineCallHistory = await this.webTelemedicineService.GetTelemedicineCallHistory(model.Content, model.AccountId);
                    if (teleMedicineCallHistory.TelemedicineCallHistoryId != 0)
                    {
                        //To send notifications to other users
                        var account = await this.accountServices.FindAsync(teleMedicineCallHistory.CreatedBy);
                        var appointmentInfo = await this.appointmentServices.FindByAppointmentNoAsync(model.Content);
                        if (account.RoleId == 6 || account.RoleId == 7 || account.RoleId == 11 || account.RoleId == 12)
                        {
                            //If Assistant get doctor
                            var otherUsersDeviceTokens = await this.webTelemedicineService.GetProviderDeviceTokensAsync(appointmentInfo.ProviderId);
                            if (otherUsersDeviceTokens.Count() > 0)
                            {
                                allDeviceTokens.AddRange(otherUsersDeviceTokens);
                            }

                        }
                        else if (account.RoleId == 3)
                        {
                            //If Doctor get assistants
                            var assistantIds = await this.webTelemedicineService.GetAssistantIdsAsync(appointmentInfo.ProviderId);
                            if (assistantIds.Count() > 0)
                            {
                                var otherUsersDeviceTokens = await this.webTelemedicineService.GetAssistantsDeviceTokensAsync(string.Join(',', assistantIds.ToArray()));
                                if (otherUsersDeviceTokens.Count() > 0)
                                    allDeviceTokens.AddRange(otherUsersDeviceTokens);
                            }
                        }
                    }
                }
                // Action Complete Intimate
                await this.hubContext.Clients.All.SendAsync("Communication", new CommunicationMessage
                {
                    Content = model.Content,
                    Type = CommunicationType.AppointmentRefresh
                }).ConfigureAwait(false);

                // Notification
                if (allDeviceTokens != null && allDeviceTokens.Any())
                {
                    var userName = await this.webTelemedicineService.GetRoomName(model.Content);
                    var roomName = model.Content + "-" + userName.Trim().Replace(' ', '-');
                    var domain = await this.webTelemedicineService.GetActiveDomain(null);

                    var notification = new NotificationIntimateModel
                    {
                        Title = $"Virtual Steth {(model.StartAudioOnly ? "Audio" : "Video")} Call",
                        SubTitle = subTitle,
                        AccountId = model.AccountId,
                        DomainName = domain.Domain,
                        TelemedicineId = telemedicineId,
                        RoomName = model.Content,
                        StartAudioOnly = model.StartAudioOnly,
                        OriginalRoomName = roomName,
                        Devices = new List<NotificationDeviceModel>(),
                        Message = model.Message,
                        IsCall = model.Type == CommunicationType.Call || model.Type == CommunicationType.Notify || model.Type==CommunicationType.Initiate,
                        Type = model.Type == CommunicationType.Join
                            ? "Telemedicine Joined"
                            : model.Type == CommunicationType.Reject
                                ? "Telemedicine Rejected"
                                : model.Type == CommunicationType.CloseAll
                                    ? "Telemedicine Ended"
                                    : model.Type == CommunicationType.Cancel
                                        ? "Telemedicine Cancel"
                                        : model.Type == CommunicationType.Close
                                            ? "Telemedicine Close"
                                        : model.Type == CommunicationType.Initiate
                                            ? "Telemedicine Initiate"
                                            : "Telemedicine"
                    };

                    foreach (var device in allDeviceTokens)
                    {
                        notification.Devices.Add(
                            new NotificationDeviceModel
                            {
                                DeviceType = device.DeviceType,
                                DeviceToken = device.DeviceToken
                            });
                    }

                    var restResponse = await this.pushNotificationHelper.TelemedicineCallNotification(notification, notificationType);

                    callLog.AccountId = model.AccountId;
                    callLog.StartDate = DateTime.UtcNow;
                    callLog.EndDate = DateTime.UtcNow;
                    callLog.OpponentId = opponentId;
                    callLog.TelemedicineId = telemedicineId;
                    callLog.Payload = JsonConvert.SerializeObject(model);
                    await this.webTelemedicineService.CallLogAsync(callLog);

                    return new CommonResponse
                    {
                        Content = restResponse.Content,
                        TelemedicineId = telemedicineId,
                        Response = subResponse
                    };
                }

                return new CommonResponse
                {
                    Content = "No device tokens found.",
                    TelemedicineId = telemedicineId,
                    Response = subResponse
                };
            }
            catch (Exception ex)
            {
                // ignore
                return new CommonResponse
                {
                    Content = ex.Message,
                    TelemedicineId = model.TelemedicineId
                };
            }
        }

        /// <summary>
        /// Tests the notification.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Route("test-notification")]
        [AllowAnonymous]
        public async Task<ActionResult> TestNotification([FromBody] Shared.UserModels.Common.CommonNotificationModel model)
        {
            var response = await this.pushNotificationHelper.TestNotification(model.DeviceToken, NotificationType.TestNotification);
            return this.Ok(response);
        }

        /// <summary>
        /// Tests the notification.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Route("custom-notification")]
        [AllowAnonymous]
        public async Task<ActionResult> CustomNotification([FromBody] string model)
        {
            try
            {
                var response = await this.pushNotificationHelper.CustomNotification(model);
                return this.Ok(response);
            }
            catch (Exception e)
            {
                return this.ServerError(JsonConvert.SerializeObject(e));
            }
        }

        /// <summary>
        /// The notify helper.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        public async Task<CommonResponse> FrequentIosCallHelper(CommunicationMessage model)
        {
            try
            {
                var subResponse = string.Empty;
                var telemedicineId = model.TelemedicineId;
                var subTitle = string.Empty;

                IEnumerable<AccountSessionModel> deviceTokens = new List<AccountSessionModel>();
                if (model.Type == CommunicationType.Call)
                {
                    subTitle = "Incoming call from " + model.Message;
                    deviceTokens = await this.webTelemedicineService.GetPatientIosDeviceTokens(model.Content);
                }

                if (deviceTokens != null && deviceTokens.Any())
                {
                    var userName = await this.webTelemedicineService.GetRoomName(model.Content);
                    var roomName = model.Content + "-" + userName.Trim().Replace(' ', '-');
                    var domain = await this.webTelemedicineService.GetActiveDomain(null);

                    var notification = new NotificationIntimateModel
                    {
                        Title = $"Virtual Steth {(model.StartAudioOnly ? "Audio" : "Video")} Call",
                        SubTitle = subTitle,
                        DomainName = domain.Domain,
                        TelemedicineId = telemedicineId,
                        RoomName = model.Content,
                        StartAudioOnly = model.StartAudioOnly,
                        OriginalRoomName = roomName,
                        Devices = new List<NotificationDeviceModel>(),
                        Message = model.Message,
                        IsCall = model.Type == CommunicationType.Call || model.Type == CommunicationType.Notify,
                        Type = model.Type == CommunicationType.Reject ? "Telemedicine Rejected" : model.Type == CommunicationType.CloseAll ? "Telemedicine Ended" : model.Type == CommunicationType.Cancel ? "Telemedicine Cancel" : "Telemedicine"
                    };

                    foreach (var device in deviceTokens)
                    {
                        notification.Devices.Add(
                            new NotificationDeviceModel
                            {
                                DeviceType = device.DeviceType,
                                DeviceToken = device.DeviceToken
                            });
                    }

                    var restResponse = await this.pushNotificationHelper.TelemedicineCallNotification(notification, NotificationType.FrequentCall);
                    return new CommonResponse
                    {
                        Content = restResponse.Content,
                        TelemedicineId = telemedicineId,
                        Response = subResponse
                    };
                }

                return new CommonResponse
                {
                    Content = "No device tokens found.",
                    TelemedicineId = telemedicineId,
                    Response = subResponse
                };
            }
            catch (Exception ex)
            {
                // ignore
                return new CommonResponse
                {
                    Content = ex.Message,
                    TelemedicineId = model.TelemedicineId
                };
            }
        }

        /// <summary>
        /// The update count async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("ios-frequent-notification")]
        public async Task<ActionResult> FrequentNotificationAsync([FromBody] CommunicationMessage model)
        {
            var response = await this.FrequentIosCallHelper(model);
            //for (var i = 0; i <= 3; i++)
            //{
            //    await Task.Delay(5000).ConfigureAwait(false);
            //    response = await this.FrequentIosCallHelper(model);
            //}

            return this.Ok(response);
        }

        /// <summary>
        /// The update count async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("call-web")]
        public async Task<ActionResult> CallWebAsync([FromBody] CommunicationMessage model)
        {
            if (model == null)
            {
                return this.Ok("model is null");
            }

            var callLog = new CallLog
            {
                AppointmentNo = model.Content,
                CallLogTypeId = (int)model.Type,
                StartDate = DateTime.Now,
                Note = model.ClientName
            };

            model.ClientName = null;
            await this.hubContext.Clients.All.SendAsync("Communication", model).ConfigureAwait(false);

            var response = await this.NotifyHelper(model, callLog);
            // model.TelemedicineId = response.TelemedicineId;
            // model.TemplateName = response.TemplateName;=
            return this.Ok(response);
        }

        /// <summary>
        /// The update count async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("call-log")]
        public async Task<ActionResult> ManualLogAsync([FromBody] CommunicationMessage model)
        {
            if (model == null)
            {
                return this.Ok("model is null");
            }

            var callLog = new CallLog
            {
                AppointmentNo = model.Content,
                CallLogTypeId = (int)model.Type,
                StartDate = DateTime.UtcNow,
                Note = model.ClientName,
                AccountId = model.AccountId,
                EndDate = DateTime.UtcNow,
                OpponentId = null,
                TelemedicineId = model.TelemedicineId,
                Payload = JsonConvert.SerializeObject(model),
                Info = model.UniqueId
            };
            var response = await this.webTelemedicineService.CallLogAsync(callLog);
            return this.Ok(response);
        }

        /// <summary>
        /// The update count async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-all-templates")]
        public async Task<ActionResult> GetAllTemplatesAsync([FromBody] CommunicationMessage model)
        {
            var records = await this.webTelemedicineService.GetAllTemplates();
            return this.Ok(records ?? new List<TelemedicineTemplateModel>());
        }

        /// <summary>
        /// The update count async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("close-rooms")]
        public async Task<ActionResult> CloseRoomsAsync([FromBody] List<CommunicationMessage> model)
        {
            if (model == null)
            {
                return this.Ok("model is null");
            }

            foreach (var data in model)
            {
                data.Type = CommunicationType.CloseAll;
                var callLog = new CallLog
                {
                    AppointmentNo = data.Content,
                    CallLogTypeId = (int)data.Type,
                    StartDate = DateTime.Now
                };
                await this.NotifyHelper(data, callLog);
                await this.hubContext.Clients.All.SendAsync("Communication", model).ConfigureAwait(false);
            }

            return this.Ok();
        }

        /// <summary>
        /// The fetch patient appointments async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-nurses-receptionists")]
        public async Task<ActionResult> GetNursesOrReceptionistsAsync([FromBody] TelemedicineUserReceiveModel model)
        {
            try
            {
                var records = new List<TelemedicineUserModel>();
                var info = await this.webTelemedicineService.GetAppointmentDetails(model.AppointmentNo);
                if (info != null)
                {
                    records.Add(new TelemedicineUserModel
                    {
                        AccountId = info.ProviderAccountId,
                        FullName = info.ProviderFullName,
                        RoleId = 3,
                        RoleName = "Doctor"
                    });

                    records.Add(new TelemedicineUserModel
                    {
                        AccountId = info.PatientAccountId,
                        FullName = info.PatientFullName,
                        RoleId = 4,
                        RoleName = "Patient"
                    });
                }

                var response = await this.webTelemedicineService.GetNursesOrReceptionists(model.AppointmentNo);
                if (response != null)
                {
                    records.AddRange(response);
                }

                return this.Ok(records);
            }
            catch
            {
                return this.Ok(new List<TelemedicineUserModel>());
            }
        }

        /// <summary>
        /// The get appointment details async.
        /// </summary>
        /// <param name="model">
        /// The appointment no.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-appointment-details")]
        public async Task<ActionResult> GetAppointmentDetailsAsync([FromBody] TelemedicineAppointmentDetailModel model)
        {
            try
            {
                var response = await this.webTelemedicineService.GetAppointmentDetails(model.AppointmentNo);
                response.EncryptedAppointmentId = this.aesHelper.Encode(response.AppointmentId.ToString());
                response.AppointmentId = 0;

                return this.Ok(response);
            }
            catch (Exception ex)
            {
                return this.Ok(ex.Message);
            }
        }

        /// <summary>
        /// The get appointments async.
        /// </summary>
        /// <param name="patientId">
        /// The patient id.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-appointments")]
        public async Task<ActionResult> FetchPatientAppointmentsAsync([FromBody] int patientId)
        {
            var response = await this.webTelemedicineService.FetchPatientAppointmentsAsync(patientId);
            return this.Ok(response);
        }

        /// <summary>
        /// The get appointments async.
        /// </summary>
        /// <param name="model"></param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-provider-appointments")]
        public async Task<ActionResult> FetchProviderAppointmentsAsync([FromBody] FetchAppointmentNumbersRequest model)
        {
            if (model == null)
            {
                return this.Ok();
            }

            var response = await this.webTelemedicineService.FetchProviderAppointmentsAsync(model.ProviderId, model.PracticeId);
            return this.Ok(response);
        }

        /// <summary>
        /// The get room names async.
        /// </summary>
        /// <param name="appointmentIds">
        /// The appointment ids.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-room-names")]
        public async Task<ActionResult> GetRoomNamesAsync([FromBody] List<int> appointmentIds)
        {
            try
            {
                var response = await this.webTelemedicineService.FetchRoomNamesAsync(appointmentIds);
                return this.Ok(response);
            }
            catch
            {
                return this.Ok();
            }
        }

        /// <summary>
        /// The get token.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-authentication-info")]
        public async Task<ActionResult> GetTokenAsync([FromBody] WebTelemedicineTokenRequest model)
        {
            try
            {
                var userName = await this.webTelemedicineService.GetRoomName(model.RoomName);
                var roomName = model.RoomName + "-" + userName.Replace(' ', '-');
                var domain = await this.webTelemedicineService.GetActiveDomain(null);
                domain.RoomName = roomName;

                if (domain.IsAuthenticationRequired)
                {
                    var user = await this.webTelemedicineService.GetUserAccount(model.AccountId);
                    var token = this.TokenHelper(roomName, user);
                    domain.Token = token;
                }

                return this.Ok(domain);
            }
            catch
            {
                return this.Ok();
            }
        }

        /// <summary>
        /// The get token.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-room-name")]
        public async Task<ActionResult> GetRoomNameAsync([FromBody] WebTelemedicineTokenRequest model)
        {
            try
            {
                var userName = await this.webTelemedicineService.GetRoomName(model.RoomName);
                var roomName = model.RoomName + "-" + userName.Trim().Replace(' ', '-');
                var domain = await this.webTelemedicineService.GetActiveDomain(null);
                domain.RoomName = roomName;

                return this.Ok(domain);
            }
            catch
            {
                return this.Ok();
            }
        }

        /// <summary>
        /// The get token.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("get-active-room-info")]
        public async Task<ActionResult> GetActiveRoomInfoAsync([FromBody] WebTelemedicineTokenRequest model)
        {
            try
            {
                var response = await this.webTelemedicineService.GetActiveRoomName(model.RoomName);
                if (response.Status != 1)
                {
                    return this.Ok(response);
                }

                var roomName = model.RoomName + "-" + response.Data.ToString()?.Trim().Replace(' ', '-');
                var domain = await this.webTelemedicineService.GetActiveDomain(model.RoomName);
                domain.RoomName = roomName;
                response.Data = domain;
                return this.Ok(response);
            }
            catch
            {
                return this.Ok();
            }
        }

        /// <summary>
        /// The get token.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("send-link")]
        public async Task<ActionResult> SendVideoLinkAsync([FromBody] VideoLinkRequest model)
        {
            try
            {
                var response = await this.webTelemedicineService.GetActiveRoomName(model.AppointmentNo);
                if (response.Status != 1)
                {
                    return this.Ok(response);
                }

                var roomName = model.AppointmentNo + "-" + response.Data.ToString()?.Trim().Replace(' ', '-');
                var domain = await this.webTelemedicineService.GetActiveDomain(null);
                domain.RoomName = roomName;
                response.Data = domain;

                var patient = await this.webTelemedicineService.GetPatientDetails(model.AppointmentNo);
                var provider = await this.webTelemedicineService.GetProviderDetails(model.AppointmentNo);

                if (patient == null)
                {
                    response.Status = 4;
                    response.Message = "Patient details not found";
                    return Ok(response);
                }

                if (provider == null)
                {
                    response.Status = 4;
                    response.Message = "Provider details not found";
                    return Ok(response);
                }

                // var link = "https://" + domain.Domain + "/" + roomName;
                var link = this.applicationConfiguration.WebsiteLink +  "/site/video-call-conference/" + model.AppointmentNo;
                var url = await this.uRLShortnerHelper.MakeShorterUrl(link);
                var iosLink = await this.uRLShortnerHelper.MakeShorterUrl(this.applicationConfiguration.IOSAppLink);
                var androidLink = await this.uRLShortnerHelper.MakeShorterUrl(this.applicationConfiguration.AndroidAppLink);

                var smsResponse = await this.smsSender.SendVideoLinkAsync(patient.Mobile, patient.CountryId, url, patient.FullName, provider.FullName);
                // smsResponse = await this.smsSender.SendAppLinksAsync(patient.Mobile, patient.CountryId);
                var emailResponse = await this.emailSender.SendVideoLinkAsync(patient.Email, patient.FullName, provider.FullName, link);

                if (smsResponse && emailResponse)
                {
                    response.Status = 1;
                    response.Message = "Video conference link sent successfully";
                    return Ok(response);
                }

                if (smsResponse)
                {
                    response.Status = 1;
                    response.Message = "Video conference link sent to mobile.";
                    return Ok(response);
                }

                if (emailResponse)
                {
                    response.Status = 1;
                    response.Message = "Video conference link sent to email.";
                    return Ok(response);
                }

                return this.Ok(response);
            }
            catch (Exception ex)
            {
                return this.Ok(new StatusResponse
                {
                    Status = 4,
                    Message = ex.Message
                });
            }
        }

        /// <summary>
        /// The get token.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("get-call-history")]
        public async Task<ActionResult> CallHistoryAsync([FromBody] CallHistoryRequest model)
        {
            try
            {
                var data = new TelemedicineHistory();
                if (model.IsFirstTime)
                {
                    var telemedicineIds = await this.webTelemedicineService.GetNoOfCalls(model.AppointmentId);
                    var enumerable = telemedicineIds.ToList();
                    if (enumerable.Any())
                    {
                        model.TelemedicineId = enumerable.First();
                        data.TelemedicineIds = enumerable;
                    }
                }

                if (model.TelemedicineId > 0)
                {
                    var callHistory = await this.webTelemedicineService.GetCallHistory(model.AppointmentId, model.TelemedicineId);
                    var callHistoryList = callHistory.ToList();
                    callHistoryList.RemoveAt(callHistoryList.Count - 1);
                    var first = callHistoryList.First();
                    first.CallStatus = first.ModifiedDate != null ? "Complete" : "In progress";
                    if (first.ModifiedDate != null)
                    {
                        var duration = string.Empty;
                        var timeSpan = (first.ModifiedDate - first.CreatedDate).Value;
                        if (timeSpan.Hours > 0)
                        {
                            var hours = timeSpan.Hours;
                            duration += hours + " " + (hours > 1 ? "Hours" : "Hour") + " ";
                        }

                        if (timeSpan.Minutes > 0)
                        {
                            var minutes = timeSpan.Minutes;
                            duration += minutes + " " + (minutes > 1 ? "Minutes" : "Minute") + " ";
                        }

                        if (timeSpan.Seconds > 0)
                        {
                            var seconds = timeSpan.Seconds;
                            duration += seconds + " " + (seconds > 1 ? "Seconds" : "Second") + " ";
                        }

                        first.Duration = duration;
                    }
                    else
                    {
                        first.Duration = string.Empty;
                    }

                    foreach (var history in callHistoryList)
                    {
                        var tokens = history.FullName.Split(' ');
                        if (tokens.Length >= 2)
                        {
                            history.ShortName = tokens[0][0] + string.Empty + tokens[1][0];
                        }
                        else
                        {
                            history.ShortName = tokens[0][0] + string.Empty + tokens[0][1];
                        }

                        var callerTokens = history.CallerFullName.Split(' ');
                        if (callerTokens.Length >= 2)
                        {
                            history.CallerShortName = callerTokens[0][0] + string.Empty + callerTokens[1][0];
                        }
                        else
                        {
                            history.CallerShortName = callerTokens[0][0] + string.Empty + callerTokens[0][1];
                        }

                        switch (history.UserCallStatus)
                        {
                            case 1:
                                history.UserCallStatusName = "Missed Call";
                                break;
                            case 2:
                                history.UserCallStatusName = "Call Complete";
                                break;
                            case 3:
                                history.UserCallStatusName = "Joined & left in the middle";
                                break;
                            case 4:
                                history.UserCallStatusName = "Call Rejected";
                                break;
                        }
                    }

                    data.CallHistory = callHistoryList;
                }

                return this.Ok(data);
            }
            catch
            {
                return this.Ok();
            }
        }

        /// <summary>
        /// The create redirect url.
        /// </summary>
        /// <param name="roomName">
        /// The room name.
        /// </param>
        /// <param name="user">
        /// The user.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public string TokenHelper(string roomName, TelemedicineUserModel user)
        {
            var header = new
            {
                alg = "HS256",
                typ = "JWT"
            };

            var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
            var issueTime = DateTime.UtcNow;
            var exp = (int)issueTime.AddHours(1).Subtract(utc0).TotalSeconds; // Expiration time is up to 1 min, but lets play on safe side

            var headerPart = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(header));
            var payload = new
            {
                context = new
                {
                    user = new
                    {
                        avatar = string.Empty,
                        name = user.FullName,
                        email = user.Email,
                        id = Guid.NewGuid()
                    }
                },
                aud = "virtualsteth-call",
                iss = "virtualsteth-call",
                sub = "meet.virtualsteth.com",
                room = roomName,
                exp
            };

            var payloadPart = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(payload));

            const string Secret = "QAZ2WSX#SRE4RFV";
            var sha256 = new HMACSHA256(Encoding.UTF8.GetBytes(Secret));
            var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes($"{headerPart}.{payloadPart}"));
            var hash = Base64UrlEncoder.Encode(hashBytes);

            return $"{headerPart}.{payloadPart}.{hash}";
        }
    }
}