#nullable enable
namespace Hims.Api.Controllers
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading.Tasks;
    using Domain.Configurations;
    using Domain.Helpers;
    using Domain.Services;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.SignalR;

    using Newtonsoft.Json;
    using Shared.EntityModels;

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

    /// <summary>
    /// The pay u money controller.
    /// </summary>
    [Route("api/payment")]
    [Consumes("application/json")]
    [Produces("application/json")]
    [AllowAnonymous]
    public class PayUMoneyController : Controller
    {
        /// <summary>
        /// The appointment services.
        /// </summary>
        private readonly IAppointmentService appointmentServices;

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

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

        /// <summary>
        /// The appointment log services.
        /// </summary>
        private readonly IAppointmentLogService appointmentLogServices;

        /// <summary>
        /// The pay u configuration.
        /// </summary>
        private readonly IPayUConfiguration payUConfiguration;

        /// <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 hub context.
        /// </summary>
        private readonly IHubContext<CommunicationHub> hubContext;

        /// <summary>
        /// The appointment exception log services.
        /// </summary>
        private readonly IAppointmentExceptionLogService appointmentExceptionLogServices;

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

        /// <summary>
        /// Initializes a new instance of the <see cref="PayUMoneyController"/> class. Initializes a new instance of the <see cref="PaymentController"/> class.
        /// </summary>
        /// <param name="timelineService">
        /// The timeline Service.
        /// </param>
        /// <param name="appointmentServices">
        /// The appointment services.
        /// </param>
        /// <param name="aesHelper">
        /// The aes helper.
        /// </param>
        /// <param name="appointmentLogServices">
        /// The appointment Log Services.
        /// </param>
        /// <param name="payUConfiguration">
        /// The pay U Configuration.
        /// </param>
        /// <param name="smsSender">
        /// The sms Helper.
        /// </param>
        /// <param name="appointmentExceptionLogServices">
        /// The appointment Exception Log Services.
        /// </param>
        /// <param name="accountSessionServices">
        /// The account Session Services.
        /// </param>
        /// <param name="pushNotificationHelper">
        /// The push Notification Helper.
        /// </param>
        /// <param name="emailSender">
        /// The email Sender.
        /// </param>
        /// <param name="hubContext">
        /// The hub Context.
        /// </param>
        public PayUMoneyController(ITimelineService timelineService, IAppointmentService appointmentServices, IAESHelper aesHelper, IAppointmentLogService appointmentLogServices, IPayUConfiguration payUConfiguration, ISMSSender smsSender, IAppointmentExceptionLogService appointmentExceptionLogServices, IAccountSessionService accountSessionServices, IPushNotificationHelper pushNotificationHelper, IEmailSender emailSender, IHubContext<CommunicationHub> hubContext)
        {
            this.appointmentServices = appointmentServices;
            this.aesHelper = aesHelper;
            this.appointmentLogServices = appointmentLogServices;
            this.payUConfiguration = payUConfiguration;
            this.smsSender = smsSender;
            this.appointmentExceptionLogServices = appointmentExceptionLogServices;
            this.accountSessionServices = accountSessionServices;
            this.pushNotificationHelper = pushNotificationHelper;
            this.emailSender = emailSender;
            this.hubContext = hubContext;
            this.timelineService = timelineService;
        }

        /// <summary>
        /// The Payment.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("success")]
        [ProducesResponseType(typeof(PayuMoneyModel), 200)]
        [ProducesResponseType(typeof(string), 400)]
        [ProducesResponseType(500)]
        public async Task<IActionResult> PaymentSuccess([FromBody] PayuMoneyModel model)
        {
            await this.appointmentExceptionLogServices.LogAsync(
                new AppointmentExceptionLogModel
                {
                    AppointmentId = null,
                    AppointmentLogId = null,
                    PaymentTransactionId = "",
                    PaymentStatus = false,
                    StackTrace = null,
                    ExceptionMessage = "webhook testing " + JsonConvert.SerializeObject(model)
                });

            if (model.udf1 != "null")
            {

                await this.appointmentExceptionLogServices.LogAsync(
                new AppointmentExceptionLogModel
                {
                    AppointmentId = null,
                    AppointmentLogId = null,
                    PaymentTransactionId = "",
                    PaymentStatus = false,
                    StackTrace = null,
                    ExceptionMessage = JsonConvert.SerializeObject(model)
                });
                var transactionId = model.merchantTransactionId;
                // var appointmentLogId = Convert.ToInt32(model.udf1);
                int appointmentLogId = 0;
                try
                {
                    //if (model.status == "Success")
                    // {
                    // var bankReference = collection["bank_ref_num"];
                    // var bankCode = collection["bankcode"];
                    var paymentId = Convert.ToInt32(model.paymentId);

                    // var transaction = ToJson(collection);
                    var bankReference = string.Empty;
                    var bankCode = string.Empty;
                    var transaction = JsonConvert.SerializeObject(model);

                    if (model != null)
                    {
                        appointmentLogId = Convert.ToInt32(model.udf1);
                    }
                    var (appointmentId, isExists, errorMessage) = await this.appointmentServices.AddAsync(
                                                                      appointmentLogId,
                                                                      transactionId,
                                                                      transaction,
                                                                      bankReference,
                                                                      bankCode,
                                                                      paymentId,0,null);
                    if (!string.IsNullOrEmpty(errorMessage))
                    {
                        await this.appointmentExceptionLogServices.LogAsync(
                            new AppointmentExceptionLogModel
                            {
                                AppointmentId = null,
                                AppointmentLogId = appointmentLogId,
                                PaymentTransactionId = transactionId,
                                PaymentStatus = true,
                                ExceptionMessage = errorMessage,
                                StackTrace = null
                            });
                    }

                    switch (appointmentId)
                    {
                        case -1:
                            return this.View(
                                this.payUConfiguration.RedirectFailureLink + this.aesHelper.Encode(transactionId));
                        case 0:
                            return this.View(
                                this.payUConfiguration.RedirectFailureLink + this.aesHelper.Encode(""));
                    }

                    var appointment = await this.appointmentServices.FindAsync(appointmentId);

                    if (!isExists)
                    {
                        var timespan = new TimeSpan(
                            appointment.AppointmentTime.Hours,
                            appointment.AppointmentTime.Minutes,
                            appointment.AppointmentTime.Seconds);
                        var time = DateTime.Today.Add(timespan);
                        var displayTime = time.ToString("hh:mm tt");

                        // Sending Message
                        await this.smsSender.SendPaymentMessageAsync(appointment, displayTime);

                        // end 

                        // Sending push notifications to Provider and Patient
                        if (appointment.EnableMobileNotifications == true)
                        {
                            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();
                                await this.pushNotificationHelper.SendAltAsync(
                                    "Hims",
                                    appointment.PatientName + " patient booked an Appointment with you on Date:"
                                                            + appointment.AppointmentDate.ToString("dd MMMM yyyy")
                                                            + " Time: " + displayTime + " ",
                                    "Appointment",
                                    deviceTokenForProviderAndroid,
                                    deviceTokenForProviderIOS,
                                    appointment.ProviderName,
                                    appointment.AppointmentDate.ToString("MM/dd/yyyy") + " " + displayTime);
                            }
                        }

                        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)
                            {
                                _ = await this.pushNotificationHelper.SendAltAsync(
                                        "Hims",
                                        "Appointment booked with DR. " + appointment.ProviderName.ToUpper()
                                                                       + " on Date:"
                                                                       + appointment.AppointmentDate.ToString(
                                                                           "dd MMMM yyyy") + " Time: " + displayTime
                                                                       + " ",
                                        "Appointment",
                                        deviceTokenForProviderAndroid,
                                        deviceTokenForProviderIOS,
                                        appointment.ProviderName,
                                        appointment.AppointmentDate.ToString("MM/dd/yyyy") + " " + displayTime);
                            }
                        }

                        // end
                        if (!string.IsNullOrEmpty(appointment.PatientEmail))
                        {
                            await this.emailSender.SendBookAppointmentMailAsync(
                                appointment.PatientEmail,
                                appointment.PatientName,
                                "Patient",
                                appointment.ProviderName,
                                appointment.AppointmentDate.ToString("dd MMMM yyyy") + ", " + displayTime,
                                "booked");
                        }

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

                        await this.timelineService.LogAsync(new TimelineModel
                        {
                            PatientId = appointment.PatientId,
                            AppointmentId = appointmentId,
                            TimelineActionId = TimelineAction.AppointmentAdded,
                            CreatedBy = appointment.CreatedBy,
                            Description = $"Appointment has been booked"
                        });
                        var refreshModel = new CommunicationMessage
                        {
                            Content = appointment?.AppointmentNo,
                            MainId = appointment?.ProviderId ?? 0,
                            Type = CommunicationType.AppointmentAddRefresh
                        };
                        await this.hubContext.Clients.All.SendAsync("Communication", refreshModel)
                            .ConfigureAwait(false);

                        return this.Redirect(
                            this.payUConfiguration.RedirectSuccessLink
                            + this.aesHelper.Encode(appointmentId.ToString()));
                    }

                    // await this.appointmentLogServices.DeleteAsync(appointmentLogId);
                    await this.appointmentExceptionLogServices.LogAsync(
                        new AppointmentExceptionLogModel
                        {
                            AppointmentId = null,
                            AppointmentLogId = appointmentLogId,
                            PaymentTransactionId = transactionId,
                            PaymentStatus = false,
                            StackTrace = null
                        });
                    return this.View(this.payUConfiguration.RedirectFailureLink + this.aesHelper.Encode(transactionId));
                }
                catch (Exception exception)
                {
                    await this.appointmentExceptionLogServices.LogAsync(
                        new AppointmentExceptionLogModel
                        {
                            AppointmentId = null,
                            AppointmentLogId = appointmentLogId,
                            PaymentTransactionId = transactionId,
                            PaymentStatus = false,
                            ExceptionMessage = exception.Message,
                            StackTrace = exception.StackTrace
                        });

                    //  return this.View(this.payUConfiguration.RedirectFailureLink + this.aesHelper.Encode(transactionId));
                    return Ok();
                }
            }
            else
            {
                await this.appointmentExceptionLogServices.LogAsync(
                    new AppointmentExceptionLogModel
                    {
                        AppointmentId = null,
                        AppointmentLogId = null,
                        PaymentTransactionId = "",
                        PaymentStatus = false,
                        StackTrace = null,
                        ExceptionMessage = "model is null"
                    });

                // return this.View(this.payUConfiguration.RedirectFailureLink + this.aesHelper.Encode(""));
                return Ok();
            }
        }
    }
}