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

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

    using Models.Account;

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

    using Utilities;

    /// <inheritdoc />
    /// <summary>
    /// The account sessions controller.
    /// </summary>
    [Authorize]
    [Route("api/account-sessions")]
    [Consumes("application/json")]
    [Produces("application/json")]
    public class AccountSessionsController : BaseController
    {
        /// <summary>
        /// The account session services.
        /// </summary>
        private readonly IAccountSessionService accountSessionServices;

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

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

        /// <inheritdoc />
        public AccountSessionsController(IAccountSessionService accountSessionServices, IAuditLogService auditLogServices, IPushNotificationHelper pushNotificationHelper)
        {
            this.accountSessionServices = accountSessionServices;
            this.auditLogServices = auditLogServices;
            this.pushNotificationHelper = pushNotificationHelper;
        }

        /// <summary>
        /// The find account session.
        /// </summary>
        /// <param name="model">
        /// The find account session request model.
        /// </param>
        /// <returns>
        /// The account session model.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - The account session model.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("get")]
        [ProducesResponseType(typeof(AccountSessionModel), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FindAsync([FromBody] FindSessionRequest model)
        {
            model = (FindSessionRequest)EmptyFilter.Handler(model);
            var accountSession = await this.accountSessionServices.FindAsync(model.SessionId);
            return accountSession == null || accountSession.AccountSessionId == 0 ? this.Success(new AccountSessionModel()) : this.Success(accountSession);
        }

        /// <summary>
        /// The fetch account sessions.
        /// </summary>
        /// <param name="model">
        /// The fetch account session request.
        /// </param>
        /// <returns>
        /// The list of account sessions model.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - The list of account sessions model.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("fetch")]
        [ProducesResponseType(typeof(List<AccountSessionModel>), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> FetchAsync([FromBody] FetchSessionsRequest model)
        {
            model = (FetchSessionsRequest)EmptyFilter.Handler(model);
            var accountSession = await this.accountSessionServices.FetchAsync(model.AccountId);
            return accountSession == null ? this.Success() : this.Success(accountSession);
        }

        /// <summary>
        /// To create account session.
        /// </summary>
        /// <param name="model">
        /// The create session request Model.
        /// </param>
        /// <returns>
        /// The HTTP status.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Account session has been successfully created and returns session ID.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpPost]
        [Route("create")]
        [ProducesResponseType(typeof(int), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> CreateSessionAsync([FromBody] AccountSessionModel model,[FromHeader] LocationHeader header)
        {
            model = (AccountSessionModel)EmptyFilter.Handler(model);
            var accountSessionId = await this.accountSessionServices.CreateAsync(model);
            if (accountSessionId == 0)
            {
                return this.ServerError();
            }

            var auditLogModel = new AuditLogModel
            {
                AccountId = model.AccountId,
                LogTypeId = (int)LogTypes.AccountSessions,
                LogFrom = (int)AccountType.Administrator,
                LogDate = DateTime.UtcNow,
                LogDescription = $"{model.DeviceToken} account session has been added.",
                LocationId = Convert.ToInt32(header.LocationId)
            };
            await this.auditLogServices.LogAsync(auditLogModel);

            return this.Success(accountSessionId);
        }

        /// <summary>
        /// To kill a session.
        /// </summary>
        /// <param name="model">
        /// The kill session request model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Account session has been killed successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpDelete]
        [Route("kill")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> KillSessionAsync([FromBody] KillSessionRequest model)
        {
            model = (KillSessionRequest)EmptyFilter.Handler(model);

            if (model.IsOtherDevice != null && Convert.ToBoolean(model.IsOtherDevice) && string.IsNullOrEmpty(model.DeviceToken) && model.DeviceType != null)
            {
                await this.pushNotificationHelper.SendSingleAsync("Hims", "logout", "logout", model.DeviceToken, (int)(DeviceType)model.DeviceType);
            }

            var response = (model.DeviceType == null || model.DeviceId == null)
                                     ? await this.accountSessionServices.DeleteAllAsync(model.AccountId)
                                     : await this.accountSessionServices.DeleteAsync(model.AccountId, (DeviceType)model.DeviceType, model.DeviceId);

            return response == 0 ? this.ServerError() : this.Success("Account session has been killed.");
        }

        /// <summary>
        /// To kill all sessions.
        /// </summary>
        /// <param name="model">
        /// The kill all session request model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Account sessions has been killed successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpDelete]
        [Route("kill-all")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> KillAllSessionsAsync([FromBody] KillAllSessionsRequest model, [FromHeader] LocationHeader header)
        {
            model = (KillAllSessionsRequest)EmptyFilter.Handler(model);
            var response = await this.accountSessionServices.DeleteAllAsync(model.AccountId);
            if (response == 0)
            {
                return this.ServerError();
            }

            var auditLogModel = new AuditLogModel
            {
                AccountId = model.AccountId,
                LogTypeId = (int)LogTypes.AccountSessions,
                LogFrom = (int)AccountType.Administrator,
                LogDate = DateTime.UtcNow,
                LogDescription = $"Account's all session has been deleted.",
                LocationId = Convert.ToInt32(header.LocationId)
            };
            await this.auditLogServices.LogAsync(auditLogModel);

            return this.Success("Account's all sessions has been killed.");
        }

        /// <summary>
        /// To kill all sessions.
        /// </summary>
        /// <param name="model">
        /// The kill all session request model.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        /// <remarks>
        /// ### REMARKS ###
        /// The following codes are returned
        /// - 200 - Account sessions has been killed successfully.
        /// - 500 - Problem with Server side code.
        /// </remarks>
        [HttpDelete]
        [Route("kill-others")]
        [ProducesResponseType(typeof(string), 200)]
        [ProducesResponseType(500)]
        public async Task<ActionResult> KillOtherSessionsAsync([FromBody] KillOtherSessionsRequest model, [FromHeader] LocationHeader header)
        {
            model = (KillOtherSessionsRequest)EmptyFilter.Handler(model);

            // Sending push notifications to Other devices before deleting.
            var allDevices = await this.accountSessionServices.FetchAsync(model.AccountId);
            var accountSessionModels = allDevices as AccountSessionModel[] ?? allDevices.ToArray();
            if (accountSessionModels.Any())
            {
                var itemToRemove = accountSessionModels.ToList().Single(r => r.DeviceId == model.CurrentDeviceId);
                accountSessionModels.ToList().Remove(itemToRemove);
                var deviceTokenForProviderAndroid = accountSessionModels.Where(d => d.DeviceType == 2).Select(s => s.DeviceToken)
                    .ToList();
                var deviceTokenForProviderIOS = accountSessionModels.Where(d => d.DeviceType == 3).Select(s => s.DeviceToken).ToList();
                await this.pushNotificationHelper.SendAsync("Hims", "logout", "logout", deviceTokenForProviderAndroid, deviceTokenForProviderIOS, null);
            }

            var response = await this.accountSessionServices.DeleteOthersAsync(model.AccountId, model.CurrentDeviceId);
            if (response <= 0)
            {
                return this.ServerError();
            }

            var auditLogModel = new AuditLogModel
            {
                AccountId = model.AccountId,
                LogTypeId = (int)LogTypes.AccountSessions,
                LogFrom = (int)AccountType.Administrator,
                LogDate = DateTime.UtcNow,
                LogDescription = $"Account's other session has been deleted. Except " + model.CurrentDeviceId,
                LocationId = Convert.ToInt32(header.LocationId)
            };
            await this.auditLogServices.LogAsync(auditLogModel);

            return this.Success("Account's other session has been deleted. Except " + model.CurrentDeviceId);
        }
    }
}