using System.Runtime.Serialization.Json;
using System.Security.Cryptography;

namespace Fast.Logic;

public static partial class Util
{
    public static class File
    {
        public static async Task<FileUploadResponse<T>> ProcessUploadFileChunked<T>(IFormFile file, string metaData, System.Security.Claims.ClaimsPrincipal user, Func<FileStream, Task<T>> process)
        {
            if (file == null || file.Length == 0)
                return new FileUploadResponse<T> { Success = false, Message = "File is empty or invalid." };

            if (string.IsNullOrWhiteSpace(metaData))
                return new FileUploadResponse<T> { Success = false, Message = "Metadata is required." };

            ChunkMetaData chunkData;
            try
            {
                using var metaDataStream = new MemoryStream(Encoding.UTF8.GetBytes(metaData));
                var serializer = new DataContractJsonSerializer(typeof(ChunkMetaData));
                if (serializer.ReadObject(metaDataStream) is not ChunkMetaData cd)
                    return new FileUploadResponse<T> { Success = false, Message = "Invalid metadata." };
                chunkData = cd;
            }
            catch (Exception)
            {
                return new FileUploadResponse<T> { Success = false, Message = "Invalid metadata format." };
            }

            int userId = GetAppUserId(user);

            // Construct a consistent temporary file path.
            string extension = Path.GetExtension(chunkData.FileName);
            string originalFileName = $"{new Guid(chunkData.FileUid):N}{extension}";
            string tempFileName = $"{userId}_{HashOfOriginalFileName(chunkData.FileUid)}.tmp";
            string tempFilePath = Path.Combine(Path.GetTempPath(), tempFileName);

            // Append the current chunk to the temporary file.
            try
            {
                using var fileStream = new FileStream(tempFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                await file.CopyToAsync(fileStream);
            }
            catch (Exception)
            {
                return new FileUploadResponse<T> { Success = false, Message = "Error saving file chunk." };
            }

            // If this is the last chunk, process the complete file.
            if (chunkData.ChunkIndex == chunkData.TotalChunks - 1)
            {
                try
                {
                    using var combinedFileStream = new FileStream(tempFilePath, FileMode.Open, FileAccess.Read);
                    T processResult = await process(combinedFileStream);
                    return new FileUploadResponse<T> { Success = true, IsComplete = true, Data = processResult, Message = "File processed successfully." };
                }
                catch (Exception ex)
                {
                    var msg = String.GetExceptionMessage(ex);
                    return new FileUploadResponse<T> { Success = false, Message = $"Error processing final file.\r\n{msg}" };
                }
            }

            // Otherwise, the chunk was uploaded successfully but the file is not yet complete.
            return new FileUploadResponse<T> { Success = true, IsComplete = false, Message = "Chunk uploaded successfully." };
        }

        private static string HashOfOriginalFileName(string originalFileName)
        {
            using SHA256 sha256 = SHA256.Create();
            byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(originalFileName));
            return Convert.ToHexString(hashBytes); // Converts byte array to a hex string
        }
    }
}

public class FileUploadResponse<T>
{
    public bool Success { get; set; } // Indicates whether the operation was successful.
    public bool IsComplete { get; set; } // Set to true when the file upload has been completed and processed.
    public string Message { get; set; } = ""; // A message describing the result.
    public T? Data { get; set; } // The data returned from processing the complete file.
}
