namespace Hims.Api.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using AutoMapper;
    using Domain.Helpers;
    using Domain.Services;
    using Hims.Api.Models;
    using Hims.Domain.Entities;
    using Hims.Infrastructure.Services;
    using Hims.Shared.Library.Enums;
    using Hims.Shared.UserModels.Slots;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Models.Practice;
    using Shared.DataFilters;
    using Shared.EntityModels;
    using Shared.UserModels.Filters;
    using Utilities;

    /// <summary>
    /// The practice controller.
    /// </summary>
    [Route("api/practices")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class PracticeController : BaseController
    {
        /// <summary>
        /// The Auto Mapper
        /// </summary>
        private readonly IMapper mapper;

        /// <summary>
        /// the practice services
        /// </summary>
        private readonly IPracticeService practiceServices;

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

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

        /// <inheritdoc />
        public PracticeController(IPracticeService practiceServices, IAESHelper aesHelper, IMapper mapper, IAuditLogService auditLogService)
        {
            this.practiceServices = practiceServices;
            this.aesHelper = aesHelper;
            this.mapper = mapper;
            this.auditLogServices = auditLogService;
        }

        /// <summary>
        /// To fetch practices
        /// </summary>
        /// <param name="model">
        /// The model.
        /// </param>
        /// <returns>
        /// The list of providers.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - List of providers.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("fetch")]
        [ProducesResponseType(typeof(IEnumerable<PracticeLocationModel>), 200)]
        [ProducesResponseType(500)]

        public async Task<ActionResult> FetchAsync([FromBody] PracticeFilterModel model)
        {
            model = (PracticeFilterModel)EmptyFilter.Handler(model);
            if (!string.IsNullOrEmpty(model.EncryptedPracticeId))
            {
                model.PracticeId = Convert.ToInt32(this.aesHelper.Decode(model.EncryptedPracticeId));
            }

            var practices = await this.practiceServices.FetchAsync(model);
            if (practices == null)
            {
                return this.ServerError();
            }

            foreach (var practice in practices)
            {
                practice.EncryptedPracticeId = this.aesHelper.Encode(practice.PracticeId.ToString());
            }

            return this.Success(practices);
        }

        /// <summary>
        /// To add practice
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The http status.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Registration successful and returns Practice ID.
        /// - 409 - TIN already exists.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [AllowAnonymous]
        [Route("add")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CreateAsync([FromBody] PracticeRegistrationRequest request, [FromHeader] LocationHeader header)
        {
            request = (PracticeRegistrationRequest)EmptyFilter.Handler(request);
            var model = this.mapper.Map<PracticeModel>(request);

            var practiceId = await this.practiceServices.CreateAsync(model);


            switch (practiceId)
            {
                case -1:
                    return this.Conflict("Given TIN has already been exists with us.");
                case 0:
                    return this.ServerError();
                default:
                    return this.Success("Practice details has been added successfully.");
            }
            if (practiceId > 0)
            {
                var auditLog = new AuditLogModel
                {
                    AccountId = model.CreatedBy,
                    LogTypeId = (int)LogTypes.Practices,
                    LogDate = DateTime.Now,
                    LogFrom = (short)model.LoginRoleId,
                    LogDescription = $"<b>{model.CreatedByName}<b> has added Practices  of <strong>{model.FullName}</strong> successfully.",
                    LocationId = Convert.ToInt32(header.LocationId)
                };
                await this.auditLogServices.LogAsync(auditLog);
            }

        }

        /// <summary>
        /// The update practice.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Provider details has been updated successfully.
        /// - 409 - Provider has already been exists with us.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPut]
        [Authorize]
        [Route("update")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(typeof(string), 409)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> UpdateAsync([FromBody] ModifyPracticeRequest request, [FromHeader] LocationHeader header)
        {
            request = (ModifyPracticeRequest)EmptyFilter.Handler(request);
            var model = this.mapper.Map<PracticeModel>(request);

            var response = await this.practiceServices.UpdateAsync(model);
            if (response > 0)
            {
                var auditLog = new AuditLogModel
                {
                    AccountId = model.ModifiedBy,
                    LogTypeId = (int)LogTypes.Practices,
                    LogDate = DateTime.Now,
                    LogFrom = (short)model.LoginRoleId,
                    LogDescription = $"<b>{model.ModifiedByName}</b> has added Practices of <strong>{model.FullName}</strong> successfully.",
                    LocationId = Convert.ToInt32(header.LocationId)
                };
                await this.auditLogServices.LogAsync(auditLog);
            }
            switch (response)
            {
                case -1:
                    return this.Conflict("Given TIN has already been exists with us.");
                case 0:
                    return this.ServerError();
                default:
                    return this.Success("Practice details has been updated successfully.");
            }
           
        }

        /// <summary>
        /// Fetches the only practice information asynchronous.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        [HttpPost]
        [Route("fetch-practice-info")]
        public async Task<ActionResult> FetchOnlyPracticeInfoAsync([FromBody] PracticeFilterModel model)
        {
            model ??= new PracticeFilterModel();
            var response = await this.practiceServices.FetchOnlyPracticeInfo(model);
            return this.Success(response);
        }
    }
}