using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.Linq;
using AyaNova.Util;
using System.IO;


namespace AyaNova.Api.Controllers
{

    /// <summary>
    /// Logo controller
    /// </summary>
    [ApiController]
    [ApiVersion("8.0")]
    [Route("api/v{version:apiVersion}/logo")]
    [Produces("application/json")]
    [Authorize]
    public class LogoController : ControllerBase
    {
        private readonly AyContext ct;
        private readonly ILogger<LogoController> log;
        private readonly ApiServerState serverState;
        private const int MAXIMUM_LOGO_SIZE = 512000;//We really don't want it too big or it will slow the fuck down for everything


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





        /// <summary>
        /// Get Logo 
        /// </summary>
        /// <param name="size">One of "small", "medium", "large"</param>
        /// <returns>A single Logo and it's values</returns>
        [AllowAnonymous]
        [HttpGet("{size}")]
        public async Task<IActionResult> DownloadLogo([FromRoute] string size)
        {
            if (serverState.IsClosed)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
            if (!ModelState.IsValid)
                return BadRequest(new ApiErrorResponse(ModelState));

            if (string.IsNullOrWhiteSpace(size))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, "size", "Size is required and must be one of 'small', 'medium' or 'large'"));

            size = size.ToLowerInvariant();
            if (size != "small" && size != "medium" && size != "large")
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, "size", "Size parameter must be one of 'small', 'medium' or 'large'"));

            var logo = await ct.Logo.SingleOrDefaultAsync();
            if (logo == null)
                return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));


            switch (size)
            {
                case "small":
                    if (logo.Small == null)
                        return NotFound();
                    return new FileStreamResult(new MemoryStream(logo.Small), Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse(logo.SmallType));

                case "medium":
                    if (logo.Medium == null)
                        return NotFound();
                    return new FileStreamResult(new MemoryStream(logo.Medium), Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse(logo.MediumType));

                case "large":
                    if (logo.Large == null)
                        return NotFound();
                    return new FileStreamResult(new MemoryStream(logo.Large), Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse(logo.LargeType));
            }
            return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
        }



        /// <summary>
        /// Upload Logo
        /// Max 500 KiB total (512000 bytes)
        /// Must have full rights to Global object
        /// </summary>
        /// <param name="size">One of "small", "medium", "large"</param>       
        /// <returns>Accepted</returns>
        [Authorize]
        [HttpPost("{size}")]
        //[DisableFormValueModelBinding]
        [RequestSizeLimit(MAXIMUM_LOGO_SIZE)]//currently export file is 200kb * 50 maximum at a time = 15mb  https://github.com/aspnet/Announcements/issues/267
        public async Task<IActionResult> UploadAsync([FromRoute] string size)//, List<IFormFile> files /// <param name="files">Logo image file</param>
        {

            //https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1#upload-small-files-with-buffered-model-binding-to-a-database

            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

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

            if (string.IsNullOrWhiteSpace(size))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, "size", "Size is required and must be one of 'small', 'medium' or 'large'"));

            size = size.ToLowerInvariant();
            if (size != "small" && size != "medium" && size != "large")
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, "size", "Size parameter must be one of 'small', 'medium' or 'large'"));

            //get the one and only logo object
            var logo = await ct.Logo.FirstOrDefaultAsync();
            if (logo == null)
            {
                logo = new Logo();
                ct.Logo.Add(logo);
                await ct.SaveChangesAsync();
            }

            var file = Request.Form.Files[0];

            //var file=files[0];
            using (var memoryStream = new MemoryStream())
            {
                await file.CopyToAsync(memoryStream);
                if (memoryStream.Length > MAXIMUM_LOGO_SIZE)
                    return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, null, $"Logo files must be smaller than {MAXIMUM_LOGO_SIZE} maximum"));
                switch (size)
                {
                    case "small":
                        logo.Small = memoryStream.ToArray();
                        logo.SmallType = file.ContentType;
                        break;
                    case "medium":
                        logo.Medium = memoryStream.ToArray();
                        logo.MediumType = file.ContentType;
                        break;
                    case "large":
                        logo.Large = memoryStream.ToArray();
                        logo.LargeType = file.ContentType;
                        break;

                }
                await ct.SaveChangesAsync();
            }
            return Accepted();
        }


        /// <summary>
        /// Delete logo
        /// </summary>
        /// <param name="size"></param>
        /// <returns>NoContent</returns>
        [Authorize]
        [HttpDelete("{size}")]
        public async Task<IActionResult> DeleteLogo([FromRoute] string size)
        {
            if (!serverState.IsOpen)
                return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));

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

            if (string.IsNullOrWhiteSpace(size))
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, "size", "Size is required and must be one of 'small', 'medium' or 'large'"));

            size = size.ToLowerInvariant();
            if (size != "small" && size != "medium" && size != "large")
                return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, "size", "Size parameter must be one of 'small', 'medium' or 'large'"));

            //get the one and only logo object
            var logo = await ct.Logo.FirstOrDefaultAsync();
            if (logo != null)
            {
                switch (size)
                {
                    case "small":
                        logo.Small = null;
                        logo.SmallType = string.Empty;
                        break;
                    case "medium":
                        logo.Medium = null;
                        logo.MediumType = string.Empty;
                        break;
                    case "large":
                        logo.Large = null;
                        logo.LargeType = string.Empty;
                        break;

                }
                await ct.SaveChangesAsync();
            }

            return NoContent();
        }




    }
}