using Leanetec.EConnect.Client.Interface;
using Leanetec.EConnect.Domain.Entities;
using Microsoft.Extensions.Options;
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;

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

    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);

        if (res.IsSuccessStatusCode)
        {
            var data = await res.Content.ReadFromJsonAsync<TData>(_options.JsonSerializerOptions, cancel);
            return new ()
            {
                Ok = true,
                StatusCode = res.StatusCode,
                Data = data
            };
        }
        else
        {
            var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
            return new ()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = error
            };
        }
    }

    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);

        if (res.IsSuccessStatusCode)
        {
            var data = res.Content.ReadFromJsonAsAsyncEnumerable<TData>(_options.JsonSerializerOptions, cancel);
            return new ()
            {
                Ok = true,
                StatusCode = res.StatusCode,
                Data = data
            };
        }
        else
        {
            var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
            return new()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = error
            };
        }
    }

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

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

        if (res.IsSuccessStatusCode)
        {
            return new()
            {
                Ok = true,
                StatusCode = res.StatusCode
            };
        }
        else
        {
            var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
            return new()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = error
            };
        }
    }

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

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

        if (res.IsSuccessStatusCode)
        {
            return new()
            {
                Ok = true,
                StatusCode = res.StatusCode
            };
        }
        else
        {
            var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
            return new()
            {
                Ok = false,
                StatusCode = res.StatusCode,
                Error = error
            };
        }
    }
}