using System.Collections.Generic;
using System.Linq;
using Hims.Shared.UserModels.Common;
using Microsoft.EntityFrameworkCore.Internal;

namespace Hims.Api.Controllers
{
    using System;
    using System.Threading.Tasks;
    using Domain.Services;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Medication = Shared.UserModels.ProgressReport.Medication;
    using Timeline = Shared.UserModels.ProgressReport.Timeline;
    using General = Shared.UserModels.ProgressReport.General;
    using Lab = Shared.UserModels.ProgressReport.Labs;
    using Note = Shared.UserModels.ProgressReport.Notes;
    using MedicationInfo = Shared.UserModels.ProgressReport.MedicationInfo;
    using Utilities;
    using Hims.Shared.EntityModels;
    using Hims.Shared.Library.Enums;
    using Hims.Domain.Helpers;
    using Hims.Api.Helper;
    using Diet = Shared.UserModels.ProgressReport.Diet;
    using Hims.Api.Models;
    using Hims.Shared.UserModels.ProgressReport.Labs;

    /// <inheritdoc />
    [Authorize]
    [Route("api/progress-report")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class ProgressReportController : BaseController
    {
        /// <summary>
        /// The coupon services.
        /// </summary>
        private readonly IProgressReportService service;

        /// <summary>
        /// The aes helper
        /// </summary>
        private readonly IAESHelper aesHelper;

        /// <summary>
        /// The coupon services.
        /// </summary>
        private readonly IProgressReportLabService labService;

        /// <summary>
        /// The coupon services.
        /// </summary>
        private readonly IProgressReportNoteService noteService;

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

        /// <summary>
        /// the provider service
        /// </summary>
        private readonly IProviderService providerServices;

        /// <summary>
        /// the provider service
        /// </summary>
        private readonly IPatientService patientServices;

        /// <summary>
        /// The coupon services.
        /// </summary>
        private readonly IProgressReportAssessmentsService assessmentsService;

        /// <summary>
        /// The coupon services.
        /// </summary>
        private readonly IProgressReportVitalsService vitalsService;

        /// <inheritdoc />
        public ProgressReportController(IProgressReportService service,
            IAuditLogService auditLogServices,
            IProviderService providerServices,
            IPatientService patientServices,
            IProgressReportLabService labService,
            IProgressReportNoteService noteService,
            IProgressReportVitalsService vitalsService,
            IAESHelper aesHelper,
            IProgressReportAssessmentsService assessmentsService)
        {
            this.service = service;
            this.aesHelper = aesHelper;
            this.auditLogServices = auditLogServices;
            this.providerServices = providerServices;
            this.patientServices = patientServices;
            this.labService = labService;
            this.noteService = noteService;
            this.vitalsService = vitalsService;
            this.assessmentsService = assessmentsService;
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("insert-medications")]
        public async Task<ActionResult> InsertMedicationsAsync([FromBody] Medication.InsertModel model,[FromHeader] LocationHeader header)
        {
            try
            {
                var response = await this.service.AddMedicationAsync(model);

                var patientFullName = await this.patientServices.FindPatientByAdmissionId(model.AdmissionId);
                var providerName = await this.providerServices.FindProviderByAdmissionId(model.AdmissionId);
                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.CreatedBy,
                    LogTypeId = (int)LogTypes.ProgressReport,
                    LogFrom = (int)AccountType.Administrator,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $" {model.ModifiedByName} has added Progress report for Patient {patientFullName} on {DateTime.UtcNow.AddMinutes(330)} Prescribed by Doctor {providerName}.",
                    LocationId = Convert.ToInt32(header.LocationId)
                };
                await this.auditLogServices.LogAsync(auditLogModel);

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("update-medication")]
        public async Task<ActionResult> UpdateMedicationsAsync([FromBody] Medication.UpdateModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                var response = await this.service.UpdateMedicationAsync(model);

                var patientFullName = await this.patientServices.FindPatientByAdmissionId(model.AdmissionId);
                var providerName = await this.providerServices.FindProviderByAdmissionId(model.AdmissionId);
                var auditLogModel = new AuditLogModel
                {
                    AccountId = model.CreatedBy,
                    LogTypeId = (int)LogTypes.ProgressReport,
                    LogFrom = (int)AccountType.Administrator,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $" {model.ModifiedByName} has updated Progress report for Patient {patientFullName} on {DateTime.UtcNow.AddMinutes(330)} Prescribed by Doctor {providerName}.",
                    LocationId = Convert.ToInt32(header.LocationId)
                };
                await this.auditLogServices.LogAsync(auditLogModel);

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }
        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("delete-medication")]
        public async Task<ActionResult> DeleteMedicationAsync([FromBody] Medication.DeleteModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                var patientFullName = await this.patientServices.FindPatientByAdmissionId(model.AdmissionId);
                var providerName = await this.providerServices.FindProviderByAdmissionId(model.AdmissionId);
                var response = await this.service.DeleteMedicationAsync(model);

                try
                {

                    var auditLogModel = new AuditLogModel
                    {
                        // AccountId = model.By,
                        LogTypeId = (int)LogTypes.ProgressReport,
                        LogFrom = (int)AccountType.Administrator,
                        LogDate = DateTime.UtcNow.AddMinutes(330),
                        LogDescription = $" <b>{model.CreatedBy}</b> has deleted Medication for Patient <b>{patientFullName}</b> on {DateTime.UtcNow.AddMinutes(330)} Prescribed by Doctor <b>{providerName}</b>.",
                        LocationId = Convert.ToInt32(header.LocationId)
                    };
                    await this.auditLogServices.LogAsync(auditLogModel);

                }
                catch (Exception e)
                {
                    //audit
                }

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }
        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("stop-medication")]
        public async Task<ActionResult> StopMedicationAsync([FromBody] Medication.StopModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                var patientFullName = await this.patientServices.FindPatientByAdmissionId(model.AdmissionId);
                var providerName = await this.providerServices.FindProviderByAdmissionId(model.AdmissionId);
                var response = await this.service.StopMedicationAsync(model);

                try
                {

                    var auditLogModel = new AuditLogModel
                    {
                        AccountId = model.By,
                        LogTypeId = (int)LogTypes.ProgressReport,
                        LogFrom = (int)AccountType.Administrator,
                        LogDate = DateTime.UtcNow.AddMinutes(330),
                        LogDescription = $" <b>{model.CreatedBy}</b> has Stopped Medication for Patient <b>{patientFullName}</b> on {DateTime.UtcNow.AddMinutes(330)} Prescribed by Doctor <b>{providerName}</b>.",
                        LocationId = Convert.ToInt32(header.LocationId)
                    };
                    await this.auditLogServices.LogAsync(auditLogModel);

                }
                catch (Exception e)
                {
                    //audit
                }

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("take-medication")]
        public async Task<ActionResult> TakeMedicationAsync([FromBody] List<Medication.TakeModel> model, [FromHeader] LocationHeader header)
        {
            try
            {
                var patientFullName = await this.patientServices.FindPatientByAdmissionId(model[0].AdmissionId);
                var providerName = await this.providerServices.FindProviderByAdmissionId(model[0].AdmissionId);
                var response = await this.service.TakeMedicationAsync(model);

                try
                {

                    var auditLogModel = new AuditLogModel
                    {
                        AccountId = model[0].By,
                        LogTypeId = (int)LogTypes.ProgressReport,
                        LogFrom = (int)AccountType.Administrator,
                        LogDate = DateTime.UtcNow.AddMinutes(330),
                        LogDescription = $" <b>{model[0].CreatedBy}</b> has updated to Medication Taken for Patient <b>{patientFullName}</b> on {DateTime.UtcNow.AddMinutes(330)} Prescribed by Doctor <b>{providerName}</b>.",
                        LocationId = Convert.ToInt32(header.LocationId)
                    };
                    await this.auditLogServices.LogAsync(auditLogModel);
                }
                catch (Exception e)
                {
                    //audit
                }

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="searchParam"></param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpGet]
        [AllowAnonymous]
        [Route("medications-async")]
        public async Task<ActionResult> FetchMasterMedicationAsync(string searchParam)
        {
            try
            {
                var response = await this.service.FetchMasterMedicationsAsync(searchParam);
                return Ok(response ?? new List<Medication.MasterViewModel>());
            }
            catch (Exception)
            {
                return Ok(new List<Medication.MasterViewModel>());
            }
        }


        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <param name="searchParam"></param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpGet]
        [Route("meal-type-async")]
        public async Task<ActionResult> FetchMasterMealTypeAsync(string searchParam)
        {
            try
            {
                var response = await this.service.FetchMealTypeAsync(searchParam);
                return Ok(response ?? new List<Diet.MasterViewModel>());
            }
            catch (Exception)
            {
                return Ok(new List<Medication.MasterViewModel>());
            }
        }
        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-instructions")]
        public async Task<ActionResult> FetchInstructionsAsync()
        {
            try
            {
                var response = await this.service.FetchInstructionsAsync();
                return Ok(response ?? new List<Medication.InstructionViewModel>());
            }
            catch (Exception)
            {
                return Ok(new List<Medication.InstructionViewModel>());
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-medications")]
        public async Task<ActionResult> FetchMedicationsAsync([FromBody] Medication.FilterModel model)
        {
            try
            {
                var indentMedicines = await this.service.FetchIndentMedicationDatesAsync(model);
                if (model.DateModel != null)
                {
                    model.Date = new DateTime(model.DateModel.Year, model.DateModel.Month, model.DateModel.Day, 0, 0, 0);
                }
                model.AdmissionIds = model.IsNurse
                    ? (await this.service.FetchTopAdmissionIdsAsync(model)).ToList()
                    : new List<int> { model.AdmissionId };

                var dates = await this.service.FetchMedicationDatesAsync(model);
                model.EndDate = dates.Max;
                var response = await this.service.FetchMedicationAsync(model);

                if (indentMedicines.Count() > 0)
                {
                    var existingIds = response.ToList().Select(x => x.PharmacyProductId).ToList();
                    indentMedicines = indentMedicines.Where(x => !existingIds.Contains(x.PharmacyProductId));
                }

                var parentRecords = new List<Medication.ParentViewModel>();
                if (response.Count() > 0)
                {
                    var distinctMedications = response.Select(x => x.ProgressReportMedicationId).Distinct().ToList();
                    foreach (var id in distinctMedications)
                    {
                        var medications = response.Where(x => x.ProgressReportMedicationId == id).ToList();
                        var medication = medications.First();
                        var record = new Medication.ParentViewModel
                        {
                            ProgressReportMedicationId = medication.ProgressReportMedicationId,
                            ProgressReportId = medication.ProgressReportId,
                            PharmacyProductId = medication.PharmacyProductId,
                            Unit = medication.Unit,
                            Duration = medication.Duration,
                            MedicationDurationTypeId = medication.MedicationDurationTypeId,
                            Active = medication.Active,
                            Instructions = medication.Instructions,
                            StopReason = medication.StopReason,
                            CreatedDate = medication.CreatedDate,
                            StartDate = medication.StartDate,
                            EndDate = medication.EndDate,
                            CreatedByName = medication.CreatedByName,
                            ModifiedDate = medication.ModifiedDate,
                            ModifiedByName = medication.ModifiedByName,
                            ProductName = medication.ProductName,
                            ProductGenericName = medication.ProductGenericName,
                            ProductCompanyName = medication.ProductCompanyName,
                            ProductTypeName = medication.ProductTypeName
                        };

                        record.MedicationFrequency = medications.Select(x => new Medication.MedicationFrequency
                        {
                            MedicationDate = x.MedicationDate,
                            ProgressReportMedicationFrequencyId = x.ProgressReportMedicationFrequencyId,
                            MedicationInstructionDeepTypeId = x.MedicationInstructionDeepTypeId,
                            MedicationInstructionName = x.MedicationInstructionName,
                            MedicationInstructionTypeId = x.MedicationInstructionTypeId,
                            Status = x.Status
                        }).ToList();

                        parentRecords.Add(record);
                    }
                }

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = new Medication.GlobalViewModel
                    {
                        Records = parentRecords,
                        CurrentDayId = model.Date.Date == DateTime.Now.Date
                                        ? ProgressReportHelper.GetCurrentDayId()
                                        : 5,
                        Dates = dates,
                        IndentMedicines = indentMedicines
                    }
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-timeline-dates")]
        public async Task<ActionResult> FetchTimelineDatesAsync([FromBody] Medication.FilterModel model)
        {
            try
            {
                if (model.IsNurse)
                {
                    model.Date = new DateTime(model.DateModel.Year, model.DateModel.Month, model.DateModel.Day, 0, 0, 0);
                }
                model.AdmissionIds = model.IsNurse
                    ? (await this.service.FetchTopAdmissionIdsAsync(model)).ToList()
                    : new List<int> { model.AdmissionId };

                var dates = await this.service.FetchMedicationDatesAsync(model);
                var labsDates = await this.service.FetchLabDatesAsync(model);
                var notesDates = await this.service.FetchNoteDatesAsync(model);
                var vitalsDates = await this.service.FetchVitalsDatesAsync(model);
                var assessmentsDates = await this.service.FetchAssessmentsDatesAsync(model);

                var allDates = new List<DateTime>();
                if (dates.Min != null)
                {
                    allDates.Add(dates.Min ?? DateTime.Now.Date);
                    var currentLoopDate = dates.Min ?? DateTime.Now.Date;
                    while (true)
                    {
                        currentLoopDate = currentLoopDate.AddDays(1);
                        if (currentLoopDate.Date <= (dates.Max ?? DateTime.Now.Date))
                        {
                            allDates.Add(currentLoopDate.Date);
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                allDates.AddRange(labsDates);
                allDates.AddRange(notesDates);
                allDates.AddRange(vitalsDates);
                allDates.AddRange(assessmentsDates);
                allDates = allDates.Distinct().ToList();

                if (!allDates.Any())
                {
                    allDates.Add(DateTime.Now.Date);
                }
                allDates.Sort();

                var minDates = new List<DateTime>();
                if (dates.Min != null) minDates.Add(dates.Min ?? DateTime.Now.Date);
                if (labsDates.Any()) minDates.Add(labsDates.Min());
                if (labsDates.Any()) minDates.Add(labsDates.Min());
                if (notesDates.Any()) minDates.Add(notesDates.Min());
                if (vitalsDates.Any()) minDates.Add(vitalsDates.Min());
                if (assessmentsDates.Any()) minDates.Add(assessmentsDates.Min());
                dates.Min = minDates.Any() ? minDates.Min() : (DateTime?)null;

                var maxDates = new List<DateTime>();
                if (dates.Max != null) maxDates.Add(dates.Max ?? DateTime.Now.Date);
                if (labsDates.Any()) maxDates.Add(labsDates.Max());
                if (labsDates.Any()) maxDates.Add(labsDates.Max());
                if (notesDates.Any()) maxDates.Add(notesDates.Max());
                if (vitalsDates.Any()) maxDates.Add(vitalsDates.Max());
                if (assessmentsDates.Any()) maxDates.Add(assessmentsDates.Max());
                dates.Max = maxDates.Any() ? maxDates.Max() : (DateTime?)null;

                model.EndDate = dates.Max;
                if (model.Date > dates.Max)
                {
                    model.Date = dates.Max ?? DateTime.Now.Date;
                }

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = new Timeline.GlobalDatesViewModel
                    {
                        Dates = dates,
                        AllDates = allDates
                    }
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-counts")]
        public async Task<ActionResult> FetchCountsAsync([FromBody] General.FilterModel model)
        {
            try
            {
                var medicationCount = await this.service.FetchMedicationCountAsync(model);
                var labCount = await this.service.FetchLabCountAsync(model);
                var notesCount = await this.service.FetchNotesCountAsync(model);
                var vitalsCount = await this.service.FetchVitalsCountAsync(model);
                var assessmentsCount = await this.service.FetchAssessmentsCountAsync(model);

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = new General.ViewModel
                    {
                        MedicationCount = medicationCount,
                        LabCount = labCount,
                        NotesCount = notesCount,
                        VitalsCount = vitalsCount,
                        AssessmentsCount = assessmentsCount
                    }
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// Fetches the timeline helper asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        private async Task<GenericResponse> FetchTimelineHelperAsync(Medication.FilterModel model)
        {
            if (model.IsNurse)
            {
                model.Date = new DateTime(model.DateModel.Year, model.DateModel.Month, model.DateModel.Day, 0, 0, 0);
            }

            if (model.Id != null)
            {
                model.Value = Convert.ToInt32(this.aesHelper.Decrypt(model.Id));
            }

            model.AdmissionIds = model.IsNurse
                ? (await this.service.FetchTopAdmissionIdsAsync(model)).ToList()
                : new List<int> { model.AdmissionId };

            var medications = await this.service.FetchTimelineAsync(model);

            var currentDayId = model.Date.Date == DateTime.Now.Date
                                    ? ProgressReportHelper.GetCurrentDayIdAlt()
                                    : 5;

            if (medications.Any())
            {
                foreach (var x in medications)
                {
                    // MORNING
                    if (x.MedicationInstructionDeepTypeId == 1 && x.Status == null)
                    {
                        x.Status = currentDayId == 5 || currentDayId > 1
                        ? false
                        : (bool?)null;

                        if (x.Status == null && currentDayId != 5)
                        {
                            if (currentDayId == 1)
                            {
                                x.When = Timeline.Enums.WhenType.Now;
                            }
                            else if (currentDayId < 1)
                            {
                                x.When = Timeline.Enums.WhenType.Pending;
                            }
                        }
                    }

                    // AFTERNOON
                    if (x.MedicationInstructionDeepTypeId == 2 && x.Status == null)
                    {
                        x.Status = currentDayId == 5 || currentDayId > 2
                        ? false
                        : (bool?)null;

                        if (x.Status == null && currentDayId != 5)
                        {
                            if (currentDayId == 2)
                            {
                                x.When = Timeline.Enums.WhenType.Now;
                            }
                            else if (currentDayId < 2)
                            {
                                x.When = Timeline.Enums.WhenType.Pending;
                            }
                        }
                    }

                    // NIGHT
                    if (x.MedicationInstructionDeepTypeId == 3 && x.Status == null)
                    {
                        x.Status = currentDayId == 5 || currentDayId > 3
                       ? false
                        : (bool?)null;

                        if (x.Status == null && currentDayId != 5)
                        {
                            if (currentDayId == 3)
                            {
                                x.When = Timeline.Enums.WhenType.Now;
                            }
                            else if (currentDayId < 3)
                            {
                                x.When = Timeline.Enums.WhenType.Pending;
                            }
                        }
                    }
                    //TIME SLOTS
                    if (x.MedicationInstructionDeepTypeId == 4 && x.Status == null)
                    {
                        var currentHour = DateTime.Now.Hour;
                        var givenHourName = x.MedicationInstructionName;
                        var givenHour = Convert.ToInt32(new String(givenHourName.Where(Char.IsDigit).ToArray()));

                        if (givenHourName.Contains("PM"))
                        {

                            var givenHourInt = givenHour + 12;
                            x.Status = currentDayId == 5 || givenHourInt < currentHour
                      ? false
                       : (bool?)null;

                        }
                        if (givenHourName.Contains("AM"))
                        {
                            var givenHourInt = givenHour;
                            x.Status = currentDayId == 5 || givenHourInt < currentHour
                    ? false
                     : (bool?)null;

                        }
                        if (x.Status == null && currentDayId != 5)
                        {
                            if (givenHourName.Contains("PM"))
                            {
                                var givenHourInt = givenHour + 12;

                                if (givenHourInt == currentHour)
                                {
                                    x.When = Timeline.Enums.WhenType.Now;
                                }
                                else if (currentHour < givenHourInt)
                                {
                                    x.When = Timeline.Enums.WhenType.Pending;
                                }

                            }
                            if (givenHourName.Contains("AM"))
                            {

                                var givenHourInt = givenHour;


                                if (givenHourInt == currentHour)
                                {
                                    x.When = Timeline.Enums.WhenType.Now;
                                }
                                else if (givenHourInt < currentHour)
                                {
                                    x.When = Timeline.Enums.WhenType.Pending;
                                }
                            }
                        }
                    }

                    x.ActiveStatus = !x.Active
                        ? Timeline.Enums.ActiveStatus.Stopped
                            : Timeline.Enums.ActiveStatus.Active;

                    if (model.Date.Date > DateTime.Now.Date)
                    {
                        x.When = Timeline.Enums.WhenType.NotStarted;
                    }

                    var defaultTimes = ProgressReportHelper.GetDefaultTimes();
                    var clonedDefaultTimes = ProgressReportHelper.GetDefaultTimes();

                    var adition = (x.Aditions != null ? x.Aditions ?? 0 : 0);
                    var defaultMorning = defaultTimes.Morning;
                    defaultMorning.Hour = defaultMorning.Hour + adition;

                    var defaultAfternoon = defaultTimes.Afternoon;
                    defaultAfternoon.Hour = defaultAfternoon.Hour + adition;

                    var defaultNight = defaultTimes.Night;
                    defaultNight.Hour = defaultNight.Hour + adition;

                    x.TimeSlot = x.Hour != null
                        ? new Shared.UserModels.NurseShift.TimeModelHelper
                        {
                            Hour = x.Hour ?? 0,
                            Minute = x.Minute ?? 0
                        }
                        : x.MedicationInstructionDeepTypeId == 1
                        ? x.Breakfast == null
                            ? defaultMorning
                            : new Shared.UserModels.NurseShift.TimeModelHelper
                            {
                                Hour = x.Breakfast.Value.Hours + adition,
                                Minute = x.Breakfast.Value.Minutes
                            }
                        : x.MedicationInstructionDeepTypeId == 2
                            ? x.Lunch == null
                                ? defaultAfternoon
                                : new Shared.UserModels.NurseShift.TimeModelHelper
                                {
                                    Hour = x.Lunch.Value.Hours + adition,
                                    Minute = x.Lunch.Value.Minutes
                                }
                            : x.Dinner == null
                                ? defaultNight
                                : new Shared.UserModels.NurseShift.TimeModelHelper
                                {
                                    Hour = x.Dinner.Value.Hours + adition,
                                    Minute = x.Dinner.Value.Minutes
                                };

                    x.BreakfastModel = x.Breakfast != null
                        ? new Shared.UserModels.NurseShift.TimeModelHelper
                        {
                            Hour = x.Breakfast.Value.Hours,
                            Minute = x.Breakfast.Value.Minutes
                        }
                        : clonedDefaultTimes.Morning;

                    x.LunchModel = x.Lunch != null
                        ? new Shared.UserModels.NurseShift.TimeModelHelper
                        {
                            Hour = x.Lunch.Value.Hours,
                            Minute = x.Lunch.Value.Minutes
                        }
                        : clonedDefaultTimes.Afternoon;

                    x.DinnerModel = x.Dinner != null
                        ? new Shared.UserModels.NurseShift.TimeModelHelper
                        {
                            Hour = x.Dinner.Value.Hours,
                            Minute = x.Dinner.Value.Minutes
                        }
                        : clonedDefaultTimes.Night;
                    //To get TimeSlots Values
                    if (x.MedicationInstructionDeepTypeId == 4)
                    {
                        var givenHourName = x.MedicationInstructionName;
                        var givenHour = new String(givenHourName.Where(Char.IsDigit).ToArray());
                        var currentHour = DateTime.Now.Hour;

                        if (givenHourName.Contains("PM"))
                        {
                            var givenHourInt = Convert.ToInt32(givenHour) + 12;
                            x.TimeSlot.Hour = givenHourInt;
                        }
                        if (givenHourName.Contains("AM"))
                        {
                            var givenHourInt = Convert.ToInt32(givenHour);
                            x.TimeSlot.Hour = givenHourInt;
                        }
                    }

                    x.HasMove = x.Hour != null;
                    x.Breakfast = null;
                    x.Lunch = null;
                    x.Dinner = null;
                }
            }

            // commented out due to issues in labs
            var labs = new List<NewLabsViewModel>();
            //var labs = await this.labService.FetchAsync(new Lab.FilterModel
            //{
            //    AdmissionIds = model.AdmissionIds,
            //    Date = model.Date
            //});

            if (labs.Any())
            {
                var today = DateTime.Now.Date;
                labs = labs.Select(x =>
                {
                    x.When = x.Date == today
                                ? Timeline.Enums.WhenType.Now
                                : Timeline.Enums.WhenType.Later;

                   // x.Status = this.GetLabStatus(x);
                    return x;
                }).ToList();
            }

            var notes = model.AdmissionIds.Count > 0
                ? await this.noteService.FetchAsync(new Note.FilterModel
                {
                    AdmissionIds = model.AdmissionIds,
                    Date = model.Date
                })
                : new List<Note.ViewModel>();

            return new GenericResponse
            {
                Status = GenericStatus.Success,
                Data = new List<Timeline.GlobalAllViewModel<Timeline.MedicationViewModel>> {
                            new Timeline.GlobalAllViewModel<Timeline.MedicationViewModel>
                            {
                                Labs = labs,
                                Medications = medications,
                                Notes = notes
                            }
                        }
            };
        }

        private Timeline.Enums.LabStatusType GetLabStatus(Lab.ViewModel x)
        {
            return x.VerifiedBy != null
                                ? Timeline.Enums.LabStatusType.Verified
                                : x.IsParameterAdded
                                    ? Timeline.Enums.LabStatusType.NotVerified
                                    : x.IsSampleCollected
                                        ? Timeline.Enums.LabStatusType.Collected
                                        : Timeline.Enums.LabStatusType.NotCollected;
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-timeline")]
        public async Task<ActionResult> FetchTimelineAsync([FromBody] Medication.FilterModel model)
        {
            try
            {
                var response = await this.FetchTimelineHelperAsync(model);
                return Ok(response);
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-all-timeline")]
        public async Task<ActionResult> FetchAllTimelineAsync([FromBody] Medication.FilterModel model)
        {
            try
            {
                if (model.IsNurse)
                {
                    model.Date = new DateTime(model.DateModel.Year, model.DateModel.Month, model.DateModel.Day, 0, 0, 0);
                }

                if (model.Id != null)
                {
                    model.Value = Convert.ToInt32(this.aesHelper.Decrypt(model.Id));
                }

                model.AdmissionIds = model.IsNurse
                    ? (await this.service.FetchTopAdmissionIdsAsync(model)).ToList()
                    : new List<int> { model.AdmissionId };

                var medications = await this.service.FetchAllTimelineAsync(model);
                var labs = await this.labService.FetchAsync(new Lab.FilterModel
                {
                    AdmissionIds = model.AdmissionIds
                });

                var notes = await this.noteService.FetchAsync(new Note.FilterModel
                {
                    AdmissionIds = model.AdmissionIds
                });

                var today = DateTime.Now.Date;
                var currentDayId = ProgressReportHelper.GetCurrentDayIdAlt();

                var groupData = medications.GroupBy(
                                    p => p.GeneralDate,
                                    (key, list) => new Timeline.GlobalAllViewModel<Timeline.MedicationAllViewModel>
                                    {
                                        Date = key.Date,
                                        Medications = list.ToList().Select(x =>
                                        {
                                            // MORNING
                                            if (x.MedicationInstructionDeepTypeId == 1 && x.Status == null)
                                            {
                                                x.Status = x.GeneralDate.Date != today || (x.GeneralDate.Date == today && currentDayId > 1)
                                                ? false
                                                : (bool?)null;

                                                if (x.Status == null && x.GeneralDate.Date == today)
                                                {
                                                    if (currentDayId == 1)
                                                    {
                                                        x.When = Timeline.Enums.WhenType.Now;
                                                    }
                                                    else if (currentDayId < 1)
                                                    {
                                                        x.When = Timeline.Enums.WhenType.Pending;
                                                    }

                                                }
                                            }

                                            // AFTERNOON
                                            if (x.MedicationInstructionDeepTypeId == 2 && x.Status == null)
                                            {
                                                x.Status = x.GeneralDate.Date != today || (x.GeneralDate.Date == today && currentDayId > 2)
                                                ? false
                                                : (bool?)null;

                                                if (x.Status == null && x.GeneralDate.Date == today)
                                                {
                                                    if (currentDayId == 2)
                                                    {
                                                        x.When = Timeline.Enums.WhenType.Now;
                                                    }
                                                    else if (currentDayId < 2)
                                                    {
                                                        x.When = Timeline.Enums.WhenType.Pending;
                                                    }
                                                }
                                            }

                                            // NIGHT
                                            if (x.MedicationInstructionDeepTypeId == 3 && x.Status == null)
                                            {
                                                x.Status = x.GeneralDate.Date != today || (x.GeneralDate.Date == today && currentDayId > 3)
                                                ? false
                                                : (bool?)null;

                                                if (x.Status == null && x.GeneralDate.Date == today)
                                                {
                                                    if (currentDayId == 3)
                                                    {
                                                        x.When = Timeline.Enums.WhenType.Now;
                                                    }
                                                    else if (currentDayId < 3)
                                                    {
                                                        x.When = Timeline.Enums.WhenType.Pending;
                                                    }
                                                }
                                            }
                                            //TimeSlots
                                            if (x.MedicationInstructionDeepTypeId == 4 && x.Status == null)
                                            {
                                                var currentHour = DateTime.Now.Hour;
                                                var givenHourName = x.MedicationInstructionName;
                                                if (givenHourName.Contains("PM"))
                                                {
                                                    var givenHour = new String(givenHourName.Where(Char.IsDigit).ToArray());
                                                    var givenHourInt = Convert.ToInt32(givenHour) + 12;
                                                    x.Status = x.GeneralDate.Date != today || (x.GeneralDate.Date == today && givenHourInt < currentHour)
                                              ? false
                                              : (bool?)null;

                                                }
                                                if (givenHourName.Contains("AM"))
                                                {
                                                    var givenHour = new String(givenHourName.Where(Char.IsDigit).ToArray());
                                                    var givenHourInt = Convert.ToInt32(givenHour);
                                                    x.Status = x.GeneralDate.Date != today || (x.GeneralDate.Date == today && givenHourInt < currentHour)
                                              ? false
                                              : (bool?)null;

                                                }

                                                if (x.Status == null && x.GeneralDate.Date == today)
                                                {

                                                    if (givenHourName.Contains("PM"))
                                                    {
                                                        var givenHour = new String(givenHourName.Where(Char.IsDigit).ToArray());
                                                        var givenHourInt = Convert.ToInt32(givenHour) + 12;

                                                        if (givenHourInt == currentHour)
                                                        {
                                                            x.When = Timeline.Enums.WhenType.Now;
                                                        }
                                                        else if (currentHour < givenHourInt)
                                                        {
                                                            x.When = Timeline.Enums.WhenType.Pending;
                                                        }

                                                    }
                                                    if (givenHourName.Contains("AM"))
                                                    {
                                                        var givenHour = new String(givenHourName.Where(Char.IsDigit).ToArray());
                                                        var givenHourInt = Convert.ToInt32(givenHour);


                                                        if (givenHourInt == currentHour)
                                                        {
                                                            x.When = Timeline.Enums.WhenType.Now;
                                                        }
                                                        else if (givenHourInt < currentHour)
                                                        {
                                                            x.When = Timeline.Enums.WhenType.Pending;
                                                        }

                                                    }


                                                }
                                            }

                                            x.ActiveStatus = !x.Active
                                                ? Timeline.Enums.ActiveStatus.Stopped
                                                    : Timeline.Enums.ActiveStatus.Active;

                                            if (key.Date > DateTime.Now.Date)
                                            {
                                                x.When = Timeline.Enums.WhenType.NotStarted;
                                            }

                                            return x;
                                        }),
                                        Labs = labs.Where(x => x.Date == key.Date).Select(x =>
                                        {
                                            x.When = x.Date == today
                                                       ? Timeline.Enums.WhenType.Now
                                                       : Timeline.Enums.WhenType.Later;

                                            //x.Status = this.GetLabStatus(x);

                                            return x;
                                        }).ToList(),
                                        Notes = notes.Where(x => x.Date == key.Date)
                                    }).ToList();


                var labsGroupData = labs.GroupBy(
                                    p => p.Date,
                                    (key, list) => new Timeline.GlobalAllViewModel<Timeline.MedicationAllViewModel>
                                    {
                                        Date = key.Date,
                                        Medications = new List<Timeline.MedicationAllViewModel>(),
                                        Labs = list.Select(x =>
                                        {
                                            x.When = x.Date == today
                                                        ? Timeline.Enums.WhenType.Now
                                                        : Timeline.Enums.WhenType.Later;

                                            //x.Status = this.GetLabStatus(x);
                                            return x;
                                        }).ToList()

                                    }).ToList();

                foreach (var item in labsGroupData)
                {
                    if (!groupData.Any(x => x.Date == item.Date))
                    {
                        groupData.Add(new Timeline.GlobalAllViewModel<Timeline.MedicationAllViewModel>
                        {
                            Date = item.Date,
                            Medications = item.Medications,
                            Labs = item.Labs,
                            Notes = notes.Where(x => x.Date.Date == item.Date.Date)
                        });
                    }
                }

                var notesGroupData = notes.GroupBy(
                                    p => p.Date,
                                    (key, list) => new Timeline.GlobalAllViewModel<Timeline.MedicationAllViewModel>
                                    {
                                        Date = key.Date,
                                        Medications = new List<Timeline.MedicationAllViewModel>(),
                                        Labs = new List<Lab.NewLabsViewModel>(),
                                        Notes = list
                                    }).ToList();

                foreach (var item in notesGroupData)
                {
                    if (!groupData.Any(x => x.Date == item.Date))
                    {
                        groupData.Add(new Timeline.GlobalAllViewModel<Timeline.MedicationAllViewModel>
                        {
                            Date = item.Date,
                            Medications = item.Medications,
                            Labs = item.Labs,
                            Notes = item.Notes
                        });
                    }
                }

                groupData = groupData.OrderBy(x => x.Date).ToList();
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = groupData
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-medication-info")]
        public async Task<ActionResult> FetchMedicationInfoAsync([FromBody] MedicationInfo.FilterModel model)
        {
            try
            {
                // FETCHINGS
                var medications = await this.service.FetchMedicationInfoAsync(new Medication.FilterModel
                {
                    AdmissionIds = model.AdmissionIds,
                    Date = DateTime.Now.Date
                });

                var labFilterModel = new Lab.FilterModel
                {
                    AdmissionIds = model.AdmissionIds,
                    Date = DateTime.Now.Date
                };
                var labs = await this.labService.FetchLabInfoAsync(labFilterModel);

                var notes = await this.noteService.FetchNoteInfoAsync(labFilterModel);

                var vitals = await this.vitalsService.FetchVitalsInfoAsync(labFilterModel);

                var assessments = await this.assessmentsService.FetchAssessmentsInfoAsync(labFilterModel);

                // CONCEPT
                var currentDayId = ProgressReportHelper.GetCurrentDayIdAlt();

                var records = new List<MedicationInfo.ViewModel>();
                var uniqAdmissionIds = new List<int>();
                uniqAdmissionIds.AddRange(medications.Select(x => x.AdmissionId).ToList());
                uniqAdmissionIds.AddRange(labs.Select(x => x.AdmissionId).ToList());
                uniqAdmissionIds.AddRange(notes.Select(x => x.AdmissionId).ToList());
                uniqAdmissionIds.AddRange(vitals.Select(x => x.AdmissionId).ToList());
                uniqAdmissionIds.AddRange(assessments.Select(x => x.AdmissionId).ToList());
                uniqAdmissionIds = uniqAdmissionIds.Distinct().ToList();
                if (!uniqAdmissionIds.Any())
                {
                    return Ok(new GenericResponse
                    {
                        Status = GenericStatus.Success,
                        Data = records
                    });
                }

                foreach (var id in uniqAdmissionIds)
                {
                    var selectedMedications = medications.Where(x => x.AdmissionId == id);
                    var missed = selectedMedications.Where(x => x.MedicationInstructionDeepTypeId < currentDayId && (x.Status == null || x.Status == false));
                    var pending = selectedMedications.Where(x => x.MedicationInstructionDeepTypeId == currentDayId && (x.Status == null || x.Status == false));
                    var next = selectedMedications.Where(x => x.MedicationInstructionDeepTypeId > currentDayId && x.MedicationInstructionDeepTypeId <= (currentDayId + 1) && (x.Status == null || x.Status == false));
                    var subLabs = labs.Where(x => x.AdmissionId == id).Count();
                    var subNotes = notes.Where(x => x.AdmissionId == id).Count();
                    var subVitals = vitals.Where(x => x.AdmissionId == id).Count();
                    var subAssessments = assessments.Where(x => x.AdmissionId == id).Count();

                    var record = new MedicationInfo.ViewModel
                    {
                        AdmissionId = id,
                        Medications = new MedicationInfo.MedicationViewModel
                        {
                            Missed = missed.Any() ? new MedicationInfo.ContentViewModel
                            {
                                Count = missed.Count(),
                                DayCategory = Timeline.Enums.Daycategory.Medicine
                            } : null,

                            Pending = pending.Any() ? new MedicationInfo.ContentViewModel
                            {
                                Count = pending.Count(),
                                DayCategory = Timeline.Enums.Daycategory.Medicine
                            } : null,

                            Next = next.Any() ? new MedicationInfo.ContentViewModel
                            {
                                Count = next.Count(),
                                DayCategory = Timeline.Enums.Daycategory.Medicine
                            } : null
                        },
                        Labs = subLabs > 0 ? new MedicationInfo.ContentViewModel
                        {
                            Count = subLabs,
                            DayCategory = Timeline.Enums.Daycategory.Lab
                        } : null,

                        Notes = subNotes > 0 ? new MedicationInfo.ContentViewModel
                        {
                            Count = subNotes,
                            DayCategory = Timeline.Enums.Daycategory.Note
                        } : null,

                        Vitals = subVitals > 0 ? new MedicationInfo.ContentViewModel
                        {
                            Count = subVitals,
                            DayCategory = Timeline.Enums.Daycategory.Vitals
                        } : null,

                        Assessments = subAssessments > 0 ? new MedicationInfo.ContentViewModel
                        {
                            Count = subNotes,
                            DayCategory = Timeline.Enums.Daycategory.Assessments
                        } : null
                    };

                    records.Add(record);
                }

                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = records
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }

        /// <summary>
        /// The add coupon.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Coupon added successfully.
        /// - 409 - Coupon already exist.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch-medications-timeline")]
        public async Task<ActionResult> FetchMedicationsTimelineAsync([FromBody] Medication.TimelineFilterModel model)
        {
            try
            {
                var response = await this.service.FetchMedicationTimelineAsync(model);
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Success,
                    Data = response
                });
            }
            catch (Exception e)
            {
                return Ok(new GenericResponse
                {
                    Status = GenericStatus.Error,
                    Message = e.Message
                });
            }
        }
    }
}