namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Threading.Tasks;

    using Domain.Helpers;
    using Domain.Services;

    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.SignalR;

    using Shared.DataFilters;
    using Shared.EntityModels;

    using Utilities;

    using Hims.Api.Hubs;
    using Hims.Shared.Library.Enums;

    /// <inheritdoc />
    [Route("api/message")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class MessagesController : BaseController
    {
        /// <summary>
        /// The message service.
        /// </summary>
        private readonly IMessageService messageService;

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

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

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

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

        /// <inheritdoc />
        public MessagesController(IMessageService messageService, IHubContext<CommunicationHub> hubContext, IAccountSessionService accountSessionServices, IPushNotificationHelper pushNotificationHelper, IAppointmentService appointmentsServices)
        {
            this.messageService = messageService;
            this.hubContext = hubContext;
            this.accountSessionServices = accountSessionServices;
            this.pushNotificationHelper = pushNotificationHelper;
            this.appointmentsServices = appointmentsServices;
        }

        /// <summary>
        /// The add messages async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Authorize]
        [Route("add")]
        public async Task<ActionResult> AddMessagesAsync([FromBody] MessageModel model)
        {
            model = (MessageModel)EmptyFilter.Handler(model);
            var response = await this.messageService.CreateMessageAsync(model);
            await this.SendMessagePushNotifications(model);
            return this.Success(response);
        }

        /// <summary>
        /// The fetch message async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Authorize]
        [Route("fetch")]
        public async Task<ActionResult> FetchMessageAsync([FromBody] MessageModel model)
        {
            model = (MessageModel)EmptyFilter.Handler(model);
            var response = await this.messageService.FetchMessagesAsync(model.AppointmentId);
            if (response != null)
            {
                await this.messageService.ChangeReadStatus(response.MessageId, model.RequestFrom);
            }

            return this.Success(response);
        }

        /// <summary>
        /// The update messages async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Authorize]
        [Route("update")]
        public async Task<ActionResult> UpdateMessagesAsync([FromBody] MessageModel model)
        {
            model = (MessageModel)EmptyFilter.Handler(model);
            var response = await this.messageService.UpdateMessageAsync(model);
            await this.SendMessagePushNotifications(model);
            var refreshModel = new CommunicationMessage
            {
                UniqueId = model.MessageId.ToString(),
                MainId = model.ProviderId,
                AccountId = model.PatientId,
                Type = CommunicationType.MessageRefresh
            };
            await this.hubContext.Clients.All.SendAsync("Communication", refreshModel).ConfigureAwait(false);
            return this.Success(response);
        }

        /// <summary>
        /// The send message push notifications.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Reviewed. Suppression is OK here.")]
        [NonAction]
        private async Task<bool> SendMessagePushNotifications(MessageModel model)
        {
            try
            {
                var appointment = await this.appointmentsServices.FindAsync(model.AppointmentId);
                
                // Using prevDate to send AppointmentId in Push Notification.
                
                switch (model.RequestFrom)
                {
                    case "Patient":
                        {
                            var accountSessionProviderModel =
                                await this.accountSessionServices.FetchDeviceTokenAsync(model.ProviderId, Roles.Provider);
                            var sessionProviderModel = accountSessionProviderModel as AccountSessionModel[]
                                                       ?? accountSessionProviderModel.ToArray();

                            var patientMessage = $@"New message from patient {appointment.PatientName}";

                            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,
                                        "New Message",
                                        deviceTokenForProviderAndroid,
                                        new List<string>(),
                                        appointment.PatientName,
                                        DateTime.Now.ToString("MM/dd/yyyy hh:mm tt"),
                                        model.AppointmentId.ToString());
                                }

                                if (deviceTokenForProviderIOS.Any())
                                {
                                    await this.pushNotificationHelper.SendAltAsync(
                                        "Hims",
                                        patientMessage,
                                        "New Message",
                                        new List<string>(),
                                        deviceTokenForProviderIOS,
                                        appointment.PatientName,
                                        DateTime.Now.ToString("MM/dd/yyyy hh:mm tt"),
                                        model.AppointmentId.ToString());
                                }
                            }

                            break;
                        }

                    case "Provider":
                        {
                            var message = $@"New Message from Dr.{appointment.ProviderName}";
                            var accountSessionPatientModel =
                                await this.accountSessionServices.FetchDeviceTokenAsync(model.PatientId, Roles.Patient);
                            var sessionPatientModel = accountSessionPatientModel as AccountSessionModel[]
                                                      ?? accountSessionPatientModel.ToArray();
                            if (sessionPatientModel.Any())
                            {
                                var deviceTokenForPatientAndroid = sessionPatientModel.Where(d => d.DeviceType == 2).Select(s => s.DeviceToken).ToList();
                                var deviceTokenForPatientIOS = sessionPatientModel.Where(d => d.DeviceType == 3).Select(s => s.DeviceToken).ToList();

                                if (deviceTokenForPatientAndroid.Any())
                                {
                                    await this.pushNotificationHelper.SendAltAsync(
                                        "Hims",
                                        message,
                                        "New Message",
                                        deviceTokenForPatientAndroid,
                                        new List<string>(),
                                        appointment.ProviderName,
                                        DateTime.Now.ToString("MM/dd/yyyy hh:mm tt"),
                                        model.AppointmentId.ToString());
                                }

                                if (deviceTokenForPatientIOS.Any())
                                {
                                    await this.pushNotificationHelper.SendAltAsync(
                                        "Hims",
                                        message,
                                        "New Message",
                                        new List<string>(),
                                        deviceTokenForPatientIOS,
                                        appointment.ProviderName,
                                        DateTime.Now.ToString("MM/dd/yyyy hh:mm tt"),
                                        model.AppointmentId.ToString());
                                }
                            }

                            break;
                        }
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}