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

    using Domain.Helpers;
    using Domain.Services;
    using Hims.Api.Models;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;

    using Models.Provider;

    using Npgsql;

    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.Library.Enums;
    using Shared.UserModels.Filters;

    using Utilities;

    /// <inheritdoc />
    [Authorize]
    [Route("api/provider-documents")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class ProviderDocumentsController : BaseController
    {
        /// <summary>
        /// The provider document services.
        /// </summary>
        private readonly IProviderDocumentService providerDocumentServices;

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

        /// <summary>
        /// The provider services.
        /// </summary>
        private readonly IProviderService providerServices;

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

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

        /// <inheritdoc />
        public ProviderDocumentsController(IProviderDocumentService providerDocumentServices, IProviderService providerServices, IDocumentHelper documentHelper, IAESHelper aesHelper, IAccountService accountServices)
        {
            this.providerDocumentServices = providerDocumentServices;
            this.providerServices = providerServices;
            this.documentHelper = documentHelper;
            this.aesHelper = aesHelper;
            this.accountServices = accountServices;
        }

        /// <summary>
        /// The fetch providerDocuments.
        /// </summary>
        /// <param name="model">
        /// The providerDocument filter model.
        /// </param>
        /// <returns>
        /// The list of providerDocuments.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of providerDocuments.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch")]
        [ProducesResponseType(typeof(List<ProviderDocumentModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAsync([FromBody] ProviderDocumentFilterModel model)
        {
            model = (ProviderDocumentFilterModel)EmptyFilter.Handler(model);
            var providerDocuments = await this.providerDocumentServices.FetchAsync(model);
            return providerDocuments == null ? this.ServerError() : this.Success(providerDocuments);
        }

        /// <summary>
        /// The upload provider document.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <param name="location"></param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Provider document uploaded successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("upload")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> AddAsync([FromBody] UploadProviderDocumentModel model, [FromHeader] LocationHeader location)
        {
            model = (UploadProviderDocumentModel)EmptyFilter.Handler(model);
            if (string.IsNullOrEmpty(model.Document))
            {
                return this.BadRequest("Invalid base 64 document string.");
            }

            if (string.IsNullOrEmpty(model.DocumentType))
            {
                return this.BadRequest("Document type is required.");
            }

            var providerId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedProviderId));
            var locationId = string.IsNullOrEmpty(location.LocationId) ? (int?)null : int.Parse(location.LocationId);
            var provider = await this.providerServices.FindAsync(providerId, locationId, null);
            if (provider == null || provider.ProviderId == 0)
            {
                return this.BadRequest("Provider not found for the given information.");
            }

            var providerDocumentModel = new ProviderDocumentModel
            {
                DocumentType = model.DocumentType,
                Description = model.Description,
                DocumentName = model.DocumentName,
                ProviderId = provider.ProviderId
            };

            var guid = await this.accountServices.FindGuidAsync(provider.ProviderId, Roles.Provider);

            (providerDocumentModel.DocumentUrl, providerDocumentModel.ThumbnailUrl) = await this.documentHelper.UploadAsync(model.Document, guid, model.DocumentType, model.DocumentName);
            var response = await this.providerDocumentServices.AddAsync(providerDocumentModel);
            if (response == 0)
            {
                return this.ServerError();
            }

            return this.Success("Provider document has been uploaded successfully.");
        }

        /// <summary>
        /// The add document async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <param name="location"></param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPost]
        [Route("upload-document")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(400)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> AddDocumentAsync([FromBody] UploadProviderDocumentModel model,[FromHeader] LocationHeader location)
        {
            model = (UploadProviderDocumentModel)EmptyFilter.Handler(model);

            if (string.IsNullOrEmpty(model.DocumentType))
            {
                return this.BadRequest("Document type is required.");
            }

            var providerId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedProviderId));
            var locationId = string.IsNullOrEmpty(location.LocationId) ? (int?)null : int.Parse(location.LocationId);
            var provider = await this.providerServices.FindAsync(providerId,locationId, null);
            if (provider == null || provider.ProviderId == 0)
            {
                return this.BadRequest("Provider not found for the given information.");
            }

            var providerDocumentModel = new ProviderDocumentModel
            {
                DocumentType = model.DocumentType,
                Description = model.Description,
                DocumentName = model.DocumentName,
                ProviderId = provider.ProviderId,
                DocumentUrl = model.DocumentUrl,
                ThumbnailUrl = model.ThumbnailUrl
            };

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

            return this.Success("Provider document has been uploaded successfully.");
        }

        /// <summary>
        /// The delete provider document.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - ProviderDocument deleted successfully.
        /// - 409 - ProviderDocument can not be deleted.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("delete")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> DeleteAsync([FromBody] ModifyProviderDocumentModel model)
        {
            try
            {
                model = (ModifyProviderDocumentModel)EmptyFilter.Handler(model);
                var response = await this.providerDocumentServices.DeleteAsync(model);
                if (response == 0)
                {
                    return this.ServerError();
                }

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

                return this.ServerError();
            }
        }

        /// <summary>
        /// The modify status of provider document.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - ProviderDocument status updated successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPut]
        [Route("modify-status")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> ModifyStatusAsync([FromBody] ModifyProviderDocumentModel model)
        {
            model = (ModifyProviderDocumentModel)EmptyFilter.Handler(model);
            var response = await this.providerDocumentServices.ModifyStatusAsync(model);
            if (response == 0)
            {
                return this.ServerError();
            }

            return this.Success("Provider document status has been updated successfully.");
        }

        /// <summary>
        /// The update async.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        [HttpPut]
        [Route("update")]
        [Consumes("application/json")]
        [Produces("application/json")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UpdateAsync([FromBody] ProviderDocumentModel model)
        {
            model = (ProviderDocumentModel)EmptyFilter.Handler(model);
            var response = await this.providerDocumentServices.UpdateAsync(model);
            if (response == 0)
            {
                return this.ServerError();
            }

            return this.Success("Patient document details has been updated successfully.");
        }

        /// <summary>
        /// The modify status of provider document.
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - ProviderDocument status updated successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPut]
        [Route("modify-read")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> ModifyReadAsync([FromBody] ModifyProviderDocumentModel model)
        {
            model = (ModifyProviderDocumentModel)EmptyFilter.Handler(model);
            var response = await this.providerDocumentServices.ModifyReadAsync(model);
            if (response == 0)
            {
                return this.ServerError();
            }

            return this.Success("Provider document Read status has been updated successfully.");
        }
    }
}