namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Domain.Services;
    using Hims.Api.Models;
    using Hims.Domain.Helpers;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.Library.Enums;
    using Shared.UserModels.PatientMedication;
    using Shared.UserModels.WebNotification;
    using Shared.UserModels.Laboratory;
    using Shared.UserModels.ChargeModule;
    using Utilities;
    using Hims.Api.Helper;
    using Hims.Shared.Library.Enums;
    using Hims.Shared.UserModels.Labs;

    /// <inheritdoc />

    [Route("api/patient-encounter")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class PatientEncounterController : BaseController
    {
        /// <summary>
        /// The patient medication service
        /// </summary>
        private readonly IPatientEncounterService patientEncounterService;

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

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

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

        /// <summary>
        /// The laboratory service.
        /// </summary>
        private readonly ILabLogService labLogService;

        /// <summary>
        /// The laboratory service.
        /// </summary>
        private readonly ILaboratoryService laboratoryService;

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

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

        /// <summary>
        /// The charge module service.
        /// </summary>
        private readonly IChargeModuleService chargeModuleService;

        /// <inheritdoc />
        public PatientEncounterController(IPatientEncounterService patientEncounterService,
            IWebNotificationService webNotificationService,
            IAppointmentService appointmentsServices,
            IAESHelper aesHelper,
            ILabLogService labLogService,
            ILaboratoryService laboratoryService,
            IPushNotificationHelper pushNotificationHelper,
            IAccountSessionService accountSessionServices,
            IChargeModuleService chargeModuleService
            )
        {
            this.patientEncounterService = patientEncounterService;
            this.webNotificationService = webNotificationService;
            this.appointmentsServices = appointmentsServices;
            this.aesHelper = aesHelper;
            this.labLogService = labLogService;
            this.laboratoryService = laboratoryService;
            this.pushNotificationHelper = pushNotificationHelper;
            this.accountSessionServices = accountSessionServices;
            this.chargeModuleService = chargeModuleService;
        }

        /// <summary>
        /// Adds the patient medication asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>

        [HttpPost]
        [Authorize]
        [Route("modify-medication")]
        public async Task<ActionResult> AddPatientMedicationAsync([FromBody] PatientMedicationHeaderModel model)
        {
            model = (PatientMedicationHeaderModel)EmptyFilter.Handler(model);
            model.AppointmentId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedAppointmentId));

            var response = await this.patientEncounterService.AddPatientMedicationAsync(model);
            var encounter = new EncounterTypes();
            switch (model.EncounterType)
            {
                case "internal-medicine":
                    encounter = EncounterTypes.InternalMedicine;
                    break;
                case "behavioral-health":
                    encounter = EncounterTypes.BehavioralHealth;
                    break;
                case "gyn-encounter":
                    encounter = EncounterTypes.GynEncounter;
                    break;
                case "ob-encounter":
                    encounter = EncounterTypes.OBEncounter;
                    break;
                case "ivf-encounter":
                    encounter = EncounterTypes.IvfEncounter;
                    break;
            }

            if (response > 0 && !model.IsAdmission)
            {
                try
                {
                    await this.appointmentsServices.UpdateEncounterTypeAsync((int)model.AppointmentId,
                       (int)encounter, model.IsAdmission);

                    var appointment = await this.appointmentsServices.FindAppointmentAsync((int)model.AppointmentId, false);

                    // for mobile notification.
                    //var basicDetails = await this.encounterServices.GetBasicAppointmentDetails(model.AppointmentId, model.IsAdmission);
                    try
                    {
                        await NotificationHelper.Notification(
                       appointment.PatientId,
                              Roles.Patient,
                                model.Medicines.Count > 1
                                    ? NotificationIntimate.PrescriptionUpdated
                                    : NotificationIntimate.PrescriptionAdded,
                               this.aesHelper.Encode(model.AppointmentId.ToString()),
                               this.accountSessionServices,
                               this.pushNotificationHelper);
                    }
                    catch (Exception e)
                    {
                        //ignore
                    }

                    if (appointment == null)
                    {
                        return this.Success(response);
                    }
                    var url = "app/pharmacy/sales-bill";
                    var roles = (await this.webNotificationService.GetAllRoles()).ToList();
                    var getRolesToNotify = roles.Where(r => r.RoleName.ToLower().Contains("pharm"));
                    var getAdmins = roles.Where(r => r.RoleName.ToLower().Contains("admin"));
                    var finalRoleModel = new List<RoleModel>();
                    finalRoleModel.AddRange(getRolesToNotify.ToList());
                    finalRoleModel.AddRange(getAdmins.ToList());
                    var notification = new WebNotificationModel
                    {
                        AllowedAccounts = string.Empty,
                        AllowedRoles = string.Join(",", finalRoleModel.Select(r => r.RoleId)),
                        CreatedDate = DateTime.Now,
                        IsRead = false,
                        Message = $@"{appointment.ProviderName} prescribed medicine",
                        RedirectionLink = url,
                        WebNotificationLogTypeId = (int)WebNotificationLogType.View,
                        WebNotificationPriorityId = (int)WebNotificationPriority.High,
                        PatientId = appointment.PatientId,
                        ReferenceId = model.AppointmentId,
                        ModulesMasterId = (int)ModulesMasterType.Pharmacy
                    };
                    await this.webNotificationService.InsertAsync(notification);

                }
                catch (Exception e)
                {
                    // ignore
                }

            }

            return this.Success(response);
        }

        /// <summary>
        /// Fetches the patient medications asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch-medication")]
        public async Task<ActionResult> FetchPatientMedicationsAsync([FromBody] PatientMedicationHeaderModel model)
        {
            model = (PatientMedicationHeaderModel)EmptyFilter.Handler(model);
            if (!string.IsNullOrEmpty(model.EncryptedAppointmentId))
            {
                model.AppointmentId = int.Parse(this.aesHelper.Decode(model.EncryptedAppointmentId));
            }
            var response = await this.patientEncounterService.FetchPatientMedication(model);
            foreach (var item in response)
            {
                item.EncryptedAppointmentId = this.aesHelper.Encode(item.AppointmentId.ToString());
                item.EncryptedPatientId = this.aesHelper.Encode(item.PatientId.ToString());
                item.AppointmentTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd")).Add(item.AppointmentTimeString).ToString("hh:mm tt");

            }
            return this.Success(response);
        }

        /// <summary>
        /// Deletes the single medications asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("delete-single-medication")]
        public async Task<ActionResult> DeleteSingleMedicationsAsync([FromBody] PatientMedicationDetailModel model)
        {
            model = (PatientMedicationDetailModel)EmptyFilter.Handler(model);
            var response = await this.patientEncounterService.DeleteSingleMedicationAsync(model);
            return this.Success(response);
        }

        /// <summary>
        /// Adds the encounter labs of patient asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("modify-encounter-lab")]
        public async Task<ActionResult> AddEncounterLabsOfPatientAsync([FromBody] AddBooking model)
        {


            model = (AddBooking)EmptyFilter.Handler(model);
            var response = await this.patientEncounterService.AddPatientLabAsync(model);
            var encounter = new EncounterTypes();
            switch (model.EncounterType)
            {
                case "internal-medicine":
                    encounter = EncounterTypes.InternalMedicine;
                    break;
                case "behavioral-health":
                    encounter = EncounterTypes.BehavioralHealth;
                    break;
                case "gyn-encounter":
                    encounter = EncounterTypes.GynEncounter;
                    break;
                case "ob-encounter":
                    encounter = EncounterTypes.OBEncounter;
                    break;
                case "ivf-encounter":
                    encounter = EncounterTypes.IvfEncounter;
                    break;
            }
            try
            {
                if (!model.IsAdmission)
                {
                    await this.appointmentsServices.UpdateEncounterTypeAsync((int)model.AppointmentId,
                       (int)encounter, model.IsAdmission);
                }
                
            }
            catch (Exception)
            {
                // ignore
            }
            if (response > 0 && !model.IsAdmission)
            {
                try
                {
                    var appointment = await this.appointmentsServices.FindAppointmentAsync((int)model.AppointmentId, false);
                    if (appointment == null)
                    {
                        return this.Success(response);
                    }

                    var url = "app/labs/lab-billing";
                    var roles = (await this.webNotificationService.GetAllRoles()).ToList();
                    var getRolesToNotify = roles.Where(r => r.RoleName.ToLower().Contains("lab"));
                    var getAdmins = roles.Where(r => r.RoleName.ToLower().Contains("admin"));
                    var finalRoleModel = new List<RoleModel>();
                    finalRoleModel.AddRange(getRolesToNotify.ToList());
                    finalRoleModel.AddRange(getAdmins.ToList());
                    var notification = new WebNotificationModel
                    {
                        AllowedAccounts = string.Empty,
                        AllowedRoles = string.Join(",", finalRoleModel.Select(r => r.RoleId)),
                        CreatedDate = DateTime.Now,
                        IsRead = false,
                        Message = $@"{appointment.ProviderName} advised labs",
                        RedirectionLink = url,
                        WebNotificationLogTypeId = (int)WebNotificationLogType.View,
                        WebNotificationPriorityId = (int)WebNotificationPriority.High,
                        PatientId = appointment.PatientId,
                        ReferenceId = model.AppointmentId,
                        ModulesMasterId = (int)ModulesMasterType.Lab
                    };
                    await this.webNotificationService.InsertAsync(notification);
                }
                catch (Exception e)
                {
                    // ignore
                }

            }
            return this.Success(response);
        }

        /// <summary>
        /// Fetches the patient labs asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="location"></param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch-encounter-lab")]
        public async Task<ActionResult> FetchPatientLabsAsync([FromBody] PatientMedicationHeaderModel model, [FromHeader] LocationHeader location)
        {
            model = (PatientMedicationHeaderModel)EmptyFilter.Handler(model);
            model.LocationId = location != null && !string.IsNullOrEmpty(location.LocationId) ? Convert.ToInt32(location.LocationId) : (int?)null;
            if (!string.IsNullOrEmpty(model.EncryptedAppointmentId))
            {
                model.AppointmentId = int.Parse(this.aesHelper.Decode(model.EncryptedAppointmentId));
            }
            var response = await this.patientEncounterService.FetchPatientLabsAsync(model);
            var getModulesData = await this.chargeModuleService.GetRequiredDetailForChargeFetchAsync("lab", (int)model.LocationId);
            if(getModulesData == null && (int)model.LocationId == 0)
            {
                return this.Success(response);
            }
            if (getModulesData == null)
            {
                return this.Success(new List<PatientMedicationHeaderModel>());
            }

            foreach (var item in response)
            {
                item.EncryptedAppointmentId = this.aesHelper.Encode(item.AppointmentId.ToString());
                item.EncryptedPatientId = this.aesHelper.Encode(item.PatientId.ToString());
                foreach (var lab in item.Labs)
                {
                    lab.Charges = new List<ChargeModuleDetailsModel>();
                    getModulesData.ReferenceId = lab.LabMainDetailId;
                    lab.Charges = (await this.chargeModuleService.FetchGivenChargesAsync(getModulesData)).ToList();
                }
            }
            return this.Success(response);
        }

        /// <summary>
        /// Deletes the single lab asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("delete-single-lab")]
        public async Task<ActionResult> DeleteSingleLabAsync([FromBody] PatientMedicationDetailModel model)
        {
            model = (PatientMedicationDetailModel)EmptyFilter.Handler(model);
            var response = await this.patientEncounterService.DeleteSingleLabAsync(model);
            return this.Success(response);
        }

        /// <summary>
        /// Verifies the lab report for encounter without package asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="header">The header.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("verify-lab-non-package")]
        public async Task<ActionResult> VerifyLabReportForEncounterWithoutPackageAsync([FromBody] RequestLaboratory model, [FromHeader] LocationHeader header)
        {
            model.LocationId = Convert.ToInt32(header.LocationId);
            model = (RequestLaboratory)EmptyFilter.Handler(model);
            var response = await this.patientEncounterService.VerifyLabReportWithoutPackageAsync((int)model.LabBookingDetailId, (int)model.ProviderId);

            try
            {
                var labLogModel = new LabLogModel
                {
                    AccountId = model.AccountId,
                    LabLogTypeId = (int)LabLogTypes.Lab_BookingDetails,
                    LogFrom = (short)model.LoginRoleId,
                    LocationId = (int)model.LocationId,
                    LogDate = DateTime.Now,
                    LogDescription = $@" '{model.VerifiedByName}' has verified the Lab '{model.LabName}' under Bill number '{model.BillNumber}'."
                };
                await this.labLogService.LogAsync(labLogModel);
            }
            catch (Exception)
            {
                // ignore
            }

            return this.Success(response);
        }

        /// <summary>
        /// Verifies the lab reprt with package asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="header">The header.</param>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        [Route("verify-package-report")]
        public async Task<ActionResult> VerifyLabReprtWithPackageAsync([FromBody] RequestLaboratory model, [FromHeader] LocationHeader header)
        {
            model = (RequestLaboratory)EmptyFilter.Handler(model);
            model.LocationId = Convert.ToInt32(header.LocationId);
            var response = await this.patientEncounterService.VerifyLabBookingPackageAsync((int)model.ProviderId, model.LabBookingDetailId, model.LabBookingPackageDetailId);

            try
            {
                var labDetails = new LabHeaderModel();
                if (model.LabBookingPackageDetailId != null)
                {
                    labDetails = await this.laboratoryService.FetchLab((int)model.LabBookingPackageDetailId);
                }
                var labLogModel = new LabLogModel
                {
                    AccountId = model.AccountId,
                    LabLogTypeId = (int)LabLogTypes.Lab_BookingDetails,
                    LogFrom = (short)model.LoginRoleId,
                    LogDate = DateTime.Now,
                    LocationId = (int)model.LocationId,
                    //LogDescription = $@"'{model.CreatedByName}' has verified lab test name '{labDetails.LabName}' in the package name'{model.PackageName}' under bill no '{model.BillNumber}'."
                    LogDescription = model.LabBookingDetailId != null ? $@"'{model.CreatedByName}' has verified Package '{model.PackageName}' under bill number '{model.BillNumber}'." : $@"'{model.CreatedByName}' has verified lab test name '{labDetails.LabName}' in the Package name'{model.PackageName}' under bill number '{model.BillNumber}'."
                };
                await this.labLogService.LogAsync(labLogModel);
            }
            catch (Exception)
            {
                // ignore
            }
            return this.Success(response);
        }

        /// <summary>
        /// Fetches the frequently used labs asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Route("frequently-used-lab")]
        public async Task<ActionResult> FetchFrequentlyUsedLabsAsync([FromBody] LabMainDetailModel model)
        {
            model ??= new LabMainDetailModel();
            var response = await this.patientEncounterService.FetchFrequentlyUsedLabsAsync(model);
            return this.Success(response);
        }

        /// <summary>
        /// Fetches the frequently used medication asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Route("frequently-used-medication")]
        public async Task<ActionResult> FetchFrequentlyUsedMedicationAsync([FromBody] PatientMedicationDetailModel model)
        {
            model ??= new PatientMedicationDetailModel();
            var response = await this.patientEncounterService.FetchFrequentlyUsedMedications(model);
            return this.Success(response);
        }
    }
}