// Demo.FileApi
//
// Copyright © Eurostep
//
// All rights reserved.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System.Security.Cryptography;
using Microsoft.AspNetCore.StaticFiles;

namespace Demo.FileApi;

public static partial class Program
{
    public static async Task UploadUserStoreFileChunkedAsync(BasicAuthenticationHeaderValue basicAuthHeader, string baseAddress, string targetFilePath, string sourceFilePath, string spaceId)
    {
        using (FileStream file = new FileStream(sourceFilePath, FileMode.Open))
        {
            var requestUri = $"{baseAddress}/api/space/{spaceId}/user/fs/file";
            string fileName = Path.GetFileName(sourceFilePath);
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = basicAuthHeader;

            var fileExtContentTypeProvider = new FileExtensionContentTypeProvider();
                            
            if(!fileExtContentTypeProvider.TryGetContentType(fileName, out string contentType))
            {
                contentType = "application/octet-stream";
            }

            int chunkSize = 4 * 1024 * 1024;
            long totalSize = file.Length;
            int chunkNumber = 0;

            int totalNumberOfChunks = (int)Math.Truncate((double)file.Length / (double)chunkSize);
            if (Convert.ToInt32(file.Length % chunkSize) != 0)
            {
                totalNumberOfChunks += 1;
            }

            // dummy checksum to be deleted later
            client.DefaultRequestHeaders.Add("checksum", "checksum");

            var hasher = MD5.Create();

            // initial call
            while (true)
            {
                var buffer = new byte[chunkSize];
                int read = file.Read(buffer, 0, chunkSize);
                byte[] chunk;
                int currentChunkSize;
                if (read == 0)
                {
                    break;
                }
                else if (read < chunkSize)
                {
                    // this will be for the last chunk when we cant fill the buffer
                    chunk = new byte[read];
                    Array.Copy(buffer, chunk, read);
                    currentChunkSize = read;
                }
                else
                {
                    chunk = buffer;
                    currentChunkSize = chunkSize;
                }

                byte[] hash = hasher.ComputeHash(chunk);
                string checksum = BitConverter.ToString(hash).Replace("-", "").ToLower();
                client.DefaultRequestHeaders.Remove("checksum");
                client.DefaultRequestHeaders.Add("checksum", checksum);

                // upload chunk
                using (var bufferStream = new MemoryStream(chunk))
                {
                    using (MultipartFormDataContent content = new MultipartFormDataContent())
                    {
                        bufferStream.Position = 0;
                        using (var stream = new StreamContent(bufferStream))
                        {
                            content.Add(stream, "file", fileName);

                            var resumableTotalSize = new StringContent(totalSize.ToString());
                            content.Add(resumableTotalSize, "resumableTotalSize");

                            var resumableChunkNumber = new StringContent(((int)chunkNumber + 1).ToString());
                            content.Add(resumableChunkNumber, "resumableChunkNumber");

                            var resumableChunkSize = new StringContent(chunkSize.ToString());
                            content.Add(resumableChunkSize, "resumableChunkSize");

                            var resumableCurrentChunkSize = new StringContent(currentChunkSize.ToString());
                            content.Add(resumableCurrentChunkSize, "resumableCurrentChunkSize");

                            var resumableTotalChunks = new StringContent(totalNumberOfChunks.ToString());
                            content.Add(resumableTotalChunks, "resumableTotalChunks");

                            var resumableType = new StringContent(contentType);
                            content.Add(resumableType, "resumableType");

                            var resumableFileName = new StringContent(fileName);
                            content.Add(resumableFileName, "resumableFilename");                                

                            var resumableRelativePath = new StringContent(targetFilePath);
                            content.Add(resumableRelativePath, "resumableRelativePath");

                            using (HttpResponseMessage response = await client.PostAsync(requestUri, content))
                            {
                                response.EnsureSuccessStatusCode();
                            }
                        }
                    }
                }
                chunkNumber += 1;
            }
            hasher.Dispose();
            client.Dispose();
        }
    }
}
