// Copyright (C) 2024, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
// 
// 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;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Duplicati.Library.Utility;

/// <summary>
/// Extension methods to wrap functionality around the HttpClient class with support 
/// for cancelation via CancellationToken and progress reporting stream
/// </summary>
public static class HttpClientExtensions
{

    /// <summary>
    /// Downloads a file from the server and saves it to the specified filename
    /// </summary>
    /// <param name="client">The Http client reference</param>
    /// <param name="request">A prepared HttpRequestMessage</param>
    /// <param name="filename">Filename to created</param>
    /// <param name="progressReportingAction">Action for progress reporting</param>
    /// <param name="cancellationToken">Cancelation token</param>
    /// <returns></returns>
    public static async Task DownloadFile(this HttpClient client, HttpRequestMessage request, string filename, Action<long> progressReportingAction = null, CancellationToken cancellationToken = default)
    {
        using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
        response.EnsureSuccessStatusCode();
        using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
        using var fileStream = System.IO.File.Create(filename);
        if (progressReportingAction != null)
        {
            using var ProgressReportingStream = new ProgressReportingStream(stream, progressReportingAction);
            await ProgressReportingStream.CopyToAsync(fileStream,cancellationToken);
        }
        else
        {
            await stream.CopyToAsync(fileStream, cancellationToken);
        }
    }

    /// <summary>
    /// Downloads a file from the server and saves it to the specified filename
    /// </summary>
    /// <param name="client">The Http client reference</param>
    /// <param name="request">A prepared HttpRequestMessage</param>
    /// <param name="fileStream">Stream to write downloaded data</param>
    /// <param name="progressReportingAction">Action for progress reporting</param>
    /// <param name="cancellationToken">Cancelation token</param>
    /// <returns></returns>
    public static async Task DownloadFile(this HttpClient client, HttpRequestMessage request, Stream fileStream, Action<long> progressReportingAction = null, CancellationToken cancellationToken = default)
    {
        using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
        response.EnsureSuccessStatusCode();
        using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
        if (progressReportingAction != null)
        {
            using var ProgressReportingStream = new ProgressReportingStream(stream, progressReportingAction);
            await ProgressReportingStream.CopyToAsync(fileStream, cancellationToken);
        }
        else
        {
            await stream.CopyToAsync(fileStream, cancellationToken);
        }
    }

    /// <summary>
    /// Executes an asyc request uploading the stream to the server and returns when all content has been uploaded
    /// </summary>
    /// <param name="client">The Http client reference</param>
    /// <param name="request">A prepared HttpRequestMessage (Presumably with a stream)</param>
    /// <param name="cancellationToken">Cancelation token</param>
    public static async Task<HttpResponseMessage> UploadStream(this HttpClient client, HttpRequestMessage request,CancellationToken cancellationToken = default)
    {
        return await client.SendAsync(request,  HttpCompletionOption.ResponseContentRead, cancellationToken);    
    }
}
