using Leanetec.EConnect.Client.Interface;
using Leanetec.EConnect.Domain.Entities;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net.Http.Headers;
using System.Net.Http.Json;

namespace Leanetec.EConnect.Infrastructure;

public class EConnectClient<TError> : IEConnectClient<TError> where TError :  class
{
    private readonly ClientOptions _options;

    private readonly Lazy<HttpClient> LazyHttp;

    private HttpClient Http => LazyHttp.Value;

    private readonly ILogger<EConnectClient<TError>>? _logger;

    public EConnectClient(IOptions<ClientOptions> options, IHttpClientFactory httpFactory, ILogger<EConnectClient<TError>>? logger = null)
    {
        _options = options.Value;
        LazyHttp = new Lazy<HttpClient>(httpFactory.CreateEConnectClient);
        _logger = logger;
    }

    private string? AddQueryString(string? route = null, object? queryParams = null)
    {
        // add global query strings
        if (_options.DefaultQueryStrings is not null)
            route = route.AddQueryString(_options.DefaultQueryStrings);

        // add query strings
        if (queryParams is not null)
            route = route.AddQueryString(queryParams.ToPropertyDictionary());

        return route;
    }

    public async Task<Response<TData, TError>> GetAsync<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
        where TData : class
    {
        route = AddQueryString(route, queryParams);

        var res = await Http.GetAsync(route, cancel);

        return res.IsSuccessStatusCode
            ? new()
            {
                Ok = true,
                StatusCode = res.StatusCode,
                Data = (res.Content.Headers.ContentLength > 0)
                            ? await res.Content.ReadFromJsonAsync<TData>(_options.JsonSerializerOptions, cancel)
                            : null
            }
            : new()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = (res.Content.Headers.ContentLength > 0)
                            ? await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel)
                            : null
            };
    }

    public async Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
        where TData : class
    {
        route = AddQueryString(route, queryParams);

        var res = await Http.GetAsync(route, cancel);

        return res.IsSuccessStatusCode
            ? new()
            {
                Ok = true,
                StatusCode = res.StatusCode,
                Data = (res.Content.Headers.ContentLength > 0)
                            ? res.Content.ReadFromJsonAsAsyncEnumerable<TData>(_options.JsonSerializerOptions, cancel)
                            : null
            }
            : new()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = (res.Content.Headers.ContentLength > 0)
                            ? await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel)
                            : null
            };
    }

    public async Task<Response<TError>> PostAsync(Stream stream, string fileName, string? route = null, object? queryParams = null, CancellationToken cancel = default)
    {
        route = AddQueryString(route, queryParams);

        // create message and add accept header
        using var message = new HttpRequestMessage(HttpMethod.Post, route);
        message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));

        // add file type
        var fileContent = new StreamContent(stream);
        var mimeType = GetMimeType(fileName);
        fileContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType);

        // Create multipart form data form
        using var form = new MultipartFormDataContent
        {
            { fileContent, "file", fileName }
        };
        
        message.Content = form;

        var res = await Http.SendAsync(message, cancel);

        _logger?.LogCurl(Http, message);

        return res.IsSuccessStatusCode
            ? new()
            {
                Ok = true,
                StatusCode = res.StatusCode
            }
            : new()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = (res.Content.Headers.ContentLength > 0)
                            ? await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel)
                            : null
            };
    }

    private static string GetMimeType(string fileName)
    {
        var provider = new FileExtensionContentTypeProvider();
        if (!provider.TryGetContentType(fileName, out var contentType))
        {
            contentType = "application/octet-stream"; // fallback
        }
        return contentType;
    }
}