using System;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
using System.Collections.Generic;
using Sockeye.Models;
using Sockeye.Util;


namespace Sockeye.Api.ControllerHelpers
{


    //Adapted from the example found here: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads#uploading-large-files-with-streaming
    //See AttachmentController at bottom of class for example of form that works with this code

    /// <summary>
    /// Handle processing uplod form with potentially huge files being uploaded (which means can't use simplest built in upload handler method)
    /// </summary>
    internal static class ApiUploadProcessor
    {


        /// <summary>
        /// handle upload
        /// </summary>
        /// <param name="httpContext"></param>        
        /// <returns><see cref="ApiUploadedFilesResult"/> list of files and form field data (if present)</returns>
        internal static async Task<ApiUploadedFilesResult> ProcessUploadAsync(Microsoft.AspNetCore.Http.HttpContext httpContext)
        {

            ApiUploadedFilesResult result = new ApiUploadedFilesResult();
            FormOptions _defaultFormOptions = new FormOptions();
            try
            {
                // Used to accumulate all the form url encoded key value pairs in the 
                // request.
                var formAccumulator = new KeyValueAccumulator();

                var boundary = MultipartRequestHelper.GetBoundary(
                    MediaTypeHeaderValue.Parse(httpContext.Request.ContentType),
                    _defaultFormOptions.MultipartBoundaryLengthLimit);
                var reader = new MultipartReader(boundary, httpContext.Request.Body);

                var section = await reader.ReadNextSectionAsync();

                while (section != null)
                {
                    ContentDispositionHeaderValue contentDisposition;
                    var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

                    if (hasContentDispositionHeader)
                    {
                        if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
                        {

                            string filePathAndName = string.Empty;
                            var CleanedUploadFileName = contentDisposition.FileName.Value.Replace("\"", "");

                            //get temp file path and temp file name
                            filePathAndName = FileUtil.NewRandomAttachmentFilesFolderFileName;

                            //save to disk
                            using (var stream = new FileStream(filePathAndName, FileMode.Create))
                            {
                                await section.Body.CopyToAsync(stream);
                            }
                            result.UploadedFiles.Add(new UploadedFileInfo()
                            {
                                InitialUploadedPathName = filePathAndName,
                                OriginalFileName = CleanedUploadFileName,
                                MimeType = section.ContentType

                            });
                        }
                        else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
                        {
                            // Content-Disposition: form-data; name="key"
                            //
                            // value

                            // Do not limit the key name length here because the 
                            // multipart headers length limit is already in effect.
                            var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
                            var encoding = GetEncoding(section);
                            using (var streamReader = new StreamReader(
                                section.Body,
                                encoding,
                                detectEncodingFromByteOrderMarks: true,
                                bufferSize: 1024,
                                leaveOpen: true))
                            {
                                // The value length limit is enforced by MultipartBodyLengthLimit
                                var value = await streamReader.ReadToEndAsync();
                                if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))
                                {
                                    value = String.Empty;
                                }
                                formAccumulator.Append(key.Value, value);

                                if (formAccumulator.ValueCount > _defaultFormOptions.ValueCountLimit)
                                {
                                    throw new InvalidDataException($"Form key count limit {_defaultFormOptions.ValueCountLimit} exceeded.");
                                }
                            }
                        }
                    }

                    // Drains any remaining section body that has not been consumed and
                    // reads the headers for the next section.
                    section = await reader.ReadNextSectionAsync();
                }

                //Get any extra form fields and return them
                result.FormFieldData = formAccumulator.GetResults();
                return result;
            }
            catch (Microsoft.AspNetCore.Http.BadHttpRequestException ex)
            {
                //most commonly here due to file too large
                result.Error = $"Code:{ex.StatusCode}, Error: {ex.Message}";
                return result;

            }

        }

        public static void DeleteTempUploadFile(ApiUploadedFilesResult uploadFormData)
        {
            if (uploadFormData.UploadedFiles.Count > 0)
            {
                foreach (UploadedFileInfo a in uploadFormData.UploadedFiles)
                {
                    System.IO.File.Delete(a.InitialUploadedPathName);
                }
            }
        }

        private static Encoding GetEncoding(MultipartSection section)
        {
            MediaTypeHeaderValue mediaType;
            var hasMediaTypeHeader = MediaTypeHeaderValue.TryParse(section.ContentType, out mediaType);
            // UTF-7 is insecure and should not be honored. UTF-8 will succeed in 
            // most cases.
            if (!hasMediaTypeHeader || Encoding.UTF8.Equals(mediaType.Encoding))
            {
                return Encoding.UTF8;
            }
            return mediaType.Encoding;
        }


        /// <summary>
        /// Contains result of upload form processor
        /// </summary>
        public class ApiUploadedFilesResult
        {
            public Dictionary<string, Microsoft.Extensions.Primitives.StringValues> FormFieldData { get; set; }
            public List<UploadedFileInfo> UploadedFiles { get; set; }
            public string Error { get; set; }

            public ApiUploadedFilesResult()
            {
                FormFieldData = new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
                UploadedFiles = new List<UploadedFileInfo>();
                Error = null;
            }

        }




    }//eoc


}//eons
