namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    using Domain.Helpers;
    using Domain.Services;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Models.Patient;
    using Npgsql;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.Library.Enums;
    using Shared.UserModels.Filters;
    using Utilities;

    using Hims.Api.Helper;
    using Hims.Api.Models;
    using Hims.Domain.Entities;
    using Hims.Domain.Configurations;
    using Image = System.Drawing.Image;
    using Hims.Infrastructure.Configurations;
    using System.Net;
    using static System.Net.WebRequestMethods;
    using DocumentFormat.OpenXml.Spreadsheet;
    using System.Threading;
    using System.Net.Mime;

    /// <inheritdoc />
    [Authorize]
    [Route("api/patient-documents")]
    public class PatientDocumentsController : BaseController
    {
        /// <summary>
        /// the provider service
        /// </summary>
        private readonly IEncounterService encounterServices;

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

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

        /// <summary>
        /// The patient document services.
        /// </summary>
        private readonly IPatientDocumentService patientDocumentServices;

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

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

        /// <summary>
        /// The amazon s3 helper.
        /// </summary>
        private readonly IDocumentHelper documentHelper;

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

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

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

        ///// <summary>
        ///// The ftp configuration.
        ///// </summary>
        //private readonly IFtpConfiguration ftpConfiguration;

        ///// <summary>
        ///// The ftp configuration.
        ///// </summary>
        //private readonly INewFtpConfiguration newftpConfiguration;

        /// <summary>
        /// The ftp helper.
        /// </summary>
        private readonly IFtpHelper ftpHelper;

        private readonly IFtpUploadHelper ftpUploadHelper;

        private readonly IRunningEnvironment runningEnvironment;

        /// <inheritdoc />
        public PatientDocumentsController(
            ITimelineService timelineService,
            IPatientDocumentService patientDocumentServices,
            IPatientService patientServices,
            IDocumentHelper documentHelper,
            IAccountService accountServices,
            IEncounterService encounterServices,
            IPushNotificationHelper pushNotificationHelper,
            IAccountSessionService accountSessionServices,
            IAESHelper aesHelper,
        IAuditLogService auditLogServices,
            //IFtpConfiguration FtpConfiguration,
            //INewFtpConfiguration NewftpConfiguration
            IFtpHelper ftpHelper,
            IFtpUploadHelper ftpUploadHelper,
            IRunningEnvironment runningEnvironment
            )
        {
            this.patientDocumentServices = patientDocumentServices;
            this.patientServices = patientServices;
            this.documentHelper = documentHelper;
            this.accountServices = accountServices;
            this.aesHelper = aesHelper;
            this.timelineService = timelineService;
            this.encounterServices = encounterServices;
            this.pushNotificationHelper = pushNotificationHelper;
            this.accountSessionServices = accountSessionServices;
            this.auditLogServices = auditLogServices;
            //this.ftpConfiguration = FtpConfiguration;
            //this.newftpConfiguration = NewftpConfiguration;
            this.ftpHelper = ftpHelper;
            this.ftpUploadHelper = ftpUploadHelper;
            this.runningEnvironment = runningEnvironment;
        }

        /// <summary>
        /// The fetch patientDocuments.
        /// </summary>
        /// <param name="model">
        /// The patientDocument filter model.
        /// </param>
        /// <returns>
        /// The list of patientDocuments.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of patientDocuments.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(List<PatientDocumentModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAsync([FromBody] PatientDocumentFilterModel model)
        {
            model = (PatientDocumentFilterModel)EmptyFilter.Handler(model);

            if (!string.IsNullOrEmpty(model.EncryptedPatientId))
            {
                model.PatientId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedPatientId));
            }

            var patientDocuments = await this.patientDocumentServices.FetchAsync(model);

            if (!string.IsNullOrEmpty(model.EncryptedPatientId) && (patientDocuments == null || !patientDocuments.Any()))
            {
                patientDocuments = new List<PatientDocumentModel> { new PatientDocumentModel { PatientId = Convert.ToInt32(model.PatientId) } };
                return this.Success(patientDocuments);
            }

            if (patientDocuments == null)
            {
                return this.ServerError();
            }

            return this.Success(patientDocuments);
        }


        /// <summary>
        /// The fetch patientDocuments.
        /// </summary>
        /// <param name="model">
        /// The patientDocument filter model.
        /// </param>
        /// <returns>
        /// The list of patientDocuments.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of patientDocuments.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("downloadfetch")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(List<PatientDocumentModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> DownloadFetchAsync([FromBody] PatientDocumentFilterModel model)
        {
            model = (PatientDocumentFilterModel)EmptyFilter.Handler(model);

            if (!string.IsNullOrEmpty(model.EncryptedPatientId))
            {
                model.PatientId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedPatientId));
            }

            var patientDocumentsDownload = await this.patientDocumentServices.DownloadFetchFileFTP(model);

            if (!string.IsNullOrEmpty(model.EncryptedPatientId) && (patientDocumentsDownload == null || !patientDocumentsDownload.Any()))
            {
                patientDocumentsDownload = new List<PatientDocumentModel> { new PatientDocumentModel { PatientId = Convert.ToInt32(model.PatientId) } };
                return this.Success(patientDocumentsDownload);
            }

            if (patientDocumentsDownload == null)
            {
                return this.ServerError();
            }

            return this.Success(patientDocumentsDownload);
        }

        /// <summary>
        /// The fetch patientDocuments.
        /// </summary>
        /// <param name="base64"></param>
        /// The patientDocument filter model.
        /// </param>
        /// <returns>
        /// The list of patientDocuments.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of patientDocuments.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpGet]
        [AllowAnonymous]
        [Route("viewFile")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> DownloadFileFTP(int patientDocumentId)
        {
            var patientDocumentsDownload = "";
            if (patientDocumentId != 0)
            {
                string url = this.ftpHelper.GetFtpUrl("PatientDocument");
                patientDocumentsDownload = await this.ftpHelper.FetchDocumentBase64(patientDocumentId, url);
            }
            if (patientDocumentsDownload == null)
            {
                return this.ServerError();
            }
            else if (patientDocumentsDownload == "")
            {
                patientDocumentsDownload = null;
            }
            else if (patientDocumentsDownload == "The remote server returned an error: (550) File unavailable (e.g., file not found, no access).")
            {
                return this.Success(new PatientDocumentViewModel { ErrorMessage = patientDocumentsDownload });
            }
            return this.Success(new PatientDocumentViewModel { Base64 = patientDocumentsDownload });
        }

        /// <summary>
        /// Downloads the file from FTP and send as base64 asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        [Route("download-file-ftp")]
        public async Task<ActionResult> DownloadFileFromFTPAndSendAsBase64Async([FromBody] PatientDocumentModel model)
        {
            model = (PatientDocumentModel)EmptyFilter.Handler(model);
            var checkIf = this.ftpUploadHelper.CheckIfFileExistsOnServer(model.DocumentUrl);
            if (!checkIf)
            {
                return this.BadRequest("No such file exists.");
            }
            var patientDocumentsDownload = await this.ftpUploadHelper.DownloadBase64DataAsync(model.DocumentUrl);
            return this.Success(new PatientDocumentViewModel { Base64 = patientDocumentsDownload });
        }

        /// <summary>
        /// Downloads the file from FTP asynchronous.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <param name="url">The URL.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        [HttpGet]
        [AllowAnonymous]
        [Route("download-file-ftp-get")]
        public async Task<ActionResult> DownloadFileFromFTPAsync(int id, string url, string? type)
        {
            if (id == 0 || string.IsNullOrEmpty(url))
            {
                return this.ServerError();
            }

            var checkIf = this.ftpUploadHelper.CheckIfFileExistsOnServer(url);
            if (!checkIf)
            {
                return this.BadRequest();
            }
            if (type == "ScanReports")
            {

                var patientScanData = await this.patientDocumentServices.GetScanDocumentOnIdAsync(id);
                var memoryStream = await this.ftpUploadHelper.DownloadSmallFilesAsync(url);
                return File(new MemoryStream(memoryStream), patientScanData.ContentType, patientScanData.DocumentUrl);
            }
            else if(type == "PackageDocuments")
            {

                var patientScanData = await this.patientDocumentServices.GetPackageDocument(id);
                var memoryStream = await this.ftpUploadHelper.DownloadSmallFilesAsync(url);
                return File(new MemoryStream(memoryStream), patientScanData.ContentType, patientScanData.DocumentUrl);
            }
            else
            {
                var patientData = await this.patientDocumentServices.GetPatientDocumentOnIdAsync(id);
                var memoryStream = await this.ftpUploadHelper.DownloadSmallFilesAsync(url);
                return File(new MemoryStream(memoryStream), patientData.ContentType, patientData.DocumentUrl);
            }
        }

        /// <summary>
        /// The upload patient document.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Patient document uploaded successfully.
        /// - 400 - We're sorry. There are no files to upload.
        /// - 400 - Maximum of 10 files can be allowed to upload.
        /// - 400 - Sorry! We don't have a patient in the system.
        /// - 417 - Invalid file type.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("upload")]
        [Consumes("multipart/form-data")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(417)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UploadAsync([FromForm] UploadPatientDocumentRequest request, [FromHeader] LocationHeader header)
        {
            request = (UploadPatientDocumentRequest)EmptyFilter.Handler(request);

            var files = this.Request.Form.Files;
            if (files == null || files.Count == 0)
            {
                return this.BadRequest("We're sorry. There are no files to upload.");
            }

            if (files.Count > 10)
            {
                return this.BadRequest("Maximum of 12 files can be allowed to upload.");
            }

            var contentTypes = this.documentHelper.FetchContentTypes().ToList();
            if (!ListFilter.ContainsAll(contentTypes, files.Select(m => m.ContentType).Distinct()))
            {
                return this.Failed($"Only {string.Join(", ", contentTypes)} files are allowed.");
            }

            var guid = await this.accountServices.FindGuidAsync(request.PatientId, Roles.Patient);
            if (guid == Guid.Empty)
            {
                return this.BadRequest("Sorry! We don't have a patient in the system.");
            }

            var patientDocuments = new List<PatientDocumentModel>();
            var index = 0;
            foreach (var file in files)
            {
                var model = new PatientDocumentModel
                {
                    DocumentType = request.DocumentType,
                    Description = request.Description,
                    DocumentName = index == 0 ? request.DocumentName : $"{request.DocumentName}_{index}",
                    UploadedBy = request.UploadedBy,
                    PatientId = request.PatientId,
                    ContentType = file.ContentType,
                    Size = file.Length
                };

                #region Manish Code FTP
                var filePath = $@"{this.runningEnvironment.CurrentEnvironment}/{guid}/{model.DocumentType}";

                try
                {
                    await this.ftpUploadHelper.CreateDirectory(filePath);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                var dbPath = $@"{model.DocumentName}_{DateTime.UtcNow.Ticks}{Path.GetExtension(file.FileName)}";
                filePath += $@"/{dbPath}";


                var uploadResponse = await this.ftpUploadHelper.UploadFromFileAsync(filePath, file);

                if (uploadResponse <= 0)
                {
                    return this.BadRequest();
                }

                #endregion

                model.ThumbnailUrl = this.documentHelper.GetThumbnail(file.ContentType);
                model.DocumentUrl = dbPath;
                patientDocuments.Add(model);
                index++;
            }

            var response = await this.patientDocumentServices.AddAsync(patientDocuments);
            var enumerable = response.ToList();
            if (!enumerable.Any())
            {
                return this.ServerError();
            }

            var msg = $"Patient document{(files.Count > 0 ? "s" : string.Empty)} added";
            await this.timelineService.LogAsync(new TimelineModel
            {
                PatientId = request.PatientId,
                TimelineActionId = TimelineAction.DocumentAdded,
                CreatedBy = request.UploadedBy,
                Description = msg,
                Ids = enumerable.ToList()
            });


            var uploader = await this.encounterServices.GetUploaderRole(request.UploadedBy);
            try
            {
                // 1. Check if Provider if Yes send to patient
                if (uploader.RoleId == 3)
                {
                    await NotificationHelper.Notification(
                        request.PatientId,
                        Roles.Patient,
                        NotificationIntimate.DoctorUploadedDocuments,
                        null,
                        this.accountSessionServices,
                        this.pushNotificationHelper);
                }

                // 1. Check if Patient if Yes send to provider
                if (uploader.RoleId == 4)
                {
                    var details = await this.encounterServices.GetLatestAppointment(request.PatientId);
                    if (details != null)
                    {
                        await NotificationHelper.Notification(
                            details.ProviderId,
                            Roles.Provider,
                            NotificationIntimate.PatientUploadedDocuments,
                            null,
                            this.accountSessionServices,
                            this.pushNotificationHelper);
                    }
                }
            }
            catch (Exception)
            {
                // Ignore
            }

            try
            {
                var res = await this.patientServices.FindPatientByPatientId(request.PatientId);
                var auditLogModel = new AuditLogModel
                {
                    AccountId = request.UploadedBy,
                    LogTypeId = (int)LogTypes.PatientDocuments,
                    LogFrom = (short)uploader.RoleId,
                    LogDate = DateTime.UtcNow.AddMinutes(330),
                    LogDescription = $@"<b>Uploaded</b>  Document : {request.DocumentName} for  Patient: <b>{res.FullName}</b>",
                    LocationId = Convert.ToInt32(header.LocationId)
                };
                await this.auditLogServices.LogAsync(auditLogModel);
            }
            catch (Exception)
            {
                // Ignore
            }

            return this.Success($"Patient document{(files.Count > 0 ? "s" : string.Empty)} has been uploaded successfully.");
        }

        /// <summary>
        /// The upload document async.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("upload-document")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(417)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UploadDocumentAsync([FromBody] UploadPatientDocumentRequest request)
        {
            request = (UploadPatientDocumentRequest)EmptyFilter.Handler(request);

            var patientDocument = new PatientDocumentModel
            {
                DocumentType = request.DocumentType,
                Description = request.Description,
                DocumentName = request.DocumentName,
                UploadedBy = request.UploadedBy,
                PatientId = request.PatientId,
                ContentType = request.ContentType,
                Size = request.Size,
                DocumentUrl = request.DocumentUrl,
                ThumbnailUrl = request.ThumbnailUrl
            };

            var response = await this.patientDocumentServices.AddAsync(patientDocument);
            if (response == 0)
            {
                return this.ServerError();
            }

            var msg = $"Patient document({request.DocumentName}, {request.DocumentType}) added";
            await this.timelineService.LogAsync(new TimelineModel
            {
                PatientId = request.PatientId,
                TimelineActionId = TimelineAction.DocumentAdded,
                CreatedBy = request.UploadedBy,
                Description = msg,
                Ids = new List<int> { response }
            });

            var uploader = await this.encounterServices.GetUploaderRole(request.UploadedBy);

            // 1. Check if Provider if Yes send to patient
            if (uploader.RoleId == 3)
            {
                await NotificationHelper.Notification(
                            request.PatientId,
                            Roles.Patient,
                            NotificationIntimate.DoctorUploadedDocuments,
                            null,
                            this.accountSessionServices,
                            this.pushNotificationHelper);
            }

            // 1. Check if Provider if Yes send to patient
            if (uploader.RoleId == 4)
            {
                var details = await this.encounterServices.GetLatestAppointment(request.PatientId);
                await NotificationHelper.Notification(
                            details.ProviderId,
                            Roles.Provider,
                            NotificationIntimate.PatientUploadedDocuments,
                            null,
                            this.accountSessionServices,
                            this.pushNotificationHelper);
            }

            return this.Success($"Patient document({request.DocumentName}, {request.DocumentType}) has been uploaded successfully.");
        }

        /// <summary>
        /// The delete patient document.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Patient document deleted successfully.
        /// - 409 - Patient document can not be deleted.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("delete")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> DeleteAsync([FromBody] ModifyPatientDocumentModel model, [FromHeader] LocationHeader header)
        {
            try
            {
                model = (ModifyPatientDocumentModel)EmptyFilter.Handler(model);
                var document = await this.patientDocumentServices.GetAsync(model.PatientDocumentId);

                var response = await this.patientDocumentServices.DeleteAsync(model);
                if (response == null)
                {
                    return this.ServerError();
                }
                if (!string.IsNullOrEmpty(model.DocumentUrl))
                {
                    await this.ftpUploadHelper.DeleteFile(model.DocumentUrl);
                }

                var msg = $"Patient document({document.DocumentName}, {document.DocumentType}) deleted";
                await this.timelineService.LogAsync(new TimelineModel
                {
                    PatientId = document.PatientId,
                    TimelineActionId = TimelineAction.DocumentDeleted,
                    CreatedBy = model.ModifiedBy,
                    Description = msg,
                    Ids = new List<int> { document.PatientDocumentId }
                });

                if (model.ModifiedBy != null)
                {
                    var uploader = await this.encounterServices.GetUploaderRole(model.ModifiedBy ?? 0);

                    // 1. Check if Provider if Yes send to patient
                    if (uploader.RoleId == 3)
                    {
                        await NotificationHelper.Notification(
                                    document.PatientId,
                                    Roles.Patient,
                                    NotificationIntimate.DoctorEditedDocuments,
                                    null,
                                    this.accountSessionServices,
                                    this.pushNotificationHelper);
                    }

                    // 1. Check if Provider if Yes send to patient
                    if (uploader.RoleId == 4)
                    {
                        var details = await this.encounterServices.GetLatestAppointment(document.PatientId);
                        await NotificationHelper.Notification(
                                    details.ProviderId,
                                    Roles.Provider,
                                    NotificationIntimate.PatientEditedDocuments,
                                    null,
                                    this.accountSessionServices,
                                    this.pushNotificationHelper);
                    }
                }

                try
                {
                    if (model.PatientId != 0)
                    {
                        var res = await this.patientServices.FindPatientByPatientId(model.PatientId);

                        var auditLogModel = new AuditLogModel
                        {
                            AccountId = model.LoginAccountId,
                            LogTypeId = (int)LogTypes.PatientDocuments,
                            LogFrom = (short)model.LoginRoleId,
                            LogDate = DateTime.UtcNow.AddMinutes(330),
                            LogDescription = $@"<b>Deleted</b>  Document: {document.DocumentName} for patient: <b>{res.FullName}</b>",
                            LocationId = Convert.ToInt32(header.LocationId)
                        };
                        await this.auditLogServices.LogAsync(auditLogModel);
                    }

                }
                catch (Exception)
                {
                    // Ignore
                }

                return this.Success($"Patient document({document.DocumentName}, {document.DocumentType}) has been deleted successfully.");
            }
            catch (NpgsqlException exception)
            {
                if (exception.Message.Contains("violates foreign key constraint"))
                {
                    return this.Conflict("Patient document can't be deleted. Please contact administrator.");
                }

                return this.ServerError();
            }
        }

        /// <summary>
        /// The modify status of patient document.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - PatientDocument status updated successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPut]
        [Route("update")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UpdateAsync([FromBody] PatientDocumentModel model, [FromHeader] LocationHeader header)
        {
            var document = await this.patientDocumentServices.GetAsync(model.PatientDocumentId);

            model = (PatientDocumentModel)EmptyFilter.Handler(model);
            var response = await this.patientDocumentServices.UpdateAsync(model);
            if (response == 0)
            {
                return this.ServerError();
            }

            var msg = $"Patient document({document.DocumentName}, {document.DocumentType}) added";
            await this.timelineService.LogAsync(new TimelineModel
            {
                PatientId = document.PatientId,
                TimelineActionId = TimelineAction.DocumentEdited,
                CreatedBy = model.ModifiedBy,
                Description = msg,
                Ids = new List<int> { document.PatientDocumentId }
            });

            if (model.ModifiedBy != null)
            {
                var uploader = await this.encounterServices.GetUploaderRole(model.ModifiedBy ?? 0);

                // 1. Check if Provider if Yes send to patient
                if (uploader.RoleId == 3)
                {
                    await NotificationHelper.Notification(
                                document.PatientId,
                                Roles.Patient,
                                NotificationIntimate.DoctorEditedDocuments,
                                null,
                                this.accountSessionServices,
                                this.pushNotificationHelper);
                }

                // 1. Check if Provider if Yes send to patient
                if (uploader.RoleId == 4)
                {
                    var details = await this.encounterServices.GetLatestAppointment(document.PatientId);
                    if (details != null)
                    {
                        await NotificationHelper.Notification(
                                    details.ProviderId,
                                    Roles.Provider,
                                    NotificationIntimate.PatientEditedDocuments,
                                    null,
                                    this.accountSessionServices,
                                    this.pushNotificationHelper);
                    }
                }
                try
                {
                    var res = await this.patientServices.FindPatientByPatientId(document.PatientId);
                    var auditLogModel = new AuditLogModel
                    {
                        AccountId = model.ModifiedBy,
                        LogTypeId = (int)LogTypes.PatientDocuments,
                        LogFrom = (short)uploader.RoleId,
                        LogDate = DateTime.UtcNow.AddMinutes(330),
                        LogDescription = $@"<b>Updated</b> Document: {document.DocumentName} for Patient: <b>{res.FullName}</b>",
                        LocationId = Convert.ToInt32(header.LocationId)
                    };
                    await this.auditLogServices.LogAsync(auditLogModel);
                }
                catch (Exception ex)
                {
                    // Ignore
                }

            }
            return this.Success($"Patient document({document.DocumentName}, {document.DocumentType}) details has been updated successfully.");
        }

        /// <summary>
        /// The modify read async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPut]
        [Route("modify-read")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> ModifyReadAsync([FromBody] ModifyPatientDocumentModel model)
        {
            var document = await this.patientDocumentServices.GetAsync(model.PatientDocumentId);

            model = (ModifyPatientDocumentModel)EmptyFilter.Handler(model);
            var response = await this.patientDocumentServices.ModifyReadAsync(model);
            if (response == 0)
            {
                return this.ServerError();
            }

            var msg = $"Patient document({document.DocumentName}, {document.DocumentType}) modified";
            await this.timelineService.LogAsync(new TimelineModel
            {
                PatientId = document.PatientId,
                TimelineActionId = TimelineAction.DocumentEdited,
                CreatedBy = model.ModifiedBy,
                Description = msg,
                Ids = new List<int> { document.PatientDocumentId }
            });

            if (model.ModifiedBy != null)
            {
                var uploader = await this.encounterServices.GetUploaderRole(model.ModifiedBy ?? 0);

                // 1. Check if Provider if Yes send to patient
                if (uploader.RoleId == 3)
                {
                    await NotificationHelper.Notification(
                                uploader.ReferenceId,
                                Roles.Patient,
                                NotificationIntimate.DoctorEditedDocuments,
                                null,
                                this.accountSessionServices,
                                this.pushNotificationHelper);
                }

                // 1. Check if Provider if Yes send to patient
                if (uploader.RoleId == 4)
                {
                    await NotificationHelper.Notification(
                                uploader.ReferenceId,
                                Roles.Provider,
                                NotificationIntimate.PatientEditedDocuments,
                                null,
                                this.accountSessionServices,
                                this.pushNotificationHelper);
                }
            }

            return this.Success($"Patient document({document.DocumentName}, {document.DocumentType}) Read status has been updated successfully.");
        }

        /// <summary>
        /// The upload old documents.
        /// </summary>
        /// <param name="path">
        /// The path.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - documents updated successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("upload-old-documents")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UploadOldDocumentsAsync(string path)
        {
            var documentType = "Patient Documents";
            var logPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\logs", "Log_" + DateTime.Now.ToString("mmddyyhhMMss") + ".txt");
            try
            {
                var directories = Directory.GetDirectories($@"{path}");
                var count = 0;
                foreach (var directory in directories)
                {
                    count++;
                    var umrNumber = directory.Substring(directory.Length - 11);
                    var account = await this.accountServices.FindGuidAsync(umrNumber, Roles.Patient);
                    if (account == null)
                    {
                        UpdateLog(logPath, $"{count}.Patient ({umrNumber}) not found");
                        continue;
                    }

                    var patientDirectories = Directory.GetDirectories(directory);
                    if (patientDirectories.Length <= 0)
                    {
                        UpdateLog(logPath, $"{count}.Patient ({umrNumber}) documents not found");
                    }
                    else
                    {
                        var patientDocumentsPath = patientDirectories.FirstOrDefault(x => x.Split("\\")[4] == documentType);
                        if (patientDocumentsPath == null)
                        {
                            UpdateLog(logPath, $"{count}.Patient ({umrNumber}) documents not found");
                        }
                        else
                        {
                            switch (documentType)
                            {
                                case "PatientDocuments":
                                    var patientDocumentsDirectories = Directory.GetDirectories(patientDocumentsPath);
                                    if (patientDocumentsDirectories.Length <= 0)
                                    {
                                        UpdateLog(logPath, $"{count}.Patient ({umrNumber}) documents not found");
                                    }
                                    else
                                    {
                                        UpdateLog(logPath, $"{count}.Patient ({umrNumber}) documents upload started");
                                        foreach (var patientDirectory in patientDocumentsDirectories)
                                        {
                                            var subDocumentType = patientDirectory.Split("\\")[5];
                                            var files = Directory.GetFiles(patientDirectory);
                                            string[] documentTypes = { "Radiology", "Laboratory", "Prescription", "Full Transcript" };
                                            if (documentTypes.Contains(subDocumentType))
                                            {
                                                var patientDocuments = await this.UploadPatientDocuments(logPath, files, subDocumentType, account);
                                                if (files.Length > 0)
                                                {
                                                    UpdateLog(logPath, $"{files.Length} {subDocumentType} documents uploaded");
                                                    var response = await this.patientDocumentServices.AddAsync(patientDocuments);
                                                    var enumerable = response.ToList();
                                                    if (!enumerable.Any())
                                                    {
                                                        return this.ServerError();
                                                    }
                                                }
                                            }
                                        }

                                        UpdateLog(logPath, $"Documents uploaded successfully");
                                    }

                                    break;
                            }
                        }
                    }
                }

                return this.Success("Patient documents has been updated successfully.");
            }
            catch (Exception ex)
            {
                UpdateLog(logPath, $"Documents upload aborted : {ex.Message} {Environment.NewLine} {ex.StackTrace}");
                return this.ServerError();
            }
        }

        /// <summary>
        /// The upload old profiles.
        /// </summary>
        /// <param name="path">
        /// The path.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - documents updated successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("upload-old-profiles")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UploadOldProfilesAsync(string path)
        {
            var logPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\logs", "Log_" + DateTime.Now.ToString("mmddyyhhMMss") + ".txt");
            try
            {
                var directories = Directory.GetDirectories($@"{path}");
                var count = 0;
                foreach (var directory in directories)
                {
                    count++;
                    var umrNumber = directory.Substring(directory.Length - 11);
                    var account = await this.accountServices.FindGuidAsync(umrNumber, Roles.Patient);
                    if (account == null)
                    {
                        UpdateLog(logPath, $"{count}.Patient ({umrNumber}) not found");
                        continue;
                    }

                    var patientDirectories = Directory.GetDirectories(directory);
                    if (patientDirectories.Length <= 0)
                    {
                        UpdateLog(logPath, $"{count}.Patient ({umrNumber}) Profile documents not found");
                    }
                    else
                    {
                        var patientDocumentsPath = patientDirectories.FirstOrDefault(x => x.Split("\\")[4] == "Profile");
                        if (patientDocumentsPath == null)
                        {
                            UpdateLog(logPath, $"{count}.Patient ({umrNumber}) Profile documents not found");
                        }
                        else
                        {
                            var files = Directory.GetFiles(patientDocumentsPath);
                            if (files.Length <= 0)
                            {
                                UpdateLog(logPath, $"{count}.Patient ({umrNumber}) Profile documents not found");
                            }
                            else
                            {
                                UpdateLog(logPath, $"{count}.Patient ({umrNumber}) Profile documents upload started");

                                foreach (var item in files)
                                {
                                    using (var image = Image.FromFile(item))
                                    {
                                        using (var stream = new MemoryStream())
                                        {
                                            image.Save(stream, image.RawFormat);
                                            var bytes = stream.ToArray();
                                            var base64String = Convert.ToBase64String(bytes);
                                            base64String = "data:image/jpg;base64," + base64String;
                                            var patientModel = new PatientModel { PatientId = account.ReferenceId };
                                            (patientModel.ProfileImageUrl, patientModel.ThumbnailUrl) = await this.documentHelper.UploadAsync(base64String, account.PatientGuid, "Profile", "profile_" + DateTime.UtcNow.Ticks);
                                            await this.patientServices.UpdateImageUrlsAsync(patientModel, account.PatientGuid);
                                        }
                                    }
                                }

                                if (files.Length > 0)
                                {
                                    UpdateLog(logPath, $"{files.Length} Profile documents uploaded");
                                }

                                UpdateLog(logPath, $"Documents uploaded successfully");
                            }
                        }
                    }
                }

                return this.Success("Patient documents has been updated successfully.");
            }
            catch (Exception ex)
            {
                UpdateLog(logPath, $"Documents upload aborted : {ex.Message} {Environment.NewLine} {ex.StackTrace}");
                return this.ServerError();
            }
        }

        /// <summary>
        /// The update log.
        /// </summary>
        /// <param name="logPath">
        /// The log path.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        private static void UpdateLog(string logPath, string message)
        {
            message = $"{DateTime.Now:G}: {message}.{Environment.NewLine}";
            System.IO.File.AppendAllText(logPath, message);
        }

        /// <summary>
        /// The upload patient documents.
        /// </summary>
        /// <param name="logPath">
        /// The log Path.
        /// </param>
        /// <param name="files">
        /// The files.
        /// </param>
        /// <param name="documentType">
        /// The document Type.
        /// </param>
        /// <param name="account">
        /// The account.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [NonAction]
        private async Task<List<PatientDocumentModel>> UploadPatientDocuments(string logPath, IEnumerable<string> files, string documentType, AccountModel account)
        {
            var patientDocuments = new List<PatientDocumentModel>();
            foreach (var item in files)
            {
                var fileInfo = new FileInfo(item);
                using (var fileStream = new FileStream(item, FileMode.Open, FileAccess.Read))
                {
                    var bytes = new byte[fileStream.Length];
                    fileStream.Read(bytes, 0, Convert.ToInt32(fileStream.Length));
                    var name = fileInfo.Name.Split(".")[0];
                    var extension = fileInfo.Name.Split(".")[1];
                    var formFile = new FormFile(fileStream, 0, fileStream.Length, null, name)
                    {
                        Headers = new HeaderDictionary(),
                        ContentType = "image/" + extension
                    };
                    var model = new PatientDocumentModel
                    {
                        DocumentType = documentType,
                        Description = formFile.FileName,
                        DocumentName = name,
                        UploadedBy = 1,
                        PatientId = account.ReferenceId,
                        ContentType = formFile.ContentType,
                        Size = formFile.Length
                    };

                    var fileName = $"{model.DocumentName}_{DateTime.UtcNow.Ticks}.{extension}";
                    model.DocumentUrl = await this.documentHelper.UploadAsync(formFile, account.PatientGuid, model.DocumentType, fileName);
                    string[] imageFormats = { "jpg", "bmp", "gif", "png", "jpeg" };
                    try
                    {
                        if (imageFormats.Contains(extension))
                        {
                            var image = Image.FromStream(fileStream);
                            var thumbnail = image.GetThumbnailImage(150, 150, () => false, IntPtr.Zero);
                            var stream = new MemoryStream();
                            thumbnail.Save(stream, image.RawFormat);
                            stream.Position = 0;
                            var thumbnailFormFile = new FormFile(stream, 0, stream.Length, null, name)
                            {
                                Headers = new HeaderDictionary(),
                                ContentType = "image/" + extension
                            };
                            model.ThumbnailUrl = await this.documentHelper.UploadThumbnailAsync(thumbnailFormFile, account.PatientGuid, model.DocumentType, fileName);
                        }
                        else
                        {
                            model.ThumbnailUrl = this.documentHelper.GetThumbnail(formFile.ContentType);
                        }
                    }
                    catch
                    {
                        UpdateLog(logPath, $"{fileInfo.Name} is invalid file");
                    }

                    patientDocuments.Add(model);
                }
            }

            return patientDocuments;
        }
    }
}