using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace AyaNova.Api.Controllers
{
    [ApiController]
    [ApiVersion("8.0")]
    [Route("api/v{version:apiVersion}/notify")]
    [Produces("application/json")]
    [Authorize]
    public class NotifyController : ControllerBase
    {
        private readonly AyContext ct;
        private readonly ILogger<NotifyController> log;
        private readonly ApiServerState serverState;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="dbcontext"></param>
        /// <param name="logger"></param>
        /// <param name="apiServerState"></param>  
        public NotifyController(AyContext dbcontext, ILogger<NotifyController> logger, ApiServerState apiServerState)
        {
            ct = dbcontext;
            log = logger;
            serverState = apiServerState;
        }

        /// <summary>
        /// Pre-login route to confirm server is available
        /// </summary>       
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet("hello")]
        public async Task<IActionResult> GetPreLoginPing()
        {
            bool showSampleLogins = false;
            if (AyaNova.Core.License.ActiveKey.Status == AyaNova.Core.License.AyaNovaLicenseKey.LicenseStatus.ActiveTrial)
                showSampleLogins = await AyaNova.Util.DbUtil.DBHasTrialUsersAsync(ct, log);

            //confirm if there are logo's to show as well
            var logo = await ct.Logo.AsNoTracking().SingleOrDefaultAsync();
            if (logo == null)
            {
                return Ok(ApiOkResponse.Response(new { eval = showSampleLogins, ll = false, ml = false, sl = false }));
            }
            return Ok(ApiOkResponse.Response(new { eval = showSampleLogins, ll = logo.Large != null ? true : false, ml = logo.Medium != null ? true : false, sl = logo.Small != null ? true : false }));
        }



        /// <summary>
        /// Get count of new notifications waiting
        /// </summary>       
        /// <returns></returns>        
        [HttpGet("new-count")]
        public async Task<IActionResult> GetNewCount()
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            var UserId = UserIdFromContext.Id(HttpContext.Items);
            return Ok(ApiOkResponse.Response(await ct.Notification.CountAsync(z => z.UserId == UserId && z.Fetched == false)));
        }

        /// <summary>
        /// Get all in-app notifications
        /// </summary>       
        /// <returns></returns>        
        [HttpGet("app-notifications")]
        public async Task<IActionResult> GetAppNotifications()
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            var UserId = UserIdFromContext.Id(HttpContext.Items);
            var ret = await ct.Notification.AsNoTracking().Where(z => z.UserId == UserId).OrderByDescending(z => z.Created).ToListAsync();
            await ct.Database.ExecuteSqlInterpolatedAsync($"update anotification set fetched={true} where userid = {UserId}");
            return Ok(ApiOkResponse.Response(ret));
        }


        /// <summary>
        /// Delete app Notification
        /// </summary>
        /// <param name="id"></param>
        /// <returns>NoContent</returns>
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteAppNotification([FromRoute] long id)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            var UserId = UserIdFromContext.Id(HttpContext.Items);
            var n = await ct.Notification.FirstOrDefaultAsync(z => z.Id == id);
            if (n == null)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_FOUND, "id"));
            if (n.UserId != UserId)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, null, "Can't delete notification for another user"));
            ct.Notification.Remove(n);
            await ct.SaveChangesAsync();
            return NoContent();
        }


        /// <summary>
        /// Get Notify Event object list from queue
        /// </summary>     
        /// <returns>Notify Event objects awaiting delivery</returns>
        [HttpGet("queue")]
        public async Task<IActionResult> GetQueue()
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

            if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.OpsNotificationSettings))
            {
                return StatusCode(403, new ApiNotAuthorizedResponse());
            }

            if (!ModelState.IsValid)
            {
                return BadRequest(new ApiErrorResponse(ModelState));
            }

            var ret = await ct.NotifyEvent.Include(z => z.NotifySubscription).Select(z => new
            {
                z.Id,
                z.Created,
                z.EventDate,
                DeliverAfter = (z.EventDate + z.NotifySubscription.AgeValue - z.NotifySubscription.AdvanceNotice),
                z.UserId,
                z.EventType,
                z.AyaType,
                z.Name
            }).ToListAsync();
            return Ok(ApiOkResponse.Response(ret));
        }

        /// <summary>
        /// Delete pending notification event
        /// </summary>
        /// <param name="id"></param>
        /// <returns>NoContent</returns>
        [HttpDelete("notify-event/{id}")]
        public async Task<IActionResult> DeleteNotifyEvent([FromRoute] long id)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));
            if (!Authorized.HasDeleteRole(HttpContext.Items, AyaType.OpsNotificationSettings))
            {
                return StatusCode(403, new ApiNotAuthorizedResponse());
            }
            var n = await ct.NotifyEvent.FirstOrDefaultAsync(z => z.Id == id);
            if (n == null)
                return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_FOUND, "id"));
            ct.NotifyEvent.Remove(n);
            await ct.SaveChangesAsync();
            return NoContent();
        }



        /// <summary>
        /// Send direct message notification to selected users
        /// </summary>       
        /// <returns>NoContent on success or error</returns>        
        [HttpPost("direct-message")]
        public async Task<IActionResult> SendNotifyDirectMessage([FromBody] NotifyDirectMessage notifyDirectMessage)
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));


            foreach (long l in notifyDirectMessage.Users)
            {
                if (l != 0)
                    await NotifyEventProcessor.AddGeneralNotifyEvent(
                        NotifyEventType.GeneralNotification, notifyDirectMessage.Message, UserNameFromContext.Name(HttpContext.Items), null, l
                        );
            }

            return NoContent();
        }

        public class NotifyDirectMessage
        {
            public NotifyDirectMessage()
            {
                Users = new List<long>();
            }
            [Required]
            public string Message { get; set; }

            [Required]
            public List<long> Users { get; set; }
        }
        //------------


    }//eoc
}//eons